M
M
Mikhail2016-11-09 11:00:19
ASP.NET
Mikhail, 2016-11-09 11:00:19

I don't know how to prepare a repository, or is it just not very good?

Very often I meet articles and questions in the style of "best solutions/practices for asp.net mvc" and almost everywhere they talk about such a cool pattern as a repository, which is really very cool and gives a lot of abstraction and allows you to test whatever you want. Well, for example, the first link in Google:
https://www.asp.net/mvc/overview/older-versions/ge...
I understand why the pattern is needed, I don’t understand another:
1)
how to select from a list of entities .
Well, for example, I have a Log table in which I write all the actions of users, and there are really VERY many entries, a lot of entries. I need, for example, to get all the records for a specific user, or let's say for a certain period. How can I do it?

public IEnumerable<Student> GetLogs()
        {
            return context.Logs.ToList();
        }

If I use GetLogs, it will immediately get all the records into memory, but what if, for example, I don’t have that much memory? And why drag everything into a sharp from the database to make a selection if I can do it in the database? Or do I need to write my own method for the interface and its implementation for each filtering? Type GetLogsByUserID then GetLogsByDate and so on ad infinitum
2) How to make a selection from two tables? Well, I want to get all the logs of users with the admin role with their emails, for example.
without a repository, I'll 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 I use the repository again, what do I need to get all the log entries, then all the users, then do the join? Or should I write my own method in the interface and implementation that will only join 2 tables? Which repository users or logs?
What if I need more than 1 repository in 1 controller/service? Should I connect them 1 at a time? Maybe I need 15. Or should I create a special class in which all repositories will be at once (as dbContext is direct)?
Is everyone using lazy loading? Type define in User a collection of dens which at the address will be loaded? But if I take the User collection along with the logs, there will also be a subquery for each row of the table. What if I need more than just logs? 2-3 tables for example, there will be 2-3 subqueries for each record in the table. Do you use it and are you happy with it?
And the main question. Do you even need to apply this pattern if you don't use unit testing, or don't use it to cover 100% of the whole code but only individual helpers? After all, to switch to another database, it will be enough to change the provider if you use ef, and if you use orm, you are unlikely to completely abandon it. Why write a bunch of code "in advance" with the principle "what if we change the database / OS", but this will most likely reduce code changes compared to a complete rewrite of the project. But if you are sure that OS and database will not change?
Why is it for unit tests if you can safely moq the entire dbcontext?
I ask people who really work on projects that they use to answer. Because I already know the position of theorists who have read such articles.

Answer the question

In order to leave comments, you need to log in

5 answer(s)
V
Valery Abakumov, 2016-11-11
@dmitrievMV

Colleagues, good afternoon!
Why has no one mentioned the UnitOfWork pattern? The repository pattern becomes very convenient to use if:
When using such a bundle, you have:
Think right - this special class is UnitOfWork. It is "just like dbContext", only in this way you abstract from all sorts of EF, NHibernate and other ORMs. As a result, your business logic doesn't care what ORM you use.
It is enough to connect 1 UnitOfWork (and it is better to do this through dependency injection - DI - DependencyInjection using an IoC container, for example, Autofac, Ninject, Unity. I personally like Autofac more, if only because it has the most adequate and understandable documentation than Ninject and even more so Unity [no, this is not a topic for a holivar "who is better: Autofac / Ninject / Unity / ... - these are all conclusions from my personal experience]).
In any enterprise project, you need to think ahead, even if it is a "small online store" project. Because in a year this small online store can turn into a normal business with very high turnover and profit, and then if you don’t have a good abstract architecture , if you don’t have modularity, then any new task will be implemented with an increasing number of crutches, and any new task will lead to system instability and an increase in the degree of regression (verified!). But if you, as an architect, put modularity and abstraction of components into your system during development, then you will thereby simplify your life in the future, which means that after some time any task to change the system will be solved faster, more reliably, and this means that you will be able to charge more money as a developer for this (because the cost of new changes will be much lower). And if you also use unit testing in development, then by doing so you reduce the risk of regression in the behavior of your system, which, again, reduces the development cost for you personally. Believe me
Fat Lorry - I absolutely agree with you on this.
Michael mentioned the principle of single responsibility very to the point. 1 component - 1 responsibility. I noticed on my own - if you follow at least this principle, then the system automatically becomes modular. And since there are modules, then you can reduce the degree of connectivity between components using abstractions (interfaces and abstract classes). And since there is abstraction between components, then unit testing will only be a joy. Moreover, both a class and a whole project can act as a component. For example, some module of the system can be moved to a separate project, and then this project can be connected to another system during development. This increases the degree of code reuse (why do the same thing 2 times?).
Mikhail also indicated in a comment in which cases the repository can not be used. I'll add: in non-enterprise solutions. For example, you are making some simple website for yourself. Or for a friend. Or just to learn new technology.

F
Fat Lorrie, 2016-11-09
@Free_ze

It is advisable to read about the work of EF and IQuerablein particular. With this interface and LINQ, you will be able to build queries (which render to native SQL) instead of just filtering collections.

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();
}

Depends on the amount of data and speed criticality.
Yes, one by one. Or you can combine it into a service or UnitOfWork.
dbcontextit makes no sense to wet it, if only because it is nailed to EF. Changing a DBMS is not such a common practice, but abandoning ORM (and EF in particular) in favor of performance is a real case with increasing load.

S
Sergey, 2016-11-09
@senal

I think if a small, new project in which all the data lies in the database, which is created from EF Code First, then there is no special need to use the repository. But for example, if today I take data from the database, and tomorrow I plan to receive it from api or some kind of NoSQL to which there is no ORM? Or maybe the entities that my application operates on are not at all similar to those that lie in the database or are "collected" from several sources?
It is not necessary to consider the repository as a wrapper over EF.
Now a couple of notes:
like this

public IEnumerable<Student> GetLogs()
{
      return context.Logs;
}

will return an IQueryable and
will limit the fetch from the server.
I would love to see a page that shows ALL the logs of all admins.
But if it is still really needed, I would form it on the client like this: I received an AJAX request for all admins for each of them in parallel or sequentially requested the top 100 messages from the logs (if necessary, the next 100). As a consequence, it is necessary to start from UX, then there will be no desire to write a repository that returns ALL records.
ZY While ORM provides 100% of the data and there is enough power of the DB server, everything is ok and without repositories, but at high load the architecture is fundamentally different and JOINs are not performed by DB servers there.

E
eRKa, 2016-11-09
@kttotto

In principle, the post above answered, but I will also add.
If you close ToList() for each request, then indeed, for each such action there will be a request to the database. If you use LINQ, even if there are several lines of code, one query to the database will be generated. And repositories are often wrapped in one service, centralized access to data and work already with it.
Users.Find(id).Logs.Where(x => x.Date == date) or
Logs.Where(x => x.UserId == userId && x.Date == date)
Either way there will be one request.
And for each complex request (often requested), a method is added to the interface.
It is necessary, it is not necessary, this is a personal matter if only one person is involved in the project. If it needs to be supported, developed, then you have to think about flexibility. And they don’t always change the provider, they can go to pure ado.net if the orm stops pulling some requests. Therefore, without a repository, it is problematic here.

M
Michael, 2016-11-10
@Sing303

1. Write your own methods to infinity (There won’t be many such methods)
2. Also write your own method, place it depending on which table the selection is from (In your case, logs)
3. Connect 1 if you need 15 , then connect 15 one by one, but here you most likely have a clear violation of the single responsibility principle. There should be no problems, there are IoC containers, a facade, if necessary.
4. Lazy loading is used only where performance is not critical, in all other cases it is disabled and all data is received at once
5. This pattern is primarily used to eliminate code duplication and encapsulation, which is the basis of OOP. If you need a "clean" and flexible code, there is nothing difficult to implement and maintain a repository, this is a very simple pattern, and if it seems complicated to you, then you did not understand it.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question