Deferred Resolution/Automatic Factories with parameters

Jul 22, 2010 at 9:36 PM
Edited Jul 23, 2010 at 3:36 PM

Deferred Resolution works great with no-arg constructors. Is there a way to get it to work if the calling class needs to pass arguments (not dependencies) to the constructor?

For instance, the following will work if the ProcessModel constructor takes a parameter of type Func<IFoo>. If it's changed as follows, it fails with:

Resolution of the dependency failed, type = "UserQuery+ProcessModel", name = "(none)".
Exception occurred while: while resolving.
Exception is: VerificationException - Operation could destabilize the runtime.

void Main()
{
    IUnityContainer container = new UnityContainer();
    
    container.RegisterType<IFoo, Foo>();
    
    ProcessModel processModel = container.Resolve<ProcessModel>();
    
    processModel.CallDoIt();
    processModel.CallDoIt();
}

public class ProcessModel
{
    public ProcessModel(Func<string, IFoo> fooFactory)
    {
        Console.WriteLine ("Creating ProcessModel");
        _fooFactory = fooFactory;
    }
    
    public void CallDoIt()
    {
        IFoo foo = _fooFactory("Lemon Curry");
        foo.DoIt();
    }
    
    private Func<string, IFoo> _fooFactory;
}

public interface IFoo
{
    void DoIt();
}

public class Foo : IFoo
{
    public Foo(string name)
    {
        Console.WriteLine ("Creating Foo");
        _name = name;
    }

    public void DoIt()
    {
        Console.WriteLine ("In Foo " + _name);
    }
    
    private string _name;
}

[reformatted]

Jul 23, 2010 at 3:38 AM

[Don't worry about the formatting - Codeplex switched over to one of the web rich text editors a while back, but unfortunately sometime it doesn't render the editor, and you're stuck with a plain text box. Turns out you can just enter HTML.]

At this time, automatic factories don't support arguments. There's just too many ambiguities over what the signature needs to look like. For example, what if you want to create an object through the container that takes four parameters, but you want every other one to come out of the container? An option would be to support the dependency overload objects you can pass to the Resolve method; I didn't do that because then we're coupled back to the Unity container code again, which I was trying to avoid in the first place.

My recommendation for now is, if you need a factory method that doesn't match the signature you want, simply configure the container to supply it explicitly. Either that or create an explicit factory class and resolve that.

Your case would end up looking like this:

IUnityContainer container = new UnityContainer();

container.RegisterInstance<Func<string, IFoo>>(
(string name) => new Foo(name));

once you do that, you can then resolve your ProcessModel and you'll get the delegate you want. If you actually had dependencies, you would use a closure to capture the container and get them that way.

 

Jul 23, 2010 at 3:42 PM

Thanks Chris! That works great!

I completely agree with minimizing coupling to Unity.

Thanks also for the suggestions on formatting. I fixed my post based on your input. It appears that CodePlex does not support Firefox properly. When I switched to Internet Explorer, I got the editor.