Зависимости между слоями приложения | Внедрение конструктора, время жизни | Сквозные аспекты приложения, перехват, декоратор
В двух предыдущих заметках мы рассмотрели основные части веб-приложения. У нас есть объект реализующий бизнес логику – MyService. Есть IRepository, отвечающий за взаимодействие с БД. Не хватает ролевой модели и логирования.
[HttpPost]
public void DeleteProduct(int id)
{
if (!Thread.CurrentPrincipal.IsInRole("ProducManager")
throw new UnauthorizedAccessException();
this.MyService.DeleteProduct(id);
}
Листинг 1. Проверка прав в методе контроллера
[PrincipalPermission(SecurityAction.Demand,Role = "ProductManager")]
[HttpPost]
public void DeleteProduct(int id)
{
this.MyService.DeleteProduct(id);
}
Листинг 2. Проверка прав в методе контроллера с помощью атрибута
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
Принцип подстановки Лисков (Liskov Substitution Principle из SOLID) (Лисков это фамилия)
Клиент должен рассматривать все реализации абстракции как эквивалентные. Мы должны иметь возможность заменять одну реализацию на другую, не разрушая потребителя.
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.
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 с использование атрибута
IMyService service = new MyService(...);
service = new MySecurityService(service, ...);
service = new MyLoggerService(service, ...);
service = new MyExceptionSaveService(service, ...);
Листинг 6. Расширение функционала с помощью декораторов. Гипотетический пример.
К сожалению, не доступен сервер mySQL