Reputation:
This involves autofac and c#. I have an interface derived from a parent interface:
public interface IJ4JLogger<out TCalling>
{
}
public interface IJ4JSmsLogger<out TCalling> : IJ4JLogger<TCalling>
{
}
Certain classes depend on being supplied an instance of the parent interface during construction:
public FileHistoryConfiguration( IJ4JLogger<FileHistoryConfiguration> histLogger, IJ4JLogger<FileHistoryService> svcLogger )
{
}
But if I register the type like this with autofac:
builder.RegisterGeneric( typeof(J4JSmsLogger<>) )
.As(typeof(IJ4JSmsLogger<>))
.SingleInstance();
where J4JSmsLogger<> is a class implementing IJ4JSmsLogger<>, then this call fails with an error that it can't find anything registered to provide an IJ4JLogger<> interface:
_fhConfig = _svcProvider.GetRequiredService<IFileHistoryConfiguration>();
I can work around the problem by changing the As<> clause in the registration of J4JSmsLogger<> to treat it as a IJ4JLogger<> instance, and then cast the result of resolving that interface to IJ4JSmsLogger<> whenever I need the extra capabilities of the child interface.
But I don't understand why I have to do that. Is there an additional step I need to take during registration of the types with autofac so that objects implementing the child interface will satisfy a need for the parent interface?
Cleaner Workaround
Reading more about autofac I learned something new: you can define as many As<>() clauses (including AsSelf()) as you want. So changing my autofac configuration to:
builder.RegisterGeneric( typeof(J4JSmsLogger<>) )
.As(typeof(IJ4JSmsLogger<>))
.As(typeof(IJ4JLogger<>))
.SingleInstance();
provides a cleaner solution than constantly casting resolved instances.
I'm not going to submit it as an answer, though, because I am curious why autofac doesn't do this kind of downcasting automatically, and whether any other DI frameworks do.
Upvotes: 0
Views: 405
Reputation: 23924
Autofac won't cast to base types for you like that. It generally assumes wiring is exact. You could run into some real problems if it didn't, like if someone has a constructor like...
public class BadTimes
{
public BadTimes(object input) { }
}
Which object does it put in there? Everything casts down to object.
However, you could always register it as both types and call it a day:
builder.RegisterGeneric(typeof(J4JSmsLogger<>))
.As(typeof(IJ4JSmsLogger<>))
.As(typeof(IJ4JLogger<>))
.SingleInstance();
Upvotes: 0