Force Unity to register only interfaces when using RegisterTypes

我的梦境 提交于 2019-12-11 17:27:50

问题


I want to register specific interfaces of my assemblies and map them to concrete types, using the RegisterTypes method.

They get registered well, but as a result I also get the concrete types being registered as From types.

For example, instead of having just ITenantStore interface mapped to TenantStore class, I get not only that mapping but also TenantStore mapped to TenantStore.

How can I register only the interfaces as From types? Or is there a efficient way to delete the registrations going from one class to the same class?

Edit: here is a sample code

var myClasses = AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("MyProject", StringComparison.OrdinalIgnoreCase));

container.RegisterTypes(myClasses, 
                  WithMappings.FromMatchingInterface, 
                  WithName.Default, 
                  WithLifetime.ContainerControlled, 
                  getInjectionMembers: t => new InjectionMember[]
                  {
                     new Interceptor<InterfaceInterceptor>(),
                     new InterceptionBehavior<LoggingInterceptionBehavior>()
                  });

回答1:


Your container have registrations from ImplementationType to ImplementationType because you pass arguments getLifetimeManager and getInjectionMembers into container.RegisterTypes. So container.RegisterTypes checks this params and if the condition bellow is correct it registers current type the second time without type mapping. It registers current type for the first time with type mapping during invocation of UnityContainerRegistrationByConventionExtensions.RegisterTypeMappings:

    string name = getName(type);
    var lifetimeManager = getLifetimeManager(type);
    var members = getInjectionMembers(type).ToList();

    // UnityContainerRegistrationByConventionExtensions.RegisterTypeMappings(container, ...) – It's invocation of common registration 
    // for current type to getFromTypes(type). getFromTypes is WithMappings.FromMatchingInterface in your case

    if (lifetimeManager != null || members.Count > 0)
        container.RegisterType(type, name, lifetimeManager, members);

At this moment I can suggest don't pass this two arguments to container.RegisterTypes and you would not have a duplicate registrations...




回答2:


As noted by @GeorgeAlexandria the reason why "duplicate" concrete registrations appear is because the code is written that way. The behavior is by design.

One reason it's done this way is to avoid registration by convention changing the core logic and to account for some edge cases in supporting different scenarios. For example, in Unity the InjectionMembers and LifetimeManger are associated with the concrete type. If the concrete type is not explicitly registered this can cause unexpected behavior when re-registering types with different lifetime managers and/or injection members.

Internally, when you call

RegisterType<ITenantStore, TenantStore>(new 
    ContainerControlledLifetimeManager(),
    new InjectionMember[]
    {
        new Interceptor<InterfaceInterceptor>(),
        new InterceptionBehavior<LoggingInterceptionBehavior>()
    });

a mapping is registered, the LifetimeManager is associated with the concrete type TenantStore via a policy and and the injection members policies are added associated with the concrete type TenantStore. Basically, a mapping is created for the interface to the concrete type and policies are added indicating how to customize the build plan for the concrete type. Although the exact code path is slightly different I think it is very similar to what happens with the registration by convention approach with the one apparent difference being the concrete registration appearing in the public Registrations property.

If the behavior is causing an issue or is just irritating you could always register the classes yourself. If the situation is straight forward it could look like this:

var myClasses = AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("MyProject", StringComparison.OrdinalIgnoreCase));
foreach (var type in myClasses)
{
    var matchingInterfaceName = "I" + type.Name;
    var interfaceType = type.GetInterfaces().FirstOrDefault(i => string.Compare(i.Name, matchingInterfaceName, StringComparison.Ordinal) == 0);

    if (interfaceType != null)
    {
        container.RegisterType(interfaceType, type, new ContainerControlledLifetimeManager(),
            new InjectionMember[]
            {
                new Interceptor<InterfaceInterceptor>(),
                new InterceptionBehavior<LoggingInterceptionBehavior>()
            });
    }
}

This isn't much more code the the convention approach but is pretty naive and does not support generics properly.



来源:https://stackoverflow.com/questions/45534794/force-unity-to-register-only-interfaces-when-using-registertypes

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