In the MEF's host, when we declare an imported object through import, the object is created when it is assembled (Compose). For example:
Interface ILogger
{
void Log (String message);
}
[Export (typeof (ILogger))]
Class Consolelogger:ilogger
{
public void Log (String message)
{
Console.WriteLine ("Logger 1" + message);
}
}
Class Host
{
[Import]
ILogger _logger = null;
Public Host ()
{
var catalog = new Assemblycatalog (this. GetType (). Assembly);
var container = new Compositioncontainer (catalog);
The Consolelogger object is created here
Container.composeparts (this);
_logger. Log ("Hello World");
}
}
Sometimes, some components are more expensive to create, but they are not immediately available. At this point, we want to delay the initialization by delaying it to the time it is used to create it, which improves performance (which is common to increase boot speed). MEF supports this pattern, we just need to modify the imported declaration form.
[Import]
Lazy<ilogger> _logger = null;
In this way, the logger is deferred to the first use when it was created.
Meta Data metadata
Sometimes, there are multiple providers for the same service, and we need to choose a use from it. MEF provides importmany to address this need.
[Export (typeof (ILogger))]
Class Consolelogger:ilogger
{
public void Log (String message)
{
Console.WriteLine (message);
}
}
[Export (typeof (ILogger))]
Class Dblogger:ilogger
{
public void Log (String message)
{
Console.WriteLine (message);
}
}
Class Host
{
[ImportMany]
ilogger[] _logger = null;
Public Host ()
{
var catalog = new Assemblycatalog (this. GetType (). Assembly);
var container = new Compositioncontainer (catalog);
Container.composeparts (this);
_logger. FirstOrDefault (i = i is Dblogger). Log ("Hello World");
}
}
At this point, if we want to use deferred import, it will become the following form:
Class Host
{
[ImportMany]
lazy<ilogger>[] _loggerservices = null;
Public Host ()
{
var catalog = new Assemblycatalog (this. GetType (). Assembly);
var container = new Compositioncontainer (catalog);
The Consolelogger object is created here
Container.composeparts (this);
_loggerservices.firstordefault (i = I.value is Dblogger). Value.Log ("Hello World");
}
}
At first glance, there is no problem, and all logger are created by delay. But a careful analysis will find that to find Dblogger, you have to traverse all the _loggerservices, which leads to the creation of logger. That is, it is possible to create all logger when using the first logger.
So, how do we create only the logger we need? This is the time for metadata to appear, and the metadata in the MEF can be exported by attaching a single data to the export's service object, so that the corresponding service can be found through the element data. First, let's look at the final effect:
public interface Iloggerdata
{
String Name {get;}
}
Class Host
{
[ImportMany]
Lazy<ilogger, iloggerdata>[] _logger = null;
Public Host ()
{
var catalog = new Assemblycatalog (this. GetType (). Assembly);
var container = new Compositioncontainer (catalog);
Container.composeparts (this);
_logger. FirstOrDefault (i = I.metadata.name = = "DB Logger"). Value.Log ("Hello World");
}
}
Here first declares the interface of a metadata type Iloggerdata, then, the imported object becomes Lazy<ilogger, Iloggerdata> This object has a property of metadata, Its type is the iloggerdata that was just declared. The exported ILogger object was created lazily, and the metadata was not created lazily. We can find the desired logger object by traversing the Iloggerdata to create only the logger object that is used.
The question now is: How to declare the relevant metadata when exporting. The MEF provides two ways:
Flag declaration by Exportmetadataattribute
This is done in conjunction with the export of the service with the Exportmetadataattribute attribute tag element according to:
[ExportMetaData ("Name", "Console Logger")]
[Export (typeof (ILogger))]
Class Consolelogger:ilogger
{
public void Log (String message)
{
Console.WriteLine (message);
}
}
[ExportMetaData ("Name", "DB Logger")]
[Export (typeof (ILogger))]
Class Dblogger:ilogger
{
public void Log (String message)
{
Console.WriteLine (message);
}
}
Exportmetadataattribute has two parameters, name and Value,name are property names, and value is the property value. When Compse, the MEF creates a metadata object that implements it, and then assigns a value value to the name of the property, based on its assignment to name.
This is relatively simple, but it has two drawbacks:
1. Attribute names are not strongly typed
Here we have to give the string to the flag property name, there is no syntax-level consistency check between them, and in the absence of the nameof operator, it is very error-prone to change the metadata property name.
2. The assignment is cumbersome if the metadata has multiple values.
If we add an attribute of the priority type,
public interface Iloggerdata
{
String Name {get;}
int priority {get;}
}
At this point, you must add a exportmetadata tag to all the exported objects and write the following form:
[ExportMetaData ("Name", "DB Logger")]
[ExportMetaData ("Priority", 3)]
When the attributes are a bit more, the programmer will be dozens. And once an export object is omitted, the object will not be imported. This is a run-time error, very difficult to troubleshoot.
Mark by attribute
This way you can mark metadata with a attribute:
[Metadataattribute]
[AttributeUsage (AttributeTargets.Class, inherited = False, AllowMultiple = False)]
Class Loggerdataattribute:attribute, Iloggerdata
{
public string Name {get; private set;}
Public Loggerdataattribute (string name)
{
This. name = name;
}
}
[Loggerdata ("Console Logger")]
[Export (typeof (ILogger))]
Class Consolelogger:ilogger, Iloggerdata
{
public string Name {get; set;}
public void Log (String message)
{
Console.WriteLine (message);
}
}
[Loggerdata ("DB Logger")]
[Export (typeof (ILogger))]
Class Dblogger:ilogger, Iloggerdata
{
public string Name {get; set;}
public void Log (String message)
{
Console.WriteLine (message);
}
}
First, declare a loggerdataattribute, this attribute must be metadataattribute marked. The Loggerdataattribute is then added to the export object so that when the MEF is imported, the metadata is created based on the Loggerdataattribute.
It is worth mentioning that the Loggerdataattribute itself does not need to implement the Iloggerdata interface, it is a ducktype convention, only need to implement the metadata properties. I have implemented this interface here primarily to allow the compiler to ensure that metadata attributes are accurately implemented.
Implementing a deferred loading part (GO) in MEF