zcz527 / autofac

Automatically exported from code.google.com/p/autofac
Other
0 stars 0 forks source link

Func and Lazy doesn't seem to mix #406

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
The following code will throw on the x("test") even though a string is 
provided. Autofac fails to recognize that it has a string to fulfill the 
Lazy<string> requirement.

void Main()
{
    var builder = new ContainerBuilder();
    builder.RegisterType<TestClass>().AsSelf();
    var container = builder.Build();

    var x = container.Resolve<Func<string, IEnumerable<TestClass>>>();
    x("test").Dump();
}

// Define other methods and classes here
class TestClass
{
    public TestClass(Lazy<string> abc)
    {
    }
}

Original issue reported on code.google.com by byte...@bytenik.com on 18 Feb 2013 at 8:45

GoogleCodeExporter commented 8 years ago

Original comment by alex.meyergleaves on 19 Feb 2013 at 3:34

GoogleCodeExporter commented 8 years ago

Original comment by travis.illig on 19 Feb 2013 at 4:27

GoogleCodeExporter commented 8 years ago
Is there an expected ETA for this fix?

Original comment by byte...@bytenik.com on 9 Apr 2013 at 9:07

GoogleCodeExporter commented 8 years ago
We get to things as fast as we can but, unfortunately, given no one is a 
dedicated full-time engineer on the project, we can't actually promise any 
particular delivery date. If you happen to have a fix you could submit through 
a pull request or patch, we'd be able to integrate that much faster.

Original comment by travis.illig on 10 Apr 2013 at 2:41

GoogleCodeExporter commented 8 years ago
I unfortunately don't have time to solve this problem on my own, but my company 
may be willing to pay for a resolution. Is that something you would be 
interested in?

Original comment by byte...@bytenik.com on 27 Apr 2013 at 12:18

GoogleCodeExporter commented 8 years ago
Looking at the code above I don't see how this can work. The parameter for the 
function being resolved is of type string which is not convertible to 
Lazy<string>.

You will need to resolve the Func typed with a Lazy<string> parameter and 
provide an  instance when invoking the function.

e.g.

var func = container.Resolve<Func<Lazy<string>, TestClass>>();
var value = func(new Lazy<string>(() => "test")).Value;

Original comment by alex.meyergleaves on 28 Apr 2013 at 11:50

GoogleCodeExporter commented 8 years ago
Autofac routinely builds a Lazy<T> from T, delaying the resolution until the 
Lazy's .Value is called instead of at construction time.

I need this because the string in this scenario may or may not be registered. I 
can't catch the dependency resolution exception and handle with my own code in 
the TestClass constructor without it.

Note that container.Resolve<TestClass>() would work fine here with the code as 
written, and Autofac would supply the Lazy. Only when introducing the function, 
so that I'm doing container.Resolve<Func<string, TestClass>>() does it stop 
producing the Lazy for delayed resolution.

Original comment by byte...@bytenik.com on 28 Apr 2013 at 12:53

GoogleCodeExporter commented 8 years ago
Autofac is only able to provide the Lazy<T> instance during a resolve operation 
when it is in charge of providing the dependencies. It can see that a parameter 
is of type Lazy<T> and check if T is registered. In the case of a Func it has 
to use the provided signature to create a delegate that will take the user 
provided arguments and pass them into the constructor of the return type. 
Because the signature is Func<string, TestClass> it knows nothing about Lazy<T>.

If you haven't registered string with the container Autofac will not attempt to 
provide a Lazy<string> instance. You probably should not have dependencies that 
are only registered some of the time but adding a second constructor might 
solve the problem for you. It would be cleaner than trying to catch the 
exception.

// The sample class.

class TestClass
{
    public string Value { get; private set; }

    // Called when string is not registered.
    public TestClass()
    {
    }

    // Called when string is registered.
    public TestClass(string value)
    {
        Value = value;
    }
}

// Test without string registered.

var builder = new ContainerBuilder();
builder.RegisterType<TestClass>();
var container = builder.Build();

var value = container.Resolve<TestClass>().Value;

Assert.That(value, Is.Null);

// Test with string registered.

var builder = new ContainerBuilder();
builder.RegisterType<TestClass>();
builder.RegisterInstance("Test");
var container = builder.Build();

var value = container.Resolve<TestClass>().Value;

Assert.That(value, Is.EqualTo("Test"));

Original comment by alex.meyergleaves on 28 Apr 2013 at 1:37