2
Vote

Unity integration and registration names

description

MefContrib has a bug when using registration names in Unity.
It throws the ImportCardinalityMismatchException, because it ignores the registration names when creating the contract names.
 
I have the following registrations in unity:
 
container.RegisterType<IDataLayer>(new
ContainerControlledLifetimeManager(), new InjectionFactory(
(container) => DataLayerFactory.CreateSqlDataLayer());
 
container.RegisterType<IDataLayer>("Local", new
ContainerControlledLifetimeManager(), new InjectionFactory(
(container) => DataLayerFactory.CreateLocalConfigurationDataLayer());
 
And on my exported class it has an IDataLayer parameter on its constructor.
 
I noticed that on PrivateFactoryExportProvider.GetExportsCore method it always gets the contract name by its contract type.
 
Also the UnityContainerAdapter registers the contract name as the registration name, while contract names should be unique (which doesn't happen with unity where the registration name must be unique
to each type and not globally) and therefore should contain a namespace or something to identify uniquely the registration.
 
I've modified the project with the following changes:
 
 
On PrivateFactoryExportProvider.GetExportsCore:
 
protected override IEnumerable<Export> GetExportsCore(ImportDefinition
definition, AtomicComposition atomicComposition)
                   {
                           if (definition.Cardinality == ImportCardinality.ZeroOrMore)
                           {
                                   return from exportDefinition in this.ReadOnlyDefinitions
                                              let contractName = exportDefinition.ContractName ??
AttributedModelServices.GetContractName(exportDefinition.ContractType)
                                              where contractName == definition.ContractName
                                              select new Export(exportDefinition, () =>
exportDefinition.Factory(SourceProvider));
                           }
 
                           return base.GetExportsCore(definition, atomicComposition);
                   }
 
And on UnityContainerAdapter:
 
public override void Initialize()
           {
                   TypeRegistrationTrackerExtension.RegisterIfMissing(container);
 
                   var tracker = this.container.Configure<TypeRegistrationTrackerExtension>();
 
                   foreach (var entry in tracker.Entries)
                   {
                           OnRegisteringComponent(entry.Type, GetContractName(entry.Type, entry.Name));
                   }
 
                   tracker.Registering += (s, e) =>
                           OnRegisteringComponent(e.TypeFrom ?? e.TypeTo,
GetContractName(e.TypeFrom ?? e.TypeTo, e.Name));
 
                   tracker.RegisteringInstance += (s, e) =>
                           OnRegisteringComponent(e.RegisteredType,
GetContractName(e.RegisteredType, e.Name));
           }
 
           public override object Resolve(Type type, string name)
           {
                   return this.container.Resolve(type, ExtractName(name));
           }
 
And on CompositionStrategy:
 
public override void PreBuildUp(IBuilderContext context)
           {
                   // If type is registered in the Unity container, don't even bother
messing with MEF
                   var policy =
context.Policies.Get<IBuildKeyMappingPolicy>(context.OriginalBuildKey);
                   if (policy != null)
                           return;
 
                   var container =
context.Policies.Get<ICompositionContainerPolicy>(null).Container;
                   var buildKey = context.OriginalBuildKey;
                   var lazyExport = ContainerServices.Resolve(container,
buildKey.Type, buildKey.Name);
                   if (lazyExport != null)
                   {
                           context.Existing = lazyExport.Value;
                           context.BuildComplete = true;
                   }
           }
 
 
 
 
The methods GetContractName and ExtractName may be anything you want,
as long as it creates an unique contract name and extract the
registration name from the contract name.
In my case, the contract name is "type[registrationName]".
 
This solved the problem. Hope it helps someone else and makes it to next release.
 
Regards

comments