使用 Autofac 在不指定具体 class 的情况下解析多租户应用程序中的接口

Resolve an Interface in a multitenant app without specify concrete class with Autofac

我有一个多租户应用程序。我必须用 Autofac 解析一个类型。我能做到……其实并不难。这是我的做法:

第一个解决方案

var tenantIdentifier = new TenantIdentificationStrategy();
var mtc = new MultitenantContainer(tenantIdentifier, container);
mtc.ConfigureTenant("1", t => 
{
        t.RegisterType<MultiTenant.ModuleModule1>()
        .As<MultiTenant.IModule>();

        t.RegisterType<MultiTenant.Controllers.Tenant.Tenant1Controller>()
        .As<MultiTenant.Controllers.CommonController>()
        .PropertiesAutowired();
});

mtc.ConfigureTenant("2", t => 
{
        t.RegisterType<MultiTenant.ModuleModule2>()
        .As<MultiTenant.IModule>();

        t.RegisterType<MultiTenant.Controllers.Tenant.Tenant2Controller>()
        .As<MultiTenant.Controllers.CommonController>()
        .PropertiesAutowired();
});

嗯...它完美地工作...它按我预期的那样工作 但我不喜欢我必须添加具体的 class 才能注册。这意味着我必须在项目中添加一个引用...并且,例如,如果我有 10 个不同的租户,我必须在我的项目中添加 10 个引用。 但是我不太喜欢这种情况。

第二种解决方案

我试过这样解决:

mtc.ConfigureTenant("1", t => 
{
    t.RegisterType<MultiTenant.Controllers.Tenant.Tenant1Controller>()
     .As<MultiTenant.Controllers.CommonController>()
     .PropertiesAutowired();
});

mtc.ConfigureTenant("2", t => 
{
    t.RegisterType<MultiTenant.Controllers.Tenant.Tenant2Controller>()
     .As<MultiTenant.Controllers.CommonController>()
     .PropertiesAutowired();
});

并且在网络配置中:

<autofac>
   <components>
      <component type="MultiTenant.Module1, MultiTenant.Module" service="MultiTenant.IModule, MultiTenant.IModule"></component>
      <component type="MultiTenant.Module2, MultiTenant.Module" service="MultiTenant.IModule, MultiTenant.IModule"></component>
   </components>
</autofac>

这个解决方案 "works" 但我没有得到预期的结果,因为 Module1 和 Module2 都为两个租户解决了。

我该如何解决这个问题?

您可以使用自定义属性标记程序集

[assembly: TenantAttribute("tenant1")]

然后列出所有这些并在 ConfigureTenant 中使用 RegisterAssemblyModules 方法。

如果您的应用程序 运行 在 IIS 下,我建议使用 BuildManager 列出您的程序集(请参阅 Why aren’t my assemblies getting scanned after IIS restart? 了解说明)。

你可以这样做:

IEnumerable<Assembly> referencedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

if (HostingEnvironment.InClientBuildManager)
{
    referencedAssemblies = referencedAssemblies
                                 .Union(BuildManager
                                            .GetReferencedAssemblies()
                                            .Cast<Assembly>())
                                 .Distinct();
}

var groupedAssemblies = referencedAssemblies.Select(ass => new
{
    Assembly = ass,
    TenantAttribute = ass.GetCustomAttribute<TenantAttribute>()
})
.Where(o => o.TenantAttribute != null)
.GroupBy(o => o.TenantAttribute.TenantId, o => o.Assembly);

foreach (var group in groupedAssemblies)
{
    mtc.ConfigureTenant(group.Key, cb =>
    {
        cb.RegisterAssemblyModule(group);
    });
}