需要有关Autofac自定义生命周期范围与多租户的指南 [英] Need guidance on Autofac custom lifetimescopes vs. multi tenancy

查看:80
本文介绍了需要有关Autofac自定义生命周期范围与多租户的指南的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

场景:

Scenario:

我需要为 same Web应用程序(appdomain)中的 same 接口定义提供不同的接口实现,但要为不同的 范围" .

I need to provide different interface implementations to the same interface definitions within the same web application (appdomain) but to different "scopes".

想象一下这样一个简单的分层Web内容结构(如果您不熟悉SharePoint):

Imagine a simple hierarchical web content structure like this (if you are not familiar with SharePoint):

RootWeb (SPSite) (ctx here)
  |______SubWeb1 (SPWeb) (ctx here)
  |______SubWeb2 (SPWeb)
  |______SubWeb3 (SPWeb)
           |_______SubWeb3.1 (SPWeb) (ctx here)
           |_______SubWeb3.2 (SPWeb)

RootWeb,SubWeb1和SubWeb3.1提供上下文.也就是说,我实现了一个AppIsolatedContext类,该类特定于某个层次结构级别.如果某个级别不提供上下文,则它从父节点继承该上下文,依此类推.例如,SubWeb3将从RootWeb继承其上下文.但是,SubWeb3.1提供了自己的隔离上下文.

RootWeb, SubWeb1 und SubWeb3.1 provide Contexts. That is I implemented an AppIsolatedContext class that is specific for a certain hierarchy level. If a level does not provide a context it inherits the context from the parent node and so on. For example SubWeb3 would inherit its context from RootWeb. SubWeb3.1 however provides its own isolated context.

隔离的上下文仅仅是静态的ConcurrentDictionary.

The isolated context is merely a static ConcurrentDictionary.

