Reputation: 1455
I'm trying to understand when [ImportingConstructor]
would be more appropriate than decorating properties with [import]
. Is it a personal preference, or something that allows classes to be constructed by other DI containers or are there benfits over [import]
?
I'd think that maybe if you didn't want to expose public properties but MEF will resolve private fields too, so again, where is the benefit?
Upvotes: 17
Views: 19077
Reputation: 754585
The problem with using [Import]
is that it separates the creation of an object into two distinct and observable phases: created and initialized. Where [ImportingConstructor]
allows this to remain as a single phase exactly as it would for every other .Net object.
This difference becomes observable in a number of ways
[Import]
on a field changes the logical contract of a type. Yet it doesn't change the public or usage contract. This means any code which previously compiled will continue to compile even though the objects dependencies have changed (think unit tests). This takes what should be a compile time error and makes it a runtime one.[Import]
. The contract verification engine properly recognizes that all fields can exist as null
values and will require a check before every use of a field. readonly
as you would with a normal C# object.Upvotes: 26
Reputation: 61589
Instead of thinking purely in terms of MEF, look at your class design in a broader sense. Typically when you are designing a class, you have a set of associated properties, these could be services, e.g.,
public class MyService
{
public ILogger Logger { get; set; }
public void SaySomething()
{
Logger.Log("Something");
}
}
Now, I could go ahead and create an instance of that:
var service = new MyService();
And now, if I try and use the method:
service.SaySomething();
If I don't know explicitly that I have to also initialise my Logger
property:
var service = new MyService() { Logger = new ConsoleLogger() };
(or):
var service = new MyService();
service.Logger = new ConsoleLogger();
Then the error won't become apparent until runtime. If we were to redefine the class:
public class MyService
{
private readonly ILogger _logger;
public MyService(ILogger logger)
{
if (logger == null) throw new ArgumentNullException("logger");
_logger = logger;
}
public void SaySomething()
{
_logger.Log("Something");
}
}
Now, if you try and create an instance of MyService
, you explicitly have to provide this additional service (ILogger
) for the object to be initialised correctly. This helps a number of ways:
You can improve on this design more by using Code Contracts (as mentioned by @JaredPar) to incude static checking at compile time.
In terms of MEF, you can get away with using [Import]
instead of [ImportingConstructor]
as MEF will throw an exception when it cannot satisfy all imports on a type, and will only return the type after both initialisation ([ImportingConstructor]
) and then [Import]
s.
Constructor injection is generally preferable.
Upvotes: 12
Reputation: 564373
By using [ImportingConstructor]
, you allow one class that serves as an export to import its dependencies. This dramatically simplifies the architecture, as you can decouple the dependencies of a concrete object from its implementation.
Normally, you'd use [ImportingConstructor]
on a type that is, itself, marked as [Export]
. When the type is composed, the constructor arguments will get provided by MEF.
Upvotes: 5