我有一个使用注入BLL服务的页面:一个简单的服务返回一组具有如下函数的对象:
public IMyService { List<Foo> All(); }
普通用户有默认实现。 现在,我需要具有管理角色的用户可以使用该服务的另一个实现来查看更多对象。
在哪里可以配置我的页面以使用第二个实现?
我的第一个解决方案是将依赖项放在页面中的IUnityContainer中,并使用它来解析依赖项:
[Dependency]
public IUnityContainer Container { get; set;}
Page_Init(..)
{
_myService = User.IsInRole(MyRoles.Administrators)
? Container.Resolve<IMyService>("forAdmins")
: Container.Resolve<IMyService>();
}
但它非常难看:它是一个ServiceLocator,并且它既不可扩展也不可测试。
我该如何处理这种情况?也许为每个角色创建一个子容器?
答案 0 :(得分:4)
您可以将其作为装饰器和复合材料的组合实现:
public SelectiveService : IMyService
{
private readonly IMyService normalService;
private readonly IMyService adminService;
public SelectiveService(IMyService normalService, IMyService adminService)
{
if (normalService == null)
{
throw new ArgumentNullException("normalService");
}
if (adminService == null)
{
throw new ArgumentNullException("adminService");
}
this.normalService = normalService;
this.adminService = adminService;
}
public List<Foo> All()
{
if(Thread.CurrentPrincipal.IsInRole(MyRoles.Administrators))
{
return this.adminService.All();
}
return this.normalService.All();
}
}
这遵循单一责任原则,因为每个实现只做一件事。
答案 1 :(得分:1)
我同意你的看法,你当前的设计是丑陋的。我个人不喜欢这种方法的是你在页面内设置安全配置。当有人忘记这个时你会遇到安全漏洞,你如何测试这个页面配置是否正确?
以下是两个想法: 第一: 使用能够根据用户角色解析该服务的正确实现的工厂:
public static class MyServiceFactory
{
public static IMyService GetServiceForCurrentUser()
{
var highestRoleForUser = GetHighestRoleForUser();
Container.Resolve<IMyService>(highestRoleForUser);
}
private static string GetHighestRoleForUser()
{
var roles = Roles.GetRolesForUser().ToList();
roles.Sort();
return roles.Last();
}
}
第二:
在该界面上有多种方法,一种用于普通用户,一种用于管理员。该接口的实现可以在受限制的方法上定义PrincipalPermissionAttribute
:
class MyServiceImpl : IMyService
{
public List<Foo> All()
{
// TODO
}
[PrincipalPermission(SecurityAction.Demand, Role ="Administrator")]
public List<Foo> AllAdmin()
{
// TODO
}
}
我希望这会有所帮助。