我正在使用Autoface创建一个新的MVC4站点,该站点有一个公共消费者站点以及一个用于管理面向消费者站点的管理区域。管理站点将位于不同的区域,使用与面向消费者的站点相同的服务,但将不具有一些自定义品牌功能。
我遵循了其他地方给出的建议,有一个ViewDataFactory,它提供了一组共享数据供视图使用。我的目标是根据你所在的区域提供不同的ViewDataFactory。
例如,这里是实现IViewDataFactory的Service
builder.RegisterType<SelfServiceViewDataFactory>().As<IViewDataFactory>();
这给了我一个ViewFactory,它被注入到我所有的控制器。然而,我想要实现的是这样的东西(不是功能代码):
builder.RegisterType<ViewDataFactory>().As<IViewDataFactory>().ForType(ControllerBase1);
builder.RegisterType<DifferentViewDataFactory>().As<IViewDataFactory>().ForType(ControllerBase2);
控制器类型或MVC区域将决定解析哪个服务。
编辑
为了澄清我的帖子有两个问题:
- Autofac中是否有一种方式来说明"仅对于X类型的类,Y类型的服务将由实例Z提供"?
- 是否有一种方法可以根据组件正在使用的区域改变autofacc行为?
从我一直在阅读的所有内容中,对#1的答案似乎是"否",除非你有一个参数来检查要提供哪个组件。我知道Ninject可以提供基于名称空间的依赖,所以其他框架似乎可以处理这种情况。似乎解决方案是要么提供一个参数,要么定义两个不同的服务。
我还没有看到太多关于Autofac和MVC领域的讨论,所以我猜如果没有自定义解决方案,#2也不可能实现。谢谢!
使用命名服务可能是最好的选择。所以你可以这样做:
builder
.RegisterType<ViewDataFactory>()
.Named<IViewDataFactory>("Area1");
builder
.RegisterType<DifferentViewDataFactory>()
.As<IViewDataFactory>("Area2");
如果你想避免手动注册你的控制器。您可以使用我刚刚拼凑的尚未测试的代码:
把这个属性放到全局可访问的地方:
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class ServiceNamedAttribute : Attribute
{
private readonly string _key;
public ServiceNamedAttribute(string key)
{
_key = key;
}
public string Key { get { return _key; } }
}
将此模块添加到Autofac配置:
public class ServiceNamedModule : Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing +=
(sender, args) =>
{
if (!(args.Component.Activator is ReflectionActivator))
return;
var namedParameter = new ResolvedParameter(
(p, c) => GetCustomAttribute<ServiceNamedAttribute>(p) != null,
(p, c) => c.ResolveNamed(GetCustomAttribute<ServiceNamedAttribute>(p).Name, p.ParameterType));
args.Parameters = args.Parameters.Union(new[] { namedParameter });
};
}
private static T GetCustomAttribute<T>(ParameterInfo parameter) where T : Attribute
{
return parameter.GetCustomAttributes(typeof(T), false).Cast<T>().SingleOrDefault();
}
}
然后你仍然可以自动注册你的控制器通过这样装饰构造函数:
public class Controller1
{
public Controller1(ServiceNamed["Area1"] IViewDataFactory factory)
{ ... }
}