I've got an interface, implementation, and target:
public interface IPerson { public string Name { get; } }
public class Person: IPerson { public string Name { get { return "John"; } } }
public class Target { public Target(IPerson person) {} }
I'm using Autofac to tie things together:
builder.RegisterType<Person>().As<IPerson>().SingleInstance();
The problem is that IPerson
lives in a shared assembly, Person
lives in a plugin (which may or may not be there), and Target
lives in the main application that loads plugins. If there are no plugins loaded that implement IPerson
, Autofac goes ballistic about not being able to resolve Target
's dependencies. And I cannot really blame it for that.
However I know that Target
is able to handle the lack of an IPerson
and would be more than happy to get a null
instead. In fact, I'm pretty sure that all the components which rely on an IPerson
are prepared to take a null
it its stead. So how can I tell Autofac - "It's OK sweety, don't worry, just give me back a null
, alright?"
One way I found is to add a default parameter to Target
:
public class Target { public Target(IPerson person = null) {} }
That works, but then I need to do this for all the components that require an IPerson
. Can I also do it the other way round? Somehow tell Autofac "If all else fails for resolving IPerson
, return null"?
Open the solution that want to use Autofac in, then select Manager NuGet Packages for Solution... by going to: Tools -> NuGet Package Manager -> Manager NuGet Packages for Solution... Installing through NuGet will automatically add Autofac in the References of the projects which were selected during installation.
Reflection Components RegisterType(typeof(ConfigReader)); When using reflection-based components, Autofac automatically uses the constructor for your class with the most parameters that are able to be obtained from the container.
Autofac is an IoC container for Microsoft . NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity.
Autofac is an open-source dependency injection (DI) or inversion of control (IoC) container developed on Google Code. Autofac differs from many related technologies in that it sticks as close to bare-metal C# programming as possible.
You could just take your dependency on IPerson person = null
in your constructor, which is an implicit declaration of an optional IPerson
dependency (c.f. @YaserMoradi). However, that puts you in the position of having to solidify this, now and forever after:
"... I'm pretty sure that all the components which rely on an
IPerson
are prepared to take anull
it its stead."
Better that this doesn't have to be a question at all.
The "best practice" pattern (which @CyrilDurand gives as a suffix on his answer) for this is to use a default implementation (link upshot: Autofac will use the last registered component as the default provider of that service). If you have no other implementation coming from your plugin (registered after the default) this default will be used.
In your case, the default component should be some kind of no-op or base implementation of the IPerson
service, where any method called will have whatever constitutes the default behavior for your application. This gives a better reuse story as well, since you can define the default behavior once and for all.
You can use this syntax :
builder.RegisterType<Target>().WithParameter(TypedParameter.From<IPerson>(null));
Unfortunately
builder.Register(c => (IPerson)null).As<IPerson>();
// will throw : Autofac.Core.DependencyResolutionException: A delegate registered to create instances of 'ConsoleApplication17.Program+IPerson' returned null.
and
builder.RegisterInstance<IPerson>(null).As<IPerson>();
// will throw : Unhandled Exception: System.ArgumentNullException: Value cannot be null.
If you don't want to add a WithParameter for each registration, you can add a module that will do it for you
public class OptionalAutowiringModule : Autofac.Module
{
public OptionalAutowiringModule(IEnumerable<Type> optionalTypes)
{
this._optionalTypes = optionalTypes;
}
public OptionalAutowiringModule(params Type[] optionalTypes)
{
this._optionalTypes = optionalTypes;
}
private readonly IEnumerable<Type> _optionalTypes;
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
base.AttachToComponentRegistration(componentRegistry, registration);
registration.Preparing += (sender, e) =>
{
e.Parameters = e.Parameters.Concat(new Parameter[] { new OptionalAutowiringParameter(this._optionalTypes) });
};
}
}
public class OptionalAutowiringParameter : Parameter
{
public OptionalAutowiringParameter(IEnumerable<Type> optionalTypes)
{
this._optionalTypes = optionalTypes.ToList();
}
private readonly List<Type> _optionalTypes;
public override Boolean CanSupplyValue(ParameterInfo pi, IComponentContext context, out Func<Object> valueProvider)
{
if (this._optionalTypes.Contains(pi.ParameterType) && !context.IsRegistered(pi.ParameterType))
{
valueProvider = () => null;
return true;
}
else
{
valueProvider = null;
return false;
}
}
}
Then, all you have to do is to register your module with your optional dependencies
builder.RegisterModule(new OptionalAutowiringModule(typeof(IPerson)));
But instead of injecting a null reference which may cause a nullReferenceException. An another solution would be to create NullPerson
implementation.
builder.RegisterType<NullPerson>().As<IPerson>();
builder.RegisterType<Target>();
When you have your real implementation only registered it again, it will overwrite the original implementation.
Just use optional parameters, see the following sample:
public class SomeClass
{
public SomeClass(ISomeDependency someDependency = null)
{
// someDependency will be null in case you've not registered that before, and will be filled whenever you register that.
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With