到目前为止很好.现在关于Autofac(我是Autofac和任何其他DI容器的新手,虽然不是IoC的原理)...我不确定如何正确设置它以正确处理对象.实际上,这不应该是什么大问题,因为对象(一旦创建)就应该一直存在,直到appdomain回收(将它们视为每个隔离的上下文单例")为止.

Okay so far so good. Now regarding Autofac (I'm new to Autofac and any other DI container - not to the principle of IoC though)... I'm not sure how I correctly set it up to dispose of objects correctly. Actually it shouldn't be that much of an issue because the objects are (once they are created) are supposed to live until the appdomain gets recycled (think of them as a "per isolated context singleton").

我倾向于做这样的事情:

I'd be inclined to do something like that:

// For completeness.. a dummy page which creates a "dummy" context
public partial class _Default : Page
{
    private static AppIsolatedContext _dummyContainer = new AppIsolatedContext();

    public _Default()
    {
        _dummyContainer.ExceptionHandler.Execute("Test Message");            
    }
}

// The isolated context which holds all the "context" specific objects
public class AppIsolatedContext
{
    public static IContainer Container { get; set; }

    public IExceptionHandler ExceptionHandler { get; set; }
    //public ISomething Something { get; set; }
    //public ISomethingElse SomethingElse { get; set; }

    public AppIsolatedContext()
    {
        // set up autofac
        // Create your builder.
        ContainerBuilder builder = new ContainerBuilder();

        // Usually you're only interested in exposing the type
        // via its interface:
        builder.RegisterType<MailNotificationHandler>().As<INotificationHandler>();
        builder.RegisterType<ExceptionHandler>().As<IExceptionHandler>();

        Container = builder.Build();

        using (ILifetimeScope scope = Container.BeginLifetimeScope())
        {
            ExceptionHandler = scope.Resolve<IExceptionHandler>();
            //Something = scope.Resolve<ISomething>();
            //SomethingElse = scope.Resolve<ISomethingElse>();
        }
    }
}

当然,我的应用程序不限于这些上下文单例"实例.我也将有每个请求的生命周期实例..但这就是ASP.NET集成模块对吗?我希望它们也可以无缝集成到SharePoint(2013)中:)

Of course my application is not limited to these "context singleton" instances. I will have per request lifetime instances too.. but that's what the ASP.NET integration modules are there for right? I hope they can seamlessly be integrated in SharePoint (2013) too :)

所以我的问题是 我提出的建议还好吗?还是需要弄脏我的手?如果是这样,那么某些方向将会是惊人的 ...

So my question is is it okay what I proposed or do I need to get my hands dirty? If so some direction would be phenomenal...

浏览Autofac的文档时,我偶然发现了其多租户功能. 我相信这也可能符合我的目的.有人可以确认吗?

Digging through Autofac's documentation I stumbled across its multi tenancy capability. I believe this might suit my purpose as well.. can anyone confirm this?

using System;
using System.Web;
using Autofac.Extras.Multitenant;

namespace DemoNamespace
{
    public class RequestParameterStrategy : ITenantIdentificationStrategy
    {
        public bool TryIdentifyTenant(out object tenantId)
        {
            tenantId = AppIsolatedContext.Current.Id; // not implemented in the dummy class above, but present in the real thing.
            return !string.IsNullOrWhiteSpace(tenantId);
        }
    }
}

如果没有任何晶体-请不要犹豫告诉我:)

If anything is not crystal - please don't hesitate to tell me :)

推荐答案

免责声明:这是一个相当重要的问题,鉴于这个问题以及我对SharePoint 2013的不熟悉,我会尽力而为.答案,但您需要根据自己的需要对答案进行调整.

我将使用命名的生存期作用域来构造它.与其使用自己的容器创建上下文,不如使用命名作用域的层次结构.这就是多租户支持的工作方式;这也是ASP.NET每个Web请求支持的工作方式.

The way I would structure this is with named lifetime scopes. Rather than contexts with their own containers, use a hierarchy of named scopes. This is how the multitenant support works; it's also how ASP.NET per-web-request support works.

您首先需要阅读有关实例范围的Autofac Wiki页面以及有关Autofac寿命的入门.这些都不是小文章,但是都有重要的概念需要理解.如果您了解生命周期范围,那么我在这里解释的一些内容才有意义.

You will first want to read the Autofac wiki page on instance scopes as well as this primer on Autofac lifetimes. Neither of these are small articles but both have important concepts to understand. Some of what I explain here will only make sense if you understand lifetime scope.

生命周期作用域是可嵌套的,这是您共享单例或按Web实例实例的方式.在应用程序的根部是一个包含所有注册内容的容器,您可以从中生成作用域.

Lifetime scopes are nestable, which is how you share singletons or instance-per-web-request sorts of things. At the root of the application is a container with all of your registrations, and you spawn scopes from that.

  • 容器
    • 子范围
      • 具有子范围的子项
      • Container
        • Child scope
          • Child of child scope

          在更多与代码相关的格式中,它是这样的:

          In a more code related format, it's like this:

          var builder = new ContainerBuilder();
          var container = builder.Build();
          using(var child = container.BeginLifetimeScope())
          {
            using(var childOfChild = child.BeginLifetimeScope())
            {
            }
          }
          

          您实际上是在范围之外解析组件-容器本身就是一个范围.

          You actually resolve components out of scopes - the container itself is a scope.

          关于生存期范围的关键事项:

          Key things about lifetime scopes:

          • 您可以命名它们,从而使您在命名范围内拥有单子".
          • 您可以在呼叫BeginLifetimeScope的过程中即时注册事物.
          • You can name them, allowing you to have "singletons" within a named scope.
          • You can register things on the fly during the call to BeginLifetimeScope.

          这是Autofac的多租户支持的工作方式.每个租户都有自己的命名生存期范围.

          This is how the multitenant support for Autofac works. Each tenant gets its own named lifetime scope.

          不幸的是,多租户支持是一级的:应用程序容器生成租户特定的根"作用域,仅此而已.您拥有这些上下文的站点层次结构具有多个层次,因此多租户支持将无法正常工作.但是,您可以潜在地查看该源代码中的想法.

          Unfortunately, the multitenant support is one-level: Application container spawns tenant-specific "root" scopes, but that's it. Your site hierarchy where you have these contexts has more than one level, so the multitenant support isn't going to work. You can, however, potentially look at that source code for ideas.

          我要做的是在每个级别上命名作用域.每个站点都将通过一个ILifetimeScope,它可以从中解决问题.在代码中,它看起来像:

          What I'd be doing is naming scopes at each level. Each site would get passed an ILifetimeScope from which it can resolve things. In code, it'll look a little like:

          var builder = new ContainerBuilder();
          // RootWeb will use the container directly and build its per-web-request
          // scope from it.
          var container = builder.Build();
          
          // Each sub web will get its own scope...
          using(var sw1Scope = container.BeginLifetimeScope("SubWeb"))
          {
            // Each child of the sub web will get a scope...
            using(var sw11Scope = sw1Scope.BeginLifetimeScope("SubWeb"))
            {
            }
            using(var sw12Scope = sw1Scope.BeginLifetimeScope("SubWeb"))
            {
            }
          }
          

          请注意,我将子网站范围的每个级别标记为"SubWeb",这将使您在容器级别和子网站级别的注册中都具有每个子网站实例"类型的注册.

          Note I'm tagging each level of sub-web scope as "SubWeb" - that will allow you to have "instance per sub web" sort of registrations in both container-level and sub-web-level registrations.

          // Register a "singleton" per sub-web:
          builder.RegisterType<Foo>()
                 .As<IFoo>()
                 .InstancePerMatchingLifetimeScope("SubWeb");
          

          现在,很明显,这是一个概念性的东西-您实际上无法使用这样的语句来包装所有内容.您需要对创建和处置进行不同的管理,因为创建将在与处置不同的地方进行.

          Now, obviously, that's a conceptual thing there - you won't actually be able to wrap everything in using statements like that. You'll need to manage your creation and disposal differently because creation will happen in a different place than disposal.

          您可以同时查看ASP.NET和多租户源,以获取有关如何执行此操作的想法.通用算法为:

          You can look at both the ASP.NET and multitenant source to get ideas on how to do that. The general algorithm will be:

          • 在应用程序启动时,构建根容器.
          • 在子网站启动时,产生一个为子网站命名的嵌套生命周期范围.
          • 如果子站点需要注册特定组件,请在调用BeginLifetimeScope
          • 时进行操作
          • 如果在每个子网站级别都需要上下文",则可以将为该子网站创建的范围传递给它,而不是创建一个单独的容器.
          • At application startup, build the root container.
          • As sub webs start up, spawn a nested lifetime scope named for the sub web.
          • If a sub web needs a specific component registered, do that during the call to BeginLifetimeScope
          • If you need the "context" at each sub web level, you'd pass it the scope created for that sub web rather than creating a whole separate container.

          现在,您可以采取另一步骤,将子Web ID的根级字典保留在范围内,这样就根本不需要每个级别的上下文"对象.它更像是一种DependencyResolver.Current.GetService<T>模式.如果您查看Autofac多租户支持中的MultitenantContainer的工作方式,您会看到类似的租户ID范围字典.

          Now, you could take it another step by keeping a root-level dictionary of sub web ID to scope so that you'd not need per-level "context" objects at all. It'd be more like a DependencyResolver.Current.GetService<T> kind of pattern. If you look at how the MultitenantContainer in the Autofac multitenant support works, you'll see a similar sort of tenant-ID-to-scope dictionary.

          事实上,多租户支持将是一个很好的模式,特别是如果您还希望具有按Web请求的作用域时. Autofac ASP.NET支持要求您传入父ILifetimeScope,将从其生成子Web请求生存期范围.多租户支持在其中添加了一些动态方面,因此当ASP.NET支持调用BeginLifetimeScope时,事物的多租户部分会自动确定(通过租户标识)哪个租户应该是当前请求的父级.您可以对子站点的层次结构执行相同的操作.但是,再次,多租户支持是一个平面结构,而您的子网站是一个层次结构,因此多租户支持将不起作用 .

          In fact, that multitenant support will be a good pattern to look at, especially if you also want to have per-web-request scopes. The Autofac ASP.NET support requires you pass in a parent ILifetimeScope from which child web request lifetime scopes will be spawned. The multitenant support adds some dynamic aspect in there so when the ASP.NET support calls BeginLifetimeScope the multitenant portion of things automatically figures out (through tenant identification) which tenant should be the parent of the current request. You could do the same thing with your hierarchy of sub-webs. However, again, the multitenant support is a flat structure while your sub webs are a hierarchy, so the multitenant support won't just work.

          这很长的话可以说您在这里有一个有趣的用例,但是您会变得很脏.

          This is all a long way of saying you have an interesting use case here, but you're going to be getting your hands pretty dirty.

          这篇关于需要有关Autofac自定义生命周期范围与多租户的指南的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