Can I use MEF to create an extensible factory?

主宰稳场 提交于 2019-12-23 22:19:15

问题


Let me start by saying that I am coming at this with limited MEF experience and have accomplished my goals using both Castle and Unity previously. I'm hoping something similar can be done with MEF.

In short, what I need is a factory class that can instantiate objects by name. More specifically, I will have an abstract base class such as:

public abstract class TheBaseClass { ... }

There will be any number of subclasses that derive from the base class:

public class OneSubClass : TheBaseClass { ... }

public class AnotherSubClass : TheBaseClass { ... }

At runtime, I need a factory that I can call, passing a 'key' value, to get the specific subclass instance returned like:

var key = "AnotherSubClass";
TheBaseClass instance = TheFactory.CreateInstance(key);

In Castle and Unity, I could register the type with 'key' as the name and use this value as a lookup when trying to resolve the instance from the container. I thought I might be able to accomplish the same thing using ExportMetadata but am at a stand-still how I can do it.

The rationale behind this approach (in case there's a better way), is that I need to instantiate a strongly-typed subclass at runtime without any knowledge of that type at compile time because the application is extensible and (exported) types can be added through external assemblies.

Any ideas?


回答1:


I would suggest to use strongly typed names to avoid mistyping errors.

To do so, first you need to create an enum that you will use as a key:

public enum DerivedClassesKeyEnum
{
    ONE,
    TWO
}

Then you create a custom attribute:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class DirivedBaseExportAttribute : ExportAttribute
{
    public DirivedBaseExportAttribute()
        :base(typeof(TheBaseClass))
    { }

    public DerivedClassesKeyEnum DerivedClassId { get; set; }
}

Next, you apply this attribute to yuor derived classes:

[DirivedBaseExport(DerivedClassId=DerivedClassesKeyEnum.ONE)]
public class OneSubClass : TheBaseClass
{

}

In the part that will import these classes you declare an interface:

public interface IDerivedClassMetadata
{
    DerivedClassesKeyEnum DerivedClassId{get;}
}

And last bit, in your FactoryClass:

public class TheFactory
{
    [ImportMany]
    public static IEnumerable<Lazy<TheBaseClass, IDerivedClassMetadata>> DerivedClasses { get; set; }

    public static TheBaseClass CreateInstance(DerivedClassesKeyEnum id)
    {
        return DerivedClasses.Single(c => c.Metadata.DerivedClassId == id).Value;
    }

}

It is simplified code just to illustrate the usage.




回答2:


this works the same way in MEF as it does in the other IoC containers you mentioned.

[Export("one", typeof(TheBaseClass)]
public class OneSubClass : TheBaseClass { ... }

[Export("two", typeof(TheBaseClass)]
public class AnotherSubClass : TheBaseClass { ... }

The 'keys' I've assigned here are "one" and "two" but of course you can use anything you like.

Then, you use that key in combination with GetExport():

var catalog = new TypeCatalog(typeof(OneSubClass), typeof(AnotherSubClass));
var container = new CompositionContainer(catalog);

var two = container.GetExport<TheBaseClass>("two");
var value = two.Value;

A couple of notes; don't forget to release the exports you get from the container in this way, using for example container.ReleaseExport(two).

You should also note that you can use this with any catalog - I've just chosen TypeCatalog for the example, but others work equally well.



来源:https://stackoverflow.com/questions/8009107/can-i-use-mef-to-create-an-extensible-factory

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!