. Net Core MVC website development (Ninesky) 2.3. Project architecture adjustment-control reversal and dependency injection usage, mvcninesky
Adjusting the project architecture again is becauseDezhouIn a chat, my original idea is that the project should be as simple as possible. Don't be too complicated. Only DbContext injection is used, and other interfaces without writing are highly coupled. After chatting with dezhou, I thought about it carefully. Let's just decouple it. Originally, according to the software design model, it should be high cohesion and low coupling. Low coupling makes the project module independent of other modules, added maintainability and portability!
Note: The previous blog did not record every step of project operations in detail. In fact, it takes a lot of time to write a blog, and there is a lot of useless information in the whole blog. For MVC, controllers, views, and classes will be added, which are the most basic requirements. In the previous blog, all these requirements will not be detailed in the future, I mainly write some ideas and key code. The specific content will be downloaded after the blog in the form of source code.
1. Default project structure
Let's take a look at the default project structure generated by vs2015.
The model, data access, business logic, and view-related content in the project are all in the same project. The view, business logic, and display are tightly coupled, when the project becomes larger, especially when the project is updated after a period of time, the reading is messy. Sometimes a small change may cause an error in the export of the entire project, causing a headache.
Layer 2 Architecture
Let's look at the three-tier architecture:
- User Interface presentation layer (USL)
- Business logic layer (BLL)
- Data access layer (DAL)
The three-tier architecture mainly makes the project structure clearer and the division of labor clearer, which is conducive to later maintenance and upgrade. It may not improve the performance, because when the subprogram module is not executed, the main program module can only be in the waiting state. This shows that dividing an application into layers will cause some loss in its execution speed. However, in terms of team development efficiency and maintainability, task allocation is easy and maintainability is high.
According to the three-layer idea, both controller (C) and view (V) in MVC process the content related to interface display, which belongs to the user interface presentation layer (USL), model (M) is the data exchanged between the Controller and the view, so the MVC framework should belong to the user interface presentation layer on the third layer.
The data access layer (DAL) and business logic layer (BLL), business logic layer, and user interface presentation layer (USL) also need to exchange data and simply separate the model (M, as a controller and view, and data exchanged between three layers. Iii. High Coupling
Let's look at Ninesky's current project structure, such:
There are four projects:
Ninesky. DataLibrary is the data access layer that provides database access support.
Ninesky. Base is the business logic layer responsible for processing the business logic.
Ninesky. Web user interface presentation layer (USL), responsible for display page and display project logic processing.
Ninesky. Models is the data entity exchanged between layers.
From the above, we can see that the project is layered based on the three-tier idea. PS: Some friends asked why the project name is DataLibrary, Base, not DAL or BLL? This may be the cause of obsessive-compulsive disorder. I looked at DAL and BLL's project name very uncomfortable. I changed my favorite name. In fact, they all have the same functions.
Let's take a look at the call of the project.
Take a look at the CategoryService class of Ninesky. Base.
Position 1 in the Code declares the class CategoryRepository, which is a class in Ninesky. DataLibrary. Location 2 instantiates this project. In location 3, we directly call the Find method of this class. From the above we can see that the CategoryService class depends on the CategoryRepository class; The Ninesky. Base project depends on the Ninesky. DataLibrary project. A project class accurately calls the method of another project class, so they are highly coupled. High coupling is a problem in software design. Decoupling is required to convert dependency implementation code into dependency logic. At this time, an abstract layer (usually an interface) must be introduced ). Iv. Dependency Interfaces
We will add a dll project Ninesky. InterfaceDataLibrary and add references to Ninesky. DataLibrary to the Ninesky. InterfaceDataLibrary project.
Add the InterfaceBaseRepository interface to the Ninesky. InterfaceDataLibrary project.
1 using System; 2 using System. collections. generic; 3 using System. linq. expressions; 4 using System. threading. tasks; 5 6 namespace Ninesky. interfaceDataLibrary 7 {8 /// <summary> 9 // warehousing base class interface 10 /// </summary> 11 /// <typeparam name = "T"> </ typeparam> 12 public interface InterfaceBaseRepository <T> where T: class 13 {14 /// <summary> 15 // query [excluding navigation attributes] 16 /// </summary> 17 /// <param name = "predicate"> query Expression </param> 18 // <returns> Object </returns> 19 T Find (Expression <Func <T, bool> predicate); 20} 21}
View Code modify the BaseRepository Code so that BaseRepository inherits InterfaceBaseRepository
1 using System; 2 using System. collections. generic; 3 using System. linq; 4 using System. linq. expressions; 5 using Microsoft. entityFrameworkCore; 6 using Ninesky. interfaceDataLibrary; 7 8 namespace Ninesky. dataLibrary 9 {10 /// <summary> 11 /// warehouse base class 12 /// </summary> 13 public class BaseRepository <T>: InterfaceBaseRepository <T> where T: class
14 {15 protected DbContext _ dbContext; 16 public BaseRepository (DbContext dbContext) 17 {18 _ dbContext = dbContext; 19} 20 21 /// <summary> 22 // query [without navigation attributes] 23 /// </summary> 24 /// <param name = "predicate"> query Expression </param> 25 // <returns> Object </returns> 26 public virtual T Find (Expression <Func <T, bool> predicate) 27 {28 return _ dbContext. set <T> (). singleOrDefault (predicate); 29} 30} 31} 32
View Code
Reference Ninesky. InterfaceDataLibrary in the Ninesky. Base project. We are modifying the CategoryService code.
1 public class CategoryService 2 {3 private InterfaceBaseRepository <Category> _ categoryRepository; 4 public CategoryService (DbContext dbContext) 5 {6 _ categoryRepository = new BaseRepository <Category> (dbContext ); 7} 8 9 // <summary> 10 // search for 11 /// </summary> 12 // <param name = "Id"> topic Id </param> 13 // <returns> </returns> 14 public Category Find (int Id) 15 {16 return _ categoryRepository. find (c => c. categoryId = Id); 17} 18}
View Code
The variable type InterfaceBaseRepository is declared at the beginning of the Code. In the constructor, InterfaceBaseRepository is instantiated as the BaseRepository type.
Now the Ninesky. Base project is still dependent on the Ninesky. DataLibrary project, and there is no decoupling. If you want to remove the dependencies of multiple Ninesky. DataLibrary, you need to find a way to instantiate the interface and transfer it out of the project.
V. Control reversal
The inversion of control refers to moving the creation of dependency to the outside of the class. Modify the constructor of the CategoryService class.
The constructor passes an interface-type parameter. Now the class is completely irrelevant to Ninesky. DataLibrary. You can delete references to Ninesky. DataLibrary projects.
Now there is another new question: how to implement control inversion and how to instantiate interfaces?
Common solutions include service locators and dependency injection.
Vi. Service Positioner
The service positioner is instantiated in the class.
Create a project separately, add references to the project, and then instantiate the project in the factory class.
1 public class Factory 2 { 3 public InterfaceBaseRepository<Category> GetBaseRepository() 4 { 5 return new BaseRepository<Category>(); 6 } 7 }
View Code
The advantage of service positioner is that it is easy to implement and a global service positioner can be created. The disadvantage is that the component requirements are not transparent. Ninesky adopts another implementation of control inversion: dependency injection.
7. Dependency Injection.
In the past, injection in. Net MVC was quite troublesome. Fortunately,. Net Core MVC has built-in support for dependency injection.
Modify the CategoryController code and use constructor injection. In this example, the class at the data storage layer is directly used in the Controller for injection, but the class at the business logic layer is not used.
Constructor injection is used in the controller, and the constructor passes the CategoryService parameter.
1 public class CategoryController: Controller 2 {3 /// <summary> 4 // data context 5 /// </summary> 6 private NineskyDbContext _ dbContext; 7 8 // <summary> 9 // column service 10 /// </summary> 11 private CategoryService _ categoryService; 12 13 public CategoryController (CategoryService categoryService) 14 {15 _ categoryService = categoryService; 16} 17 18 /// <summary> 19 /// View column 20 /// </summary> 21 /// <pa Ram name = "id"> topic Id </param> 22 // <returns> </returns> 23 [Route ("/Category/{id: int}")] 24 public IActionResult Index (int id) 25 {26 var category = _ categoryService. find (id); 27 if (category = null) return View ("Error", new Models. error {Title = "Error message", Name = "", Description = "ID id 【 [" + ID + "], an Error occurs, and this topic does not exist. "}); 28 switch (category. type) 29 {30 case CategoryType. general: 31 if (category. general = null) return View ("Error", new Models. error {Title = "Error message", Name = "incomplete topic data", Description = "unable to find topic [" + category. detailed data of Name +. "}); 32 return View (category. general. view, category); 33 case CategoryType. page: 34 if (category. page = null) return View ("Error", new Models. error {Title = "Error message", Name = "incomplete topic data", Description = "unable to find topic [" + category. detailed data of Name +. "}); 35 return View (category. page. view, category); 36 case CategoryType. link: 37 if (category. link = null) return View ("Error", new Models. error {Title = "Error message", Name = "incomplete topic data", Description = "unable to find topic [" + category. detailed data of Name +. "}); 38 return Redirect (category. link. url); 39 default: 40 return View ("Error", new Models. error {Title = "Error message", Name = "topic data Error", Description = "topic [" + category. name + "] type error. "}); 41 42} 43} 44}
View Code
Then we enter the Web Startup class Startup for injection. For example:
The first red box is the context injected in "2.1. Foreground display of the topic;
The second red box to the fourth red box is the content added today.
The InterfaceBaseRepository interface is injected into the third red box and instantiated using BaseRepository.
There is a second red box because there is a DbContext parameter when BaseRepository is instantiated. The required parameters must be injected before injection, and the system will not automatically convert NineskyDbContext to DbContext. Therefore, a DbContext parameter must be injected.
The fourth red box is to inject CategoryService. Here, CategoryService can also use interfaces, but the time is not written.
As you can see, CategoryService removes the dependency on BaseRepository, in Ninesky. the Base project does not include Ninesky. dataLibrary carries out any dependencies. Class instantiation is injected in Web projects, and Web projects perform Ninesky. dataLibrary is dependent. The same method can be used to decouple Ninesky. Base projects from Web projects.
If you want to completely remove the dependencies of Ninesky. Web projects on Ninesky. DataLibrary and Ninesky. Base projects, you can use the configuration file to load them.
You can view the data in the F5 browser, but the data is incomplete because the data storage layer Code does not contain navigation attributes.
Other code hosting address: https://git.oschina.net/ninesky/Ninesky
Article published by: http://www.ninesky.cn
http://mzwhj.cnblogs.com/
Code package download: ninesky2.3project development adjustment-control reverse conversion and dependency injection use .rar
Returned directory