3

使用封装在接口中的 EF DbContext,为每个 Web 请求注入依赖项,以确保整个请求处理相同的上下文。还有一个自定义RoleProvider,它使用DbContextby 接口来自定义授权服务。

到目前为止,我一直在使用服务定位器模式来解析DbContextcustomRoleProvider的无参数构造函数中的实例。这导致了一些小问题,因为它RoleProvider是单例的,因此它可能会DbContext无限期地保留 a 而其他请求可能希望在Application_EndRequest.

我现在有一个基于此的解决方案,尽管使用了与 windsor 不同的 ioc 容器。我可以使用 DIRoleProvider为每个 http 请求新建一个自定义实例。

我的问题是,我应该吗?

有一个开放的DbContext悬挂RoleProvider似乎很浪费。另一方面,我知道每个 MVC 都会AuthorizeAttribute命中RoleProvider(如果它有一个非空Roles属性,我们大多数人都会这样做),所以我想已经有一个DbContext等待的可能很有用。

另一种方法是为不是每个 web 请求注入不同DbContext的。RoleProvider这样DbContext,只为 web 请求而存在的 s 可以在最后被处理,而不会影响单例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)
}

网络配置:

<roleManager enabled="true" defaultProvider="InjectedRoleProvider">
    <providers>
        <clear />
        <add name="InjectedRoleProvider" type="MyApp.InjectedRoleProvider" />
    </providers>
</roleManager>

IoC 容器:

Container.RegisterPerWebRequest<RoleProvider, CustomRoleProvider>();

至于 CUD,我的中只实现了 1 种方法CustomRoleProvider

public override string[] GetRolesForUser(string userName)

这是 MVC AuthorizeAttribute(and IPrincipal.IsInRole) 使用的唯一方法,在所有其他方法中,我只是

throw new NotSupportedException("Only GetRolesForUser is implemented.");

由于提供者没有角色 CUD 操作,我不担心交易。

4

1 回答 1

3

看看Griffin.MvcContrib项目。它包含一个使用 MVC 的MembershipProvider实现。RoleProviderDependencyResolver

你可以RoleProvider这样配置:

<roleManager enabled="true" defaultProvider="MvcRoleManager">
  <providers>
    <clear />
    <add name="MvcRoleManager" 
      type="Griffin.MvcContrib.Providers.Roles.RoleProvider, Griffin.MvcContrib"
    />
  </providers>
</roleManager>

它使用 System.Web.MVCDependencyResolver类,因此您需要为IDependencyResolver您正在使用的 DI 容器配置一个实现。使用 Simple Injector(和SimpleInjector.MVC3 集成 NuGet 包),您需要在Application_Start事件中进行以下配置:

container.RegisterAsMvcDependencyResolver();

Griffin.MvcContrib.Providers.Roles.RoleProvider依赖于IRoleRepository在同一程序集中定义的a 。不必实现一个完整的角色提供者,您现在只需实现IRoleRepository并在您的容器中注册它:

container.Register<IRoleRepository, MyOwnRoleRepository>();

可以在 NuGet 上找到这个项目。

更新

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

Griffin.MvcContrib RoleProvider 将是单例的,问题现在转移到IRoleRepository及其依赖项,但问题确实仍然存在。

如果你所做的只是从角色提供者中读取(永远不要更新数据库);在这种情况下,您选择哪个生命周期并不重要,只要您不在DbContext线程上重用相同的生命周期。

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

于 2012-03-23T07:22:53.813 回答