使用包装在接口中的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 MembershipProviderRoleProviderDependencyResolver实现。

您可以这样配置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/

10-11 04:09