【问题标题】:Resolving per-user/per-request dependency with Autofac in StartUp在 StartUp 中使用 Autofac 解决每个用户/每个请求的依赖关系
【发布时间】:2016-09-26 08:26:24
【问题描述】:

我正在尝试解决 ASP.Net WebApi 2 项目的 StartUp 类中的依赖关系。 Autofac 用于配置依赖注入。场景如下:

  • 不同的用户看到不同的数据。每个用户都有特定的权限,允许该用户查看特定的数据。
  • 正在使用包含多个边界的域驱动架构。不允许在域和数据访问层进行跨界调用。这一点很重要,因为为了获取用户允许数据的所有代码,我们需要做一些跨界调用。
  • 我们希望过滤数据访问层内的数据,只为每个用户检索允许的数据。此处不允许跨境通话。
  • 为了实现这一点,我想从用户可以看到的记录中注入所有代码的列表。
  • 为此,我尝试向 Autofac 注册一个工厂,它会根据用户创建此列表。我们将工厂称为StuffFactory,将列表称为StuffModel

现在的问题是所有这些都需要注册为InstancePerRequest。这会抛出一个InvalidOperatonException,这听起来很合理,因为在StartUp 中无法访问请求生命周期范围?

从请求实例的范围中看不到带有与“AutofacWebRequest”匹配的标记的范围。这通常表明注册为 per-HTTP 请求的组件正在由 SingleInstance() 组件(或类似场景)请求。在 Web 集成下,始终从 DependencyResolver.Current 或 ILifetimeScopeProvider.RequestLifetime 请求依赖项,而不是从容器本身.

允许的东西在容器中注册的代码:

private IContainer RegisterAllowedStuff(IContainer container)
{
    var builder = new ContainerBuilder();

    builder.Register(x => GetAllowedStuffForUser(container))
        .As<StuffModel>()
        .InstancePerRequest();

    builder.Update(container);
    return container;
}

private static StuffModel GetAllowedStuffForUser(IContainer container)
{
    var stuffFactory = container.Resolve<IStuffFactory>();
    return stuffFactory.CreateStuffModel(Helper.GetParsedUserName());
}

我有点纠结于如何从这里前进。对于我完全监督的 Autofac 问题,是否有一个简单的解决方案?有人可能对我如何实现这一点有更好的了解吗?提前致谢!

【问题讨论】:

    标签: c# asp.net-web-api dependency-injection autofac


    【解决方案1】:

    您的启动类中不应包含任何业务逻辑。 Autofac 允许您注册将在解析时调用的 lambda 方法。此时有一个HttpRequest可用,所以你可以将这些lambda注册为InstancePerRequest

    在您的情况下,如果您想将StuffModel 注册为InstancePerRequest,您可以这样做:

    builder.RegisterType<ConcreteStuffFactory>()
           .As<IStuffFactory>();
    builder.Register(c => c.Resolve<IStuffFactory>()
                           .CreateStuffModel(Helper.GetParsedUserName()))
           .As<IStuffModel>()
           .InstancePerRequest();
    

    顺便说一句,我也不会使用看起来像反模式的 Helper 类,您可以创建一个服务来获取当前用户的用户名,例如 IUserContextProvider。您还可以在ConcreteStuffFactory 上添加IUserContextProvider 依赖项

    public class ConcreteStuffFactory
    {
        public ConcreteStuffFactory(IUserContextProvider userContextProvider)
        {
             this._userContextProvider = userContextProvider; 
        }
    
        private readonly IUserContextProvider _userContextProvider; 
    
    
        public IStuffModel CreateStuffModel()
        {
            String userName = this._userContextProvider.UserName; 
            // do things with userName
        }
    }
    

    和注册:

    builder.RegisterType<ConcreteStuffFactory>()
           .As<IStuffFactory>();
    builder.RegisterType<HttpContextUserContextProvider>()
           .As<IUserContextProvider>()
           .InstancePerRequest();
    builder.Register(c => c.Resolve<IStuffFactory>().CreateStuffModel())
           .As<IStuffModel>()
           .InstancePerRequest();
    

    【讨论】:

    • 我知道我忽略了这样一个简单的解决方案。谢谢!至于Helper:完全有效的评论,只是重构了代码库的旧部分。
    【解决方案2】:

    在我看来,您提供的异常文本准确地解释了您的代码无法运行的原因。

    在我看来,很明显不可能在启动方法中访问每个请求实例,该方法本身只会在应用程序的生命周期内调用一次。

    我会考虑将您的代码移动到可以按请求调用的位置。例如,您是否考虑过写一个custom action filter

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多