How can I dynamically register generic classes with a name with Unity?

℡╲_俬逩灬. 提交于 2019-12-06 02:35:14

问题


I have an assembly with a lot of classes (300+) with a BaseClass and I want register a generic class with a interface.

With unity you have to register by {Name} if you want to resolve an array of objects of the interface. I want an array of objects in the MainViewModel automatically.

Is there a way to automate this with reflection? Any suggestions?

Example (pseudo):

public class BaseClass
{
   public void doFoo();
}

public ClassNumber001 : BaseClass
{
}
public ClassNumber002 : BaseClass
{
}

public interface ISuperman
{
}

public class Superman : ISuperman where T : BaseClass
{
}

public MainViewModel(IEnumerable<ISuperman> lotsofSuperman)
{
}

Working example by hand:

container.RegisterType<ISuperman, Superman <ClassNumber001>>("ClassNumber001");
container.RegisterType<ISuperman, Superman <ClassNumber002>>("ClassNumber002");
container.RegisterType<IEnumerable<ISuperman>, ISuperman[]>();

回答1:


This is something that comes to my mind that might work for you...

You can register the type as follows, and should work for the open generic.

container.RegisterType(typeof(ISuperman<>), typeof(Superman<>), ... );

Registering generic parameters and types

Hope this helps!




回答2:


Yes, you'll need to use reflection to easily create all of the mappings that you want. Since you are using Unity 3 you can take advantage of Registration by Convention to provide help (with the heavier lifting) in registering the classes.

I've taken your pseudo code and translated it into real code:

public abstract class BaseClass
{
    public abstract void DoFoo();
}

public class ClassNumber001 : BaseClass
{
    public override void DoFoo()
    {
        Console.WriteLine("001 Foo");
    }
}

public class ClassNumber002 : BaseClass
{
    public override void DoFoo()
    {
        Console.WriteLine("002 Foo");
    }
}

public interface ISuperman
{
    void Do();
}

public class Superman<T> : ISuperman where T : BaseClass
{
    private T baseClass;

    public Superman(T baseClass)
    {
        this.baseClass = baseClass;
    }

    public void Do()
    {
        this.baseClass.DoFoo();
    }
}

public class MainViewModel
{
    public MainViewModel(IEnumerable<ISuperman> lotsofSuperman)
    {
        foreach(ISuperman superman in lotsofSuperman)
        {
            superman.Do();
        }
    }
}

Then use registration by convention to register all the generics:

IUnityContainer container = new UnityContainer();

container.RegisterTypes(
    AllClasses.FromAssembliesInBasePath().Where(t => typeof(BaseClass).IsAssignableFrom(t))
        .Select(t => typeof(Superman<>).MakeGenericType(t)),
    t => new Type[] { typeof(ISuperman) },
    t => t.GetGenericArguments().First().Name,
    WithLifetime.Transient);

container.RegisterType<IEnumerable<ISuperman>, ISuperman[]>();

container.Resolve<MainViewModel>();

In the above code we get all classes that inherit from BaseClass and then construct a type Superman<> and map that to ISuperman using the name of the BaseClass. The RegisterTypes call will be equivalent to calling RegisterType for every BaseClass:

container.RegisterType<ISuperman, Superman<ClassNumber001>("ClassNumber001");
container.RegisterType<ISuperman, Superman<ClassNumber002>("ClassNumber002");

Then when MainViewModel is resolved it iterates over all ISuperman instances and calls a method which prints out:

001 Foo
002 Foo

showing that we injected 2 ISuperman instances: Superman<ClassNumber001> and Superman<ClassNumber002>.

If you need specific registrations for the BaseClasses (e.g. non-default lifetime manager) then you can use registration by convention to register those too).




回答3:


There are some of the ways this can be done. One is by using XML where the type is defined lets say MyClass and IMyClass and during runtime it resolves based on the assemblies available. But a better approach in my opinion would be to create a project to which you can delegate the responsibility of loading up the dependencies.

Lets say you create a class like so:

     public class MyClass : IMyClass
    {
        private readonly IUnityContainer _container;

        #ctor
        // initialie the container through the constructor

        public void DoWork<Interface, Class>() where Class: Interface
        {
            _container.RegisterType<Interface, Class>(
             //TODO: You can setup the container lifecycle which can be transient
             // or singleton or custom based on your project requirement  
             )
        }
    }

Now whoever needs to register itself can call this interface IMyClass to get itself registered in the container and dependency can be injected to whichever class needs to perform that task.



来源:https://stackoverflow.com/questions/31431500/how-can-i-dynamically-register-generic-classes-with-a-name-with-unity

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