1

我有一个 MVC 应用程序分为几个区域:

+ Root
+ -- Areas
   --+ US
     + -- USCodeController : XXXCodeBaseController
   --+ UK
       -- UKCodeController : XXXCodeBaseController
   --+ (Other countries pending)
+ -- Controllers
   --+ XXXCodeBaseController

因此,我在文件夹中定义了一些基本功能,Controllers然后在区域文件夹中继承和扩展了这些功能。通过这种方式,可以围绕特定于每个国家/地区、工作流程等的 UI 组件自定义 UI。

在使用 Autofac 时,我要做的就是将IComponentContextup 传递给构造函数参数,这不是很好。问题包括:

  1. 当使用真正的 IoC 时,我需要注入 IXXXServices,所以我得到了一个特定于区域的类而不是 AutofacIComponentContext
  2. 每个区域都与不同的数据库关联,但共享模式 - 因此使用相同的 EF 库,仅使用不同的连接字符串。

通过直接注入服务,设计肯定会更优雅——这就是我所追求的。但是解析器似乎不够灵活,无法在没有大规模破解的情况下解析控制器,这对于每个使用的控制器来说都变得相当大。以下是手动连接控制器的示例:

 builder.Register(s => new OrderService(r.ResolveNamed<IOrderRepository>("US"))
        .Named("US")
        .InstancePerHttpRequest();

 // Lets say we add a DeclinedOrderController

 public class DeclinedOrderControllerBase
 {
     public DeclinedOrderControllerBase ( IDeclinedOrderService service )
     { }
 }

为了使连接正常工作,您需要将注册添加到 Autofac,因为我们需要在DeclinedOrderService

     builder.Register(s => new DeclinedOrderController(r.ResolvedName<IDeclinedOrderService>("US")
        .Named("US")
        .InstancePerHttpRequest();

我的问题是(最后):

  • 有没有办法按区域动态选择一组映射的 Autofac 注册,这将自动解析为新的控制器?
4

2 回答 2

2

我想推荐LifetimeScope每个区域的自定义,但似乎有一个现有的解决方案。

看看这个: https ://code.google.com/p/autofac/wiki/MultitenantIntegration 。

于 2013-07-09T03:27:01.640 回答
0

在 MVC4 中,您可以创建自定义控制器工厂以在创建控制器实例时捕获它们。您可以将其用作单个合成点,而不必在每个控制器中都这样做。

通过继承来创建自定义控制器工厂System.Web.Mvc.DefaultControllerFactory。覆盖该GetControllerInstance方法并让基本函数为您创建控制器。现在你可以让你的依赖注入引擎组成控制器。

public class AreaControllerFactory : DefaultControllerFactory
{
    // We use a controller factory to hook into the MVC pipeline in order to get a reference to controllers
    // as they are being created.  We inherit from the default controller factory because we do not want to
    // have to locate the appropriate controllers ourself (we only want a reference to it).  Once we have a
    // reference, we simply hand the controller off for composition.

    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        IController controller = base.GetControllerInstance(requestContext, controllerType); // create the controller
        if (controller != null)
        {
            var container = ...; //construct your composition container
            container.ComposeParts(controller); //compose your controller
        }
        return controller;
    }
}

Application_StartGlobal.asax.cs. _

var controllerFactory = new AreaControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);

为了按区域过滤组合对象,我为每个区域构建了一个自定义的全局区域组件字典(例如区域名称字典、适用类型列表)。

// NOTE:ComposablePartCatalog is MEF specific, change as needed

internal static class AreaComponents
{
    /// <summary>
    /// A list of Area name, catalog pairs.
    /// Each Area can provide a custom list of components to import from.
    /// </summary>
    internal static readonly Dictionary<string, ComposablePartCatalog> AreaCompositionCatalogs =
        new Dictionary<string, ComposablePartCatalog>(); 
}

RegisterArea在每个区域的AreaRegistration实现方法中填充字典。

public class XAreaRegistration : AreaRegistration
{
    public override void RegisterArea(AreaRegistrationContext context)
    {
        /* ... Standard route logic ... */

        // Set up an MEF catalog with Area components
        var xCatalog = new AssemblyCatalog(
            typeof(MyPluginsNamespace.ArbitraryTypeToLookupAssembly).Assembly);

        AreaComponents.AreaCompositionCatalogs["X"] = xCatalog;
    }
}

在为给定控制器构建组合容器时,使用此字典在我的自定义控制器工厂中选择适当的组合对象子集。

// Capture current area name
System.Web.HttpContextBase contextBase = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current);
System.Web.Routing.RouteData routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(contextBase);
object areaObject = routeData.DataTokens["area"];
string areaName = areaObject as string ?? string.Empty;

// Create a composition container specific to this area
ComposablePartCatalog areaCatalog =
                    AreaMefComponents.AreaCompositionCatalogs.ContainsKey(areaName) ?
                    AreaMefComponents.AreaCompositionCatalogs[areaName] : null;
var container = new CompositionContainer(areaCatalog);
于 2015-03-10T15:23:44.747 回答