简体   繁体   中英

Using Autofac Implemented with IDependencyResolver in MVC5

I've been having a go at this for a while no luck :(

Where I'm getting stuck:

I have an MVC5 web application and I'm using Autofac for the IoC container under the hood. Basically I have a dependency that requires me to pass in a parameter through the constructor but since I'm resolving Autofac through the IDependencyResolver interface I'm not allowed to insert parameters directly.

I've tried to use a delegate factory but im still getting errors I'm sure I must be doing something wrong....

////////////////
//My Interface//
////////////////

public interface IConfiguration
{
    string OwnerId { get; set; }
    IDictionary<string, string> GetAllSettings();
    string GetSettingByKey(string SettingsKey);
}

///////////////////
//My Concrete Obj//
///////////////////

public class Configuration : IConfiguration
{
    public delegate Configuration Factory(string ownerId); //Added this based on what's on the Autofac Wiki - Delegate Factories

    public string OwnerId { get; set; }
    public Dictionary<string, string> AllSettings {get; set;}

    public Configuration(string ownerId) {
        //Some logic that requires ownerId
    }

    public IDictionary<string, string> GetAllSettings()
    {
        //Some other logic irrelevant for now
    }

    public string GetSettingByKey(string settingsKey)
    {
        //Some other logic irrelevant for now
    }
}

///////////////////////////////////
//Registering Dependencies in MVC//
///////////////////////////////////

var builder = new ContainerBuilder();
builder.RegisterType<Configuration>().As<IConfiguration>();

//Some other configuration

var container = builder.Build();

//Set Dependency Resolver for MVC5
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

//////////////////////////////////////////////////////////
//Somewhere in my MVC App where I require IConfiguration//
//////////////////////////////////////////////////////////

var ownerId = GetOwnerId();

//Where I require Help

var config = DependencyResolver.Current.GetService<IConfiguration>(); //How do I pass in a parameter (ownerId) to my underlying autofac container?

//I Tried this but it didn't work 
var configFactory = DependencyResolver.Current.GetService<Func<string,IConfiguration>>(); 
var config = configFactory.Invoke(ownerId);

The YSOD I'm getting goes about like this

None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Common.Configuration' can be invoked with the available services and parameters: Cannot resolve parameter 'System.String ownerId' of constructor 'Void .ctor(System.String)'.

Thanks in advance guys :)

I think you probably saw this link: DependencyResolver: Pass parameters

One suggestion that may resolve your problem in this specific case, is do the cast back to AutofacDependencyResolver and call the native method from ApplicationContainer, something like this (I don't know if the syntax is correct):

var rawContainer = (AutofacDependencyResolver)DependencyResolver.Current;
        rawContainer.ApplicationContainer.Resolve<IConfiguration>(new[] { ownerId });

I don't have acess to the code right now, but I've seen in the company that I work for an Extension Method to IDependencyResolver to wrap this code above, something like this:

  public static class AutofacHelper
{
    public static T Resolve<T>(this IDependencyResolver container, Parameter[] paramsCtor = null)
    {
        var rawContainer = (AutofacDependencyResolver) DependencyResolver.Current;
        return rawContainer.ApplicationContainer.Resolve<T>((IEnumerable<Parameter>) paramsCtor);
    }
}

and then you can call it normally through DependencyResolver.Current

The troubles are caused because you mix runtime data with behavior. You should not inject runtime data (your ownerId is runtime data) into your application's components. Your application components should only depend on other components. Mixing this all sorts of troubles. For instance, it makes verifying the object graph much harder.

So instead of injecting runtime data in the constructor, inject a service that allows retrieving that runtime value after the object graph is built. In your case I could imagine the existence of an IOwnerContext abstraction:

public interface IOwnerContext
{
    string CurrentOwnerId { get; }
}

How this owner id is retrieved will depend on the application you are running. For instance, you could imagine having a background service that runs on top of your business logic, that has an IOwnerContext implementation that returns the value based on some message that is being processed, or based on some configuration value.

In the case of a web application, you will probably retrieve that data from the current request and this logic should be placed in this application-specific implementation:

public class AspNetOwnerContext : IOwnerContext
{
    public string CurrentOwnerId
    {
        get
        {
            string host = HttpContext.Current.Request.Url.Host;
            int index = host.IndexOf(".");
            string subdomain = host.Substring(0, index);
            return subdomain;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM