📜 ⬆️ ⬇️

Dependency injection in .Net Mark Siman 3 - Cross-cutting aspects of the application, interception, decorator

Dependencies between application layers | Designer implementation, lifetime | Cross-cutting aspects of the application, interception, decorator

In the two previous notes we covered the main parts of the web application. We have an object that implements business logic - MyService. There is an IRepository responsible for interacting with the database. Not enough role model and logging.

Decorator


There is an opinion that in MVC web applications it is convenient to check the rights right at the beginning of the controller method. For example:
')
[HttpPost] public void DeleteProduct(int id) { if (!Thread.CurrentPrincipal.IsInRole("ProducManager") throw new UnauthorizedAccessException(); this.MyService.DeleteProduct(id); }  1.      

or using an attribute

 [PrincipalPermission(SecurityAction.Demand,Role = "ProductManager")] [HttpPost] public void DeleteProduct(int id) { this.MyService.DeleteProduct(id); }  2.         

This approach has its advantages. Not the most obvious of these is that the rights check is rendered from MyService. It turns out the division of responsibility: the core business of logic performs only its task and is not engaged in verification of rights. Speaking in "popular science" language follows the Single Responsibility Principle (SOLID).

But isn't the role model part of business logic? In addition, there are more complex checks. For example, ProducManager may not be responsible for all products, but only for certain product categories, etc. It will not be good to place such checks in controllers. In MyService, as we already know, they also have no place. How to be?

The “Decorator” pattern comes to the rescue.

We have already agreed that


The controller code in Listing 3 satisfies both remarks:

 public class MyController { private readonly IMyService MyService; public class MyController(IMyService service) { if(service == null) throw new ArgumentNullException(nameof(service)); MyService = service } [HttpPost] public void DeleteProduct(int id) { this.MyService.DeleteProduct(id); } }  3. MyController  IMyService 

We can do the decorator MySecurityService: IMyService (Listing 4), in which there is a rights check. And, since our code follows the Liskov substitution principle, use it in the controller. In this case, we do not need to change something in MyController.
The substitution principle of Liskov (Liskov Substitution Principle of SOLID) (Liskov is the last name)

The client must treat all implementations of abstraction as equivalent. We should be able to replace one implementation with another without destroying the consumer.


 class MySecurityService : IMyService { private readonly IMyService MyService; private readonly IUserInfo UserInfo; public MySecurityService(IMyService service, IUserInfo userInfo) { if(service == null) throw new ArgumentNullException(nameof(service)); if(userInfo == null) throw new ArgumentNullException(nameof(userInfo)); MyService = service; UserInfo = userInfo; } public void DeleteProduct(int id) { if(UserInfo.IsInRole("ProductManager")) throw new UnauthorizedAccessException(nameof(service)); this.MyService.DeleteProduct(id); } }  4.  MySecurityService           DeleteProduct. 

or so, with the attribute:

 class MySecurityService : IMyService { private readonly IMyService MyService; public MySecurityService(IMyService service) { if(service == null) throw new ArgumentNullException(nameof(service)); MyService = service; } [PrincipalPermission(SecurityAction.Demand,Role = "ProductManager")] public void DeleteProduct(int id) { this.MyService.DeleteProduct(id); } }  5.  MySecurityService    

Note that you should not use the PrincipalPermission attribute directly in MyService. It is better if MyService is not associated with a specific rights verification implementation.

In the same way, using decorators, you can add logging and error handling. Below is an example (for understanding only) of the assembly of full functionality: decorators are nested inside each other.

 IMyService service = new MyService(...); service = new MySecurityService(service, ...); service = new MyLoggerService(service, ...); service = new MyExceptionSaveService(service, ...);  6.     .  . 

Note that decorators can be in different projects (in different assemblies). You can, for example, have separate error handling decorators for the web application and the mobile client.

Retreat:

In the literature can be found the term "interception". Here are the listings 1 and 2 essentially implement the "Interception": intercept control, do something and give control to the main code. The "decorator" also implements "Interception".

Parts of the program that apply to many functions of the application, such as logging, security, caching, etc. referred to as “Aspects” or “Cross-cutting Aspects.”

If at the interview you are asked about logging / caching / checking rights, you can wrap up a phrase like this: I think that to implement the end-to-end aspects of the application, it is better to use traps based on the Decorator pattern. Of course, for this, the code must follow the principle of Barbara Liskov's substitution.

Conclusion


We covered typical web application development tasks:

- How to break an application into layers
- How to organize the interaction between classes
- How to implement end-to-end aspects of the application

We made the best understanding of the topic “dependency injection”. Knowing why you need to use the OT connection of the ioc-container will be a matter of technique.

Source: https://habr.com/ru/post/344986/


All Articles