Autofac Dependencies Per Area

蹲街弑〆低调 提交于 2019-12-23 02:52:33

问题


I'm creating a new MVC4 site using Autoface that has a public consumer site as well as an admin area for managing the consumer facing site. The admin site will be located in a different area be using the same services as the consumer facing site, but will not having some of the custom branding features.

I've followed the advice given elsewhere of having a ViewDataFactory which provides a set of shared data for the view to use. My goal is to provide a different ViewDataFactory depending on what Area you are in.

So for example, here is the Service that implements IViewDataFactory

builder.RegisterType<SelfServiceViewDataFactory>().As<IViewDataFactory>();

This gives me one ViewFactory which is injected into all my controllers. However what I'm trying to acheive is something like this (not functional code):

builder.RegisterType<ViewDataFactory>().As<IViewDataFactory>().ForType(ControllerBase1);    
builder.RegisterType<DifferentViewDataFactory>().As<IViewDataFactory>().ForType(ControllerBase2);    

Where the controller type or the MVC area would determine which service is resolved.

EDIT

To clarify my post has two questions:

  1. Is there a way in Autofac to say "only for classes of type X, a service of type Y will be provided by instance Z" ?
  2. Is there a way to change the Autofac behavior based on the Area the component is being used in?

From everything I've been reading the answer to #1 seems to be "no" unless you have a parameter to use to check which component to supply. I know Ninject can supply a dependency based on namespace so other frameworks seems to handle this case. Seems the solution is to either supply a parameter or have two different services defined.

I haven't really seen much discussion of Autofac and MVC areas so I'm guessing #2 is also not possible without a custom solution. Thanks!


回答1:


Using named services is probably your best option. So you'd do something like:

builder
    .RegisterType<ViewDataFactory>()
    .Named<IViewDataFactory>("Area1");    

builder
    .RegisterType<DifferentViewDataFactory>()
    .As<IViewDataFactory>("Area2");

And then if you want to avoid having to then manually register your controllers. You could use this code that I just cobbled together and haven't tested:

Put this attribute somewhere globally accessible:

[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; } }
}

Add this module to your Autofac config:

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();
    }
}

And then you can still auto-register your controllers by decorating the constructor like so:

public class Controller1
{
    public Controller1(ServiceNamed["Area1"] IViewDataFactory factory)
    { ... }
}


来源:https://stackoverflow.com/questions/14075427/autofac-dependencies-per-area

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!