使用包装在接口中的EF DbContext
,每个Web请求注入依赖项,以确保整个请求处理相同的上下文。还有一个自定义RoleProvider
,它通过接口使用DbContext
来自定义授权服务。
到目前为止,我一直在使用服务定位器模式来解析自定义DbContext
的无参数构造函数中的RoleProvider
实例。这引起了一些小问题,因为RoleProvider
是单调的,因此它可能会无限期地保留在DbContext
上,而其他请求可能希望在Application_EndRequest
期间进行处理。
我现在有一个解决方案based on this,尽管使用的是与Windsor不同的ioc容器。我可以使用DI为每个http请求创建一个自定义RoleProvider
实例。
我的问题是,应该吗?
将DbContext
挂在RoleProvider
旁似乎很浪费。另一方面,我知道每个MVC AuthorizeAttribute
都命中RoleProvider
(如果它具有非null的Roles
属性,我们大多数人都这样做),所以我想已经有一个DbContext
在等待中。
替代方法是为不是每个Web请求的DbContext
注入不同的RoleProvider
。这样,仅用于Web请求的DbContext
可以放在最后,而不会影响单调的RoleProvider
。
哪种方法更好,为什么?
评论后更新
史蒂文,这基本上就是我所做的。唯一的区别是我不依赖System.Web.Mvc.DependencyResolver
。相反,我在自己的项目中基本上拥有相同的东西,只是命名不同:
public interface IInjectDependencies
{
object GetService(Type serviceType);
IEnumerable<object> GetServices(Type serviceType);
}
public class DependencyInjector
{
public static void SetInjector(IInjectDependencies injector)
{
// ...
}
public static IInjectDependencies Current
{
get
{
// ...
}
}
}
这些类是项目核心API的一部分,并且与MVC在不同的项目中。这样,其他项目(以及域项目)就无需依赖
System.Web.Mvc
即可针对其DependencyResolver
进行编译。有了这个框架,到目前为止,用SimpleInjector换出Unity一直很轻松。这是多用途单例RoleProvider设置的样子:
public class InjectedRoleProvider : RoleProvider
{
private static IInjectDependencies Injector
{ get { return DependencyInjector.Current; } }
private static RoleProvider Provider
{ get { return Injector.GetService<RoleProvider>(); } }
private static T WithProvider<T>(Func<RoleProvider, T> f)
{
return f(Provider);
}
private static void WithProvider(Action<RoleProvider> f)
{
f(Provider);
}
public override string[] GetRolesForUser(string username)
{
return WithProvider(p => p.GetRolesForUser(username));
}
// rest of RoleProvider overrides invoke WithProvider(lambda)
}
Web.config:
<roleManager enabled="true" defaultProvider="InjectedRoleProvider">
<providers>
<clear />
<add name="InjectedRoleProvider" type="MyApp.InjectedRoleProvider" />
</providers>
</roleManager>
IoC容器:
Container.RegisterPerWebRequest<RoleProvider, CustomRoleProvider>();
至于CUD,我的
CustomRoleProvider
中仅实现了一种方法:public override string[] GetRolesForUser(string userName)
这是MVC的
AuthorizeAttribute
(和IPrincipal.IsInRole
)使用的唯一方法,而从所有其他方法中,我只是throw new NotSupportedException("Only GetRolesForUser is implemented.");
由于CUD ops在提供程序上不起作用,因此我不必担心交易。
最佳答案
看一下Griffin.MvcContrib项目。它包含使用MVC MembershipProvider
的RoleProvider
和DependencyResolver
实现。
您可以这样配置RoleProvider
:
<roleManager enabled="true" defaultProvider="MvcRoleManager">
<providers>
<clear />
<add name="MvcRoleManager"
type="Griffin.MvcContrib.Providers.Roles.RoleProvider, Griffin.MvcContrib"
/>
</providers>
</roleManager>
它利用了System.Web.MVC
DependencyResolver
类,因此您需要为正在使用的DI容器配置IDependencyResolver
实现。使用Simple Injector(和SimpleInjector.MVC3 integration NuGet package),您需要在Application_Start
事件中进行以下配置:container.RegisterAsMvcDependencyResolver();
Griffin.MvcContrib.Providers.Roles.RoleProvider
依赖于在同一程序集中定义的IRoleRepository
。现在不必实现一个完整的角色提供程序,您只需实现IRoleRepository
并将其注册到您的容器中:container.Register<IRoleRepository, MyOwnRoleRepository>();
您可以在NuGet上找到该项目here。
更新
现在让我们回答这个问题:
Griffin.MvcContrib RoleProvider将是单例的,该问题现在移到
IRoleRepository
及其依赖项,但问题确实仍然存在。如果您所做的全部都是从角色提供程序中读取的(则永远不要更新数据库);在那种情况下,选择哪个生存期都没有关系,只要您不在线程上重用相同的
DbContext
即可。但是,当您使用角色提供程序来更新数据库时,情况会有所不同。在那种情况下,我会给它自己的上下文,并让它在每次操作后显式提交它。因为如果您不这样做,谁来做那些更改?当在命令处理程序(尤其是TransactionCommandHandlerDecorator)的上下文中运行时,该操作将在命令成功后提交,并在命令失败时回滚。当命令失败时,回滚更改可能很好。但是,当角色提供程序在命令处理程序的上下文之外运行时,谁将提交它?我相信您将能够解决此问题,但是我相信您最终会获得一个难以掌握的系统,并且它将使其他尝试找出为什么未提交这些更改的开发人员感到眼花azz乱。
关于asp.net-mvc - 每个Web请求的EF DbContext +自定义RoleProvider =每个Web请求或单例的RoleProvider?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9828448/