0

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 :)

5
  • Do I need to place a bounty on this guys :) ? Commented Oct 9, 2014 at 3:53
  • What is ownerId? Is that something that changes based on runtime conditions (such as the incoming request or the user session) or is it something that is fixed for the duration of the lifetime your application (for instance because it is configured in the configuration file)? Commented Oct 9, 2014 at 6:29
  • Hi Steven thanks for the question yes its something that is determined during runtime conditions :) i cant place it in the config as its not yet known... Commented Oct 9, 2014 at 19:34
  • And how do you determine its value? Commented Oct 9, 2014 at 20:35
  • I determine it by the subdomain the current user is using. Sorry bro but is it really relevant? very curious indeed... good stuff :D Commented Oct 9, 2014 at 22:22

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

4 Comments

You can use AutofacDependencyResolver.Current to avoid the casting.
Thanks guys ill give it a crack, true that i never thought of casting it back / or unsing what Travis sugested... Ill give it a shot and let u guys know cheers...
Yeah thats actually the post i saw when i was trying to come up with the solution... @Peter Lillevold might still be able to help me out?
Thanks guys this worked... the factory delegate was also working I just realized that the error was in another dependency being resolved... learned good ways to solve this though... question for you now @japonex should we go around the DependencyRevolver by casting it back to the AutofacDependency resolver? are we breaking the law here? or is this acceptable? what are your opinions specially if you guys had to do that extension method (which i admire you for remembering it from the top of ur head) thanks guys...
1

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

2 Comments

Hehe I actually have that interface I just decided not to put it in (i cut the code by placing that var ownerId = GetOwerId(); ) because I thought I would be bloating the example (for the actual problem I was trying to solve) its good that you mentioned this though makes me feel like taking the right path my Interface is called IConfigOwner and it has only one purpose to GetOwnerId() :) I do like how you mentioned not mixing runtime data with behaviours. I'm doing exactly what you mentioned; the concrete class implementing that interface... any ideas on my particular problem?
@EgliBecerra: You should inject the IConfigOwner into the Configuration class and that should solve your problems.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.