For example, I got rid of the mention of the database in the controller, made all the functions and methods in a separate class. So what? What are cookies? My controller has become skinny, and the repository has swollen - what are my advantages? What are the advantages of the application? Where has arrived in speed, or maybe in readability, or performance? Immediately make a reservation, I will not change mssql to mysql ...
- Pluses in loose coupling - MihailPw
- @ AGS17 what gives us weak connectivity? Why, following the precepts, should I rush from class to class in order to understand what function, or method, executes in each particular case? - SergeyE
- one1. In summary, you can write: I own design patterns. 2. The code has become more - you can tell the employer that you work a lot. You can tell everyone that you are creating large, serious applications. - Alexander Petrov
- @Alexander Petrov - that's it, except for "cleverness" I don’t see any advantages. The truth has not yet appreciated the advantages of the answer to the question, I will try to formulate the question a little differently, with examples, as I wrote in the answers - it’s a pity that we can’t use the code in the comments ... - SergeyE
2 answers
In order to understand why seeding has started at all, you need to understand what a controller is and what a repository is.
Once it was decided to write all the code in one large file. Or in one big class.
And it is quite justified for small applications. Those. If you have a memory calculator, then it is much easier to write:
textBoxTesult.Text = Int32.Parse(textBoxInput.Text) + ExecuteScalar("SELECT TOP 1 SavedValue FROM CalcMemory"); Unfortunately for large applications, this quickly leads to a wild mix in the code.
Therefore, the code was tried to be divided into three separate parts:
- Presentation - Data display, redirects, response to user clicks, and other things that are specific to the UI, and are not directly related to entity operations.
- Business Logic - operations on entities in your subject area, preferably without reference to how and where exactly this data is stored.
- Data Access - data access.
And under this division came up with a bunch of patterns.
MVC is the Presentation Layer pattern. Its implementation of ASP.NET MVC assumes that you break your Presentation into two and a half parts:
- View - mapping using a template engine (Razor)
- Controller - a class-event handler from the user who is engaged in validation, redirects and initiates the selection of data from the Model. It may also be displaying raw data from BL to classes for transferring data to the View. There is no business logic in the controller.
- Model is the rest of the application. In the case of a three-tier architecture, the Model is the BL level.
The advantages of this separation:
- Entity operations are no longer tied to the user interface, and when writing code to work with them you don’t have to think in which of the click handlers you need to make changes and how the UI will react to the written code. BL itself, UI itself.
- The controller stops doing BL, it’s responsible only for the UI logic, and it is much easier to cover it with unit tests.
Your question arose due to the fact that you initially did not have a division into Controller and Model. You had a class named "controller" in which you had business logic written.
Ok, what is a repository. The classic Repository pattern has almost nothing to do with what is now called a repository, at least when developing in C #. The classic Repository itself solves the following problem:
Mediation between the objects and the data mapping layers.
Closest to the repository in modern C # code is the IQueryProvider.Execute<TResult> method, which implements EF, since he does exactly what is written in the definition of the pattern.
What you call a repository is rather a query repository. It solves the following problems:
- For code that works directly with EF (i.e. with
IQueryable), it’s hard to write unit tests, since to replace the whole context at once is difficult. To reproduce the behavior of the context in complex operations is generally unrealistic. - The request code begins to be duplicated by the project, since everywhere you can take and write
context.SomeEntities.Where(e => e.IsExpired and some long condition).ToList(), and this is easier than looking for a similar query on a project and go somewhere (where?) to it. IQueryablebehaviorIQueryablevery different fromIEnumerablebehavior in some operations (for example,nullis processed differently), and the developer has to strain the brain at the sight of each request in the code.
Therefore, the problem is usually solved radically:
- Forbid to work with context directly in code
- Take out all requests to a separate class (classes), which is called the Repository
IQueryablefromIQueryablerepository, allowing onlyIEnumerable
The output is:
- Controllers that deal only with UI, and for which you can easily write tests
- BL, which works only with already selected (through the repository) objects, and in which there is no damned
IQueryable, and to which you can write tests - Repositories with no special logic except filtering (which can be backed up by integration tests)
Why do they not do this in small applications and why do all the examples boldly create the context right from the controller, climb into it and issue the entities directly in the View? Because these are examples, not live applications. No one will be interested in looking at 5 classes in order to show new engine chips for View.
You do not have a BL now, you most likely do not write tests, the application is small - and therefore you do not get any benefit from the division. And that's fine. But if you are not alone in the project, if the project is developed and if there is any business logic in it (there are exceptions), then the options "pull the repositories from the controller" or "pull the context from the controller" will quickly lead the project to nowhere.
- @Etkiner for general understanding, read also the answer to the closest question PashaPash option 1, I quote: "if you do not write tests - you do not need a repository" - AK ♦
There are several of the most significant advantages:
- Using only repository interfaces in controllers, which gives us a weak code connectivity. Those. working with the interface, you know that there is a method and it has an implementation.
- The possibility of using DI-containers. Allows you to change the behavior of classes throughout the program. You simply register another implementation without changing the code of your controllers.
- Ability to test. Using the interface, you do not need to have a database for testing. It is enough to write a class to imitate the database.
- The logic of working with the database is fixed. For example, when adding a new record to the database with a specific value, it will be necessary to make an additional record in the database logs. Agree, it is not convenient to write this in the controller every time. The probability to forget to write is not canceled.
- You can write a generalized repository and use it. Need to expand repository features? Not a problem, take inheritance and append / redefine methods. This will reduce the amount of code and the likelihood of an error.
- When using a repository, we can get away from explicit binding to a single data store and thereby increase the flexibility of the application.
What are the advantages of the application?
When using the repository, a big plus will be the fact that you have divided the logic of working with the database and their processing, which further reduces the time for correcting errors associated with the database operation.
Where arrived in speed, or maybe in readability, or performance
In readability and in performance. The compiler will generate less IL code. You do not repeat the logic of work in each method. She is in one place.
- Let us begin in order point 1 of the answer: working with a method or function in the controller - do I see its implementation? point 2 of the answer: refers to the moment if in several controllers I use the same principle for implementing my idea ... and if my controllers are separated, one is responsible for registration, the second is responsible for working with user data, if the user is registered, etc. and so on .. - SergeyE
- @Eikhner working with the interface you do not see the implementation, but only know what it is. You can inherit the interface to two different classes and choose which one to inject. p.2 There is not the controller logic, but the logic of working with the database. For example, there is a
UserRepositoryand theGetByIdmethod in it. In the controller, you simply call this method, and in theUserRepositroryalready doing everything necessary. And by the way, the source of information can be either a database or a text file. In theUserRepositoryyou define how to search for information - Vadim Prokopchuk - friend, what's stopping me from writing everything you are talking about in the controller? It’s bad that the forum doesn’t have the opportunity to carry out a dialogue using the code directly ... And what does the IL code compiler write, when from different places of the controller we turn to the same function or repository method ??? - SergeyE
- What is the difference between the entity id request from the repository, and the same entity id request from the controller - does their code differ as such? - SergeyE
- Code duplication and the likelihood of losing logic. - Vadim Prokopchuk