每个Web请求的EF DbContext +每个Web请求或单例的自定义RoleProvider = RoleProvider?

时间:2012-03-22 18:38:42

标签: asp.net-mvc dependency-injection singleton asp.net-roles simple-injector

使用包含在接口中的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

另一种方法是为不是每个网络请求的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操作系统上没有角色,我不担心交易。

1 个答案:

答案 0 :(得分:3)

看看Griffin.MvcContrib项目。它包含使用MVC MembershipProvider的{​​{1}}和RoleProvider实现。

您可以像这样配置DependencyResolver

RoleProvider

它使用System.Web.MVC <roleManager enabled="true" defaultProvider="MvcRoleManager"> <providers> <clear /> <add name="MvcRoleManager" type="Griffin.MvcContrib.Providers.Roles.RoleProvider, Griffin.MvcContrib" /> </providers> </roleManager> 类,因此您需要为正在使用的DI容器配置DependencyResolver实现。使用Simple Injector(和SimpleInjector.MVC3 integration NuGet package),您需要在IDependencyResolver事件中进行以下配置:

Application_Start

container.RegisterAsMvcDependencyResolver(); 依赖于在同一程序集中定义的Griffin.MvcContrib.Providers.Roles.RoleProvider。您现在可以只实现IRoleRepository并将其注册到容器中,而不必实现完整的角色提供程序:

IRoleRepository

您可以在NuGet上找到此项目here

<强>更新

现在让我们回答这个问题:

Griffin.MvcContrib RoleProvider将是单例,现在问题转移到container.Register<IRoleRepository, MyOwnRoleRepository>(); 及其依赖关系,但问题确实仍然存在。

如果您从角色提供程序中读取所有内容(从不更新数据库);在这种情况下,只要您不在线程上重复使用相同的IRoleRepository,您选择的生命周期并不重要。

但是,当您使用角色提供程序更新数据库时,情况会有所不同。在这种情况下,我会给它自己的上下文,让它在每次操作后显式提交。因为如果你不这样做,谁将承诺进行这些改变?在命令处理程序(尤其是TransactionCommandHandlerDecorator)的上下文中运行时,操作将在命令成功后提交,并在命令失败时回滚。也许在命令失败时将该更改回滚可以。但是当角色提供程序在命令处理程序的上下文之外运行时,谁将提交它?我相信你能够解决这个问题,但我相信你最终会得到一个难以掌握的系统,它会让那些试图找出这些改变没有提交的开发人员感到眩目。