I don't know how to cook the repository or is he just not?

I see a lot of articles and questions in the style of "the best solutions/practices for asp.net mvc" and nearly everywhere it is told about a fun pattern as the repository who live here are generally very cool and provides a lot of abstraction and gives to test what you want. Well, for example first link in Google:
https://www.asp.net/mvc/overview/older-versions/ge...
I understand why you need a pattern I don't understand other:
1)
how to make a selection from the list of entities.
well, for example I have a Log table in which I write all the actions of users, and there really are a lot of posts, very very many records. I need for example to get all records for a particular user, or for example for a certain period. How can I do it?
public IEnumerable<student> GetLogs()
{
 return context.Logs.ToList();
 }</student>

If I use the GetLogs he will get all the records into memory, but if for example I have a memory worth that? And why all the pull to the sharp from the base to make a selection if I can do it in the database? Or do I need to write under each filtrovka your method on the interface and its implementation? Type GetLogsByUserID then GetLogsByDate and so on to infinity

2) How to make a selection from two tables? Well I want to get all the logs of users with the role admin with their emails for example.
without repository I do something like:
var logs = (from l in db.Logs
 join u in db.Users on l.userID equals u.id
 where u.roleID == 1
 select new { l , u }).ToList();

If you use the repository again, I need to get all the logging then all users then make the join? Or do I have to write my method in the interface and implementation that will only make joint 2 tables? In a repository, users or logs?

If I need 1 controller/service 1 repository? I 1 them to connect? maybe I need 15. Or am I the special class to have in kotroman will all repositories(the dbContext as a direct)?

All use lazy loading? Type in the User define a collection of logs which when you call load? But if I have a User collection, along with logs there is a subquery for each row in the table. But if I have not only logs? Table 2-3 example, 2-3 is the subquery for each record in the table. You use it and you are satisfied with?

And the main question. Do I need to apply this pattern if you don't use unit testing, or not using it to cover 100% of all the code and only certain helpers? After all, to switch to a different database it will be enough to change providers if you use ef, and if you use the ORM you are unlikely to completely refuse. Why write a bunch of code "in advance" with the principle of "but suddenly we change the dB/OS", but it is likely to reduce code changes compared to a full rewrite of the project. But if you're sure that the OS and dB will not change?
Isochem it for unit tests if you can safely moqнуть entire dbcontext?

Please answer people who are actually working on projects that I use. Because the position of the theorists who have been reading these articles, I already know.
July 2nd 19 at 17:59
5 answers
July 2nd 19 at 18:01
Solution
Colleagues, good afternoon!

Why has no one mentioned the UnitOfWork pattern? Pattern repository becomes very convenient to use if:
  1. this pattern GenericRepository;
  2. if necessary, you can add specific repositories (I call them ConcreteRepository;
  3. and of course, if all of these controls UnitOfWork;


If you use this link You have:
  • if a lot of entities and there is a GenericRepository, you do not need to produce a repository for each entity, it is sufficient to do this: var unit = UnitOfWork.Repository();
  • the paragraph above automatically solves the problem "how to select data from a 2-3-4-... table" with the repositories you work with DbSet in EF (by the way, the DbContext from EF, in fact, implements the UnitOfWork and GenericRepository pattern);
  • automatically solved the issue of expansion of your repository methods on everyone (ie, the need to list the logs for a specific user - adding new method to the repository GetLogsByUserId) - no need to wind the repository with new methods, it is sufficient to do this: var unit = UnitOfWork.Repository().GetQuery(e => e.UserId == targetUserId) or var unit = UnitOfWork.Repository().AsQuaryableQuery(e => e.UserId == targetUserId) (GetQuery AsQuaryableQuery methods and methods that the entity UnitOfWork that return IEnumerable or IQueryable, respectively);
  • if the methods SaveChanges/Commit implemented in UnitOfWork (UnitOfWork and manages the transaction database), then you have solved the problem of data consistency - UnitOfWork or confirm all changes (Commit), or rolls back in case of errors (Rollback);
Or am I the special class to have in kotroman will all repositories(the dbContext as a direct)?

Think correctly - this special class is the UnitOfWork. He is "just like dbContext", the only way you abstract from any EF, NHibernate or other ORM. In the end, your business logic on the drum, which ORM you use.

If I need 1 controller/service 1 repository? I 1 them to connect? maybe I need 15.

Simply connect 1 UnitOfWork (better to do it through dependency injection - DI - DependencyInjection using an IoC container, such as Autofac, Ninject, Unity. Autofac personally I like more though, because he is the most adequate and clear documentation than Ninject and especially Unity [no, this is not a topic for holivara "who is better: Autofac/Ninject/Unity/... - this is all my personal experience]).

And the main question. Do I need to apply this pattern if you don't use unit testing, or not using it to cover 100% of all the code and only certain helpers? After all, to switch to a different database it will be enough to change providers if you use ef, and if you use the ORM you are unlikely to completely refuse. Why write a bunch of code "in advance" with the principle of "but suddenly we change the dB/OS", but it is likely to reduce code changes compared to a full rewrite of the project. But if you're sure that the OS and dB will not change?

In any enterprise project, it is necessary to think ahead, even if it is the "little online store". Because in a year this small online shop can become a normal business with very high turnover and profit, and then, if You don't have a good abstracted architecture, if you don't have modularity, any new task will be implemented with a large number of crutches, and any new task will lead to instability in the system and to increase the degree of the regression (tested!). But if you as the architect will invest in the development of your system modularity and abstraction components, you thereby already make life easier for yourself in the future, and this means that after some time any task of changing the system will be solved faster, more reliable, and means you can take as a developer for it more money (because the cost of the new changes will be much lower). And if you're unit-testing will use when developing, you thereby reduce the risk of regression in the behavior of your system, which again reduces the cost of developing for you personally. Believe me, when the system has at least the modularity and abstraction of the components from each other (i.e., any component depends on the abstraction - the interface or abstract class, and not implementation - specific class), then such a system will be fun.

DBMS change is not such a common practice, but from the ORM (and EF in particular) to abandon in favor of performance is a real case load increases.

- absolutely agree with You about this.

it is a theme referred to the principle of single responsibility. 1 component - 1 responsibility. Noticed that myself - if you follow at least this principle, then automatically the system becomes modular. And if there are modules, it is possible to reduce the degree of connectivity of system components using abstractions (interfaces and abstract classes). And if there is an abstraction between the components, the unit test will be only joy. Moreover, a component can be both a class and a project. For example, in a separate project, you can make some kind of module system, then the project can be connected to another system during development. This improves the degree of code reuse (why 2 times to do the same thing?).

also pointed out in the comments, in some cases, the repository can not be used. Add: in non-enterprise solutions. For example, you are doing some simple little website for myself. Or for a friend. Or just for learning new technologies.
And is there any project on github using the UnitOfWork+Repository for example? - dakota.Litt commented on July 2nd 19 at 18:04
enough to do so: var unit = UnitOfWork.Repository().GetQuery(e => e.UserId == targetUserId)
- in General, such a maneuver is considered a bad idea. First, obtaining the user ID may be in different places need to be - the fruit of copy-paste. Second, get out of the data access logic that the repository is good needs to encapsulate. Say, will any check box the type of IsActive or IsDeleted, which also must be considered - and will have the wool the entire project, edit the above queries. - Rebeka.Kunze commented on July 2nd 19 at 18:07
well, you can use the search - request "unitofwork with generic repository" appears enough results. If you want to publish on GitHub the realization that, at the moment of its development I see is working and unit-tested (in the sense that it is easy to isolate - using Moq for example). At the same time and more experienced colleagues will be able to appreciate it - and it would be very helpful! - darion.Wiegand commented on July 2nd 19 at 18:10
I agree with You - I gave the first and most obvious that came to mind.
Second, get out of the data access logic that the repository is good needs to encapsulate.
Here I a little You do not understand - can you explain?
Say, will any check box the type of IsActive or IsDeleted, which also must be considered - and will have the wool the entire project, edit the above queries.
In such a case, the filters can be replaced special classes following the "template Specification" (blog.byndyu.ru/2011/01/domain-driven-design-reposi... - this blog I really like, because so many things are very easy to understand and popularly explained https://habrahabr.ru/post/171559/ well, just Google). I'll be honest - template "Specification" I have yet to apply is not started, and thank You, that You tell me about him reminded! - darion.Wiegand commented on July 2nd 19 at 18:13
it would be interesting to see your implementation
Can something did not understand, but this design UnitOfWork.Repository looks like a Service Locator - dakota.Litt commented on July 2nd 19 at 18:16
:
Here I a little You do not understand - can you explain?
The idea is that the user of the repository should not know the intricacies of obtaining data from it. He just needs to tell the repository: "Give me the user with this ID here", and the repository already decides which entities to pull which attributes to compare, etc. Otherwise the data access logic is spread throughout the app. - Rebeka.Kunze commented on July 2nd 19 at 18:19
I get it now. It turns out that precisely this problem and solves the "template Specification". Class-specification aware, for example, the criteria for selecting young polzovateley, and the repository is already pulling from the repository on this specific user objects. In the event of a change of the filtering mechanism we make these changes only in 1 place in the class specification. - darion.Wiegand commented on July 2nd 19 at 18:22
OK, the end of the week I will lay out somewhere and give You the link. - darion.Wiegand commented on July 2nd 19 at 18:25
July 2nd 19 at 18:03
It is advisable to read about the work of EF and IQuerable in particular. Using this interface and LINQ you can create queries (which are rendered in native SQL), not just to filter the collection.
private IQuerable<student> GetLogs()
{
 return context.Logs;
} 
public IEnumerable<student> GetLogsForStudent(int id, DateTime from, DateTime to)
{
 return GetLogs().Where(x => x.Id == id
 && x.Date >= from
 && x.Date <= to)
.ToList();
}</student></student>


All use lazy loading?

Depends on the amount of data and criticality of the speed.

If I need 1 controller/service 1 repository? I 1 them to connect?

Yes, one by one. Can I use your UnitOfWork or service merge.

Isochem it for unit tests if you can safely moqнуть entire dbcontext?

dbcontext Moka makes no sense though, because he nails nailed to the EF. DBMS change is not such a common practice, but from the ORM (and EF in particular) to abandon in favor of performance is a real case load increases.
July 2nd 19 at 18:05
I think if a small, new project in which all the data are in the database, which is created from EF Code First, you use the repository there is no need. But for example, if today I take the data from the database, and tomorrow plan to get from the api or some sort of NoSQL to the ORM which is not ? And can the entity which operates the my app well, not similar to those that are in the database or "collected" from multiple sources ?
It is not necessary to consider the repository as wrapper around the EF.

Now couple of comments:

so
public IEnumerable<student> GetLogs()
{
 return context.Logs;
}</student>


will be returned IQueryable and

GetLogs().Where(...)

to limit the selection from the server.

I would love to see on the page that displays ALL the logs of all admins.
But if it is really needed I would have formed it on the client: received AJAX request all admins for each of them in parallel or sequentially requested top 100 of message from the logs (if necessary, the next 100). As a consequence we have to start from the UX, then, no desire to write a repository that returns ALL records.

PS While ORM ensures 100% data and have enough power DB servers are all OK and without repositories, but under high load, the architecture is radically different and perform JOINы there is not a DB server.
July 2nd 19 at 18:07
In principle, the post above answered, but I add too.

If You each request will be closed ToList(), then indeed, for each such action will query in the database. If you use LINQ, even if it is a few lines of code will form a single dB query. And repositories are often wrapped in a single service, centralized access to data and work with it.

Users.Find(id).Logs.Where(x => x.Date == date) or
Logs.Where(x => x.UserId == userId && x.Date == date)

In any case one request.

And for each complex query (often demand) add a method to the interface.

It is necessary or not, is a personal matter, if the project only one person is involved. If it is necessary to maintain, develop, you have to think about flexibility. And not always change provider, you can go in pure ado.net if orm ceases to pull some queries. Therefore, without the repository there is a problem.
July 2nd 19 at 18:09
1. To write your own methods to infinity (These methods in fact will not be much)
2. Also write your method to place depending on from which table the sample (In your case logs)
3. Connect to 1, if you need 15, 15 to connect one at a time, but then you probably have a clear violation of the principle of single responsibility. Problems should not be, there are IoC containers, facade, if necessary.
4. Lazy loading is used only where performance is not critical, in all other cases disable it and get all the data at once
5. This pattern is primarily used to eliminate code duplication and encapsulate, what is the basis in the PLO. If you need a clean and flexible code, there is nothing complicated in the implementation and support of the repository, this is a very simple pattern, and if it seems to complicated, then you did not understand it.
he doesn't seem to complicated and rather redundant - dakota.Litt commented on July 2nd 19 at 18:12
then & encapsulation in OOP is also redundant :)
The repository can not be used if you are SURE 100% that:
- Will NEVER change ORM
- Will NEVER change OBD
- Will NEVER use caching for the queries from the database
NOWHERE in your code you do not have the same requests and NEVER will be
- Will NEVER write unit tests where used queries
- NEVER need the other advantages of storing the requests in one place - Rebeka.Kunze commented on July 2nd 19 at 18:15
in this case, Yes, the repository is redundant :) - darion.Wiegand commented on July 2nd 19 at 18:18

Find more questions by tags ASP.NETC#