简体   繁体   English

每个会话在Autofac中向接口注册不同的类?

[英]Register different classes to interfaces in Autofac per session?

What I'm asking may not be possible/recommended, but I couldn't find anything on it online. 我要询问的内容可能无法/推荐,但我在网上找不到任何内容。 Feel free to point me in a different direction. 请随意指向我一个不同的方向。

What I'm trying to accomplish: 我要完成的工作:

Register different Classes to an Interface in Autofac conditionally - per user session 有条件地在Autofac中接口注册不同的 -每个用户会话

For example, I'd like to take my current implementation: 例如,我想采用当前的实现:

var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType(typeof(RunHistoryRepository))
       .As(typeof(IRunHistoryRepository));

And add something like: 并添加如下内容:

var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
if(UserIsInThisRole())
  builder.RegisterType(typeof(RunHistoryRepository))
         .As(typeof(IRunHistoryRepository));
else
    builder.RegisterType(typeof(DifferentRunHistoryRepository))
           .As(typeof(IRunHistoryRepository));

What I know: 我知道的:

This code is called from the Global.asax, so this will run only when the site is started. 此代码是从Global.asax中调用的,因此仅在网站启动时才运行。 Because of this, I feel like this logic can't/shouldn't go here... 因此,我觉得这种逻辑不能/不应该在这里...

What I've tried: 我试过的

I've tried the above implementation and it doesn't work since this code runs on application start - not each user visit. 我已经尝试了上述实现,但由于此代码在应用程序启动时运行-并非每个用户访问,都无法正常运行。 I've also looked into registering these types with parameters (and then I could modify the RunHistoryRepository logic to select a data source conditionally): 我还研究了如何使用参数注册这些类型(然后可以修改RunHistoryRepository逻辑以RunHistoryRepository地选择数据源):

builder.RegisterType<RunHistoryRepository>()
   .As<IRunHistoryRepository>()
   .WithParameter("DataSource", "DataSource1Key");

But I would need the parameter to be dynamic on user visit: 但是我需要参数在用户访问时是动态的:

builder.RegisterType<RunHistoryRepository>()
   .As<IRunHistoryRepository>()
   .WithParameter("DataSource", UserDataSource());

And again, since this executes from the Global.asax it will register before a user even hits the page. 再一次,由于这是从Global.asax执行的,因此它将在用户点击页面之前进行注册。

Is it possible to register these types dynamically for each user that visits the site or do I need to handle this logic elsewhere? 是否可以为访问该网站的每个用户动态注册这些类型,还是我需要在其他地方处理此逻辑?

There's a couple of options here. 这里有两个选择。 You can register functions in the container that will be executed every time you ask for a service to be resolved 您可以在每次要求解决服务的容器中注册将要执行的功能

var builder = new ContainerBuilder();
builder
    .Register<Func<bool, IRunHistoryRepository>>(
        x =>
        {
            var context = x.Resolve<IComponentContext>();
            return isUserInRole => 
            {
                return isUserInRole
                    ? context.Resolve<RunHistoryRepository>()
                    : context.Resolve<DifferentRunHistoryRepository>();
            }
        }
)};

You can also use Named and Keyed Services to resolve implementations via an enum or string. 您还可以使用命名服务和键控服务通过枚举或字符串解析实现。

To throw out a curveball, I've recently been registering components ' on the fly ' within a nested lifetime scope. 扔了一个弧线球,我最近一直在“注册组件上飞 ”的嵌套一生范围之内。

// a component that uses IOwinContext as a dependency
class CommandHandler
{
    // this IOwinContext will be a different instance for each HttpRequest
    readonly IOwinContext _context;

    public CommandHandler(IOwinContext context)
    {
        _context = context;
    }
}

// capture our HttpRequest via the IOwinContext object
var context = ...

// create a nested scope
using (var scope = Container.BeginLifetimeScope(builder =>
{
    // register the Owin context for this specific HttpRequest
    builder.RegisterInstance(context).As<IOwinContext>();
}))
{
    // this component will be resolved with the specific IOwinContext
    // that we registered above
    var handler = scope.Resolve<CommandHandler>();
}

You can register your service with a named service and register a factory using a delegate that will resolved the named service based on your condition : 您可以使用命名服务注册您的服务,并使用将根据您的情况解析命名服务的委托来注册工厂

builder.RegisterType(typeof(RunHistoryRepository))
       .Named<IRunHistoryRepository>("standard");
builder.RegisterType(typeof(DifferentRunHistoryRepository))
       .Named<IRunHistoryRepository>("different");

builder.Register(c => {
   if(UserIsInThisRole()) {
       return c.ResolveNamed<IRunHistoryRepository>("different"); 
   } else {
       return c.ResolveNamed<IRunHistoryRepository>("standard"); 
   }
})
.As<IRunHistoryRepository>(); 

By the way, I recommend you not to have if(UserIsInThisRole()) in the delegate but rely on another service that will do the check for you. 顺便说一句,我建议您不要在委托中包含if(UserIsInThisRole()) ,而是依靠另一个为您进行检查的服务。

if(c.Resolve<IRoleManager>().IsInRole("xxx")) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM