/// <summary> /// Register a MEF part definition. /// </summary> /// <param name="builder">The container builder.</param> /// <param name="partDefinition">The part definition to register.</param> /// <param name="exposedServicesMapper">A mapping function to transform ExportDefinitions into Services.</param> public static void RegisterComposablePartDefinition(this ContainerBuilder builder, ComposablePartDefinition partDefinition, Func <ExportDefinition, IEnumerable <Service> > exposedServicesMapper) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (partDefinition == null) { throw new ArgumentNullException(nameof(partDefinition)); } if (exposedServicesMapper == null) { throw new ArgumentNullException(nameof(exposedServicesMapper)); } var partId = new UniqueService(); var partRegistration = CreateBasePartRegistration(builder, partDefinition, partId); if (IsSharedInstance(partDefinition)) { partRegistration.SingleInstance(); } foreach (var iterExportDef in partDefinition.ExportDefinitions) { ProcessExportDefinition(builder, exposedServicesMapper, partId, iterExportDef); } }
/// <summary> /// Register a MEF part definition. /// </summary> /// <param name="builder">The container builder.</param> /// <param name="partDefinition">The part definition to register.</param> /// <param name="exposedServicesMapper">A mapping function to transform ExportDefinitions into Services.</param> public static void RegisterComposablePartDefinition(this ContainerBuilder builder, ComposablePartDefinition partDefinition, Func <ExportDefinition, IEnumerable <Service> > exposedServicesMapper) { if (builder == null) { throw new ArgumentNullException("builder"); } if (partDefinition == null) { throw new ArgumentNullException("partDefinition"); } if (exposedServicesMapper == null) { throw new ArgumentNullException("exposedServicesMapper"); } var partId = new UniqueService(); var partRegistration = builder.Register(c => partDefinition.CreatePart()) .OnActivating(e => SetPrerequisiteImports(e.Context, e.Instance)) .OnActivated(e => SetNonPrerequisiteImports(e.Context, e.Instance)) .As(partId); if (IsSharedInstance(partDefinition)) { partRegistration.SingleInstance(); } foreach (var iterExportDef in partDefinition.ExportDefinitions) { var exportDef = iterExportDef; var contractService = new ContractBasedService(exportDef.ContractName, GetTypeIdentity(exportDef)); var exportId = new UniqueService(); builder.Register(c => { var p = ((ComposablePart)c.ResolveService(partId)); return(new Export(exportDef, () => p.GetExportedValue(exportDef))); }) .As(exportId, contractService) .ExternallyOwned() .WithMetadata(exportDef.Metadata); var additionalServices = exposedServicesMapper(exportDef).ToArray(); if (additionalServices.Length > 0) { builder.Register(c => ((Export)c.ResolveService(exportId)).Value) .As(additionalServices) .ExternallyOwned() .WithMetadata(exportDef.Metadata); } } }
private static void ProcessExportDefinition(ContainerBuilder builder, Func <ExportDefinition, IEnumerable <Service> > exposedServicesMapper, UniqueService partId, ExportDefinition iterExportDef) { var exportDef = iterExportDef; var contractService = new ContractBasedService(exportDef.ContractName, GetTypeIdentity(exportDef)); var exportIsShared = IsSharedInstance(exportDef); var exportId = new UniqueService(); var exportReg = builder.Register(c => { var p = (ComposablePart)c.ResolveService(partId); return(new Export(exportDef, () => p.GetExportedValue(exportDef))); }) .As(exportId, contractService) .ExternallyOwned() .WithMetadata(exportDef.Metadata); // Issue #348: When a constructor takes in a duplicate dependency like: // public ImportsDuplicateMefClass(ImportsMefDependency first, ImportsMefDependency second) // and each of those dependencies also take in the same thing: // public ImportsMefDependency(IDependency dependency) // Then when the export/import process gets run, if the export doesn't have // the same lifetime scope sharing (per-instance vs. singleton) you // have trouble because the OnActivating from above in the original part // registration doesn't run, the chained-in prerequisite imports never get // populated, and everything fails. Setting the export registrations to be // the same lifetime scope as the part they correspond to fixes the issue. if (exportIsShared) { exportReg.SingleInstance(); } var additionalServices = exposedServicesMapper(exportDef).ToArray(); if (additionalServices.Length > 0) { var addlReg = builder.Register(c => ((Export)c.ResolveService(exportId)).Value) .As(additionalServices) .ExternallyOwned() .WithMetadata(exportDef.Metadata); if (exportIsShared) { addlReg.SingleInstance(); } } }
private static IRegistrationBuilder <ComposablePart, SimpleActivatorData, SingleRegistrationStyle> CreateBasePartRegistration(ContainerBuilder builder, ComposablePartDefinition partDefinition, UniqueService partId) { return(builder.Register(c => partDefinition.CreatePart()) .OnActivating(e => SetPrerequisiteImports(e.Context, e.Instance)) .OnActivated(e => SetNonPrerequisiteImports(e.Context, e.Instance)) .As(partId)); }
private static void RegisterPooled <TLimit, TActivatorData, TSingleRegistrationStyle>( IRegistrationBuilder <TLimit, TActivatorData, TSingleRegistrationStyle> registration, IPooledRegistrationPolicy <TLimit> registrationPolicy, object[]?tags) where TSingleRegistrationStyle : SingleRegistrationStyle where TActivatorData : IConcreteActivatorData where TLimit : class { if (registration == null) { throw new ArgumentNullException(nameof(registration)); } // Mark the lifetime appropriately. var regData = registration.RegistrationData; regData.Lifetime = new PooledLifetime(); regData.Sharing = InstanceSharing.None; var callback = regData.DeferredCallback; if (callback is null) { throw new NotSupportedException(RegistrationExtensionsResources.RequiresCallbackContainer); } if (registration.ActivatorData.Activator is ProvidedInstanceActivator) { // Can't use provided instance activators with pooling (because it would try to repeatedly activate). throw new NotSupportedException(RegistrationExtensionsResources.CannotUseProvidedInstances); } var original = callback.Callback; Action <IComponentRegistryBuilder> newCallback = registry => { // Only do the additional registrations if we are still using a PooledLifetime. if (!(regData.Lifetime is PooledLifetime)) { original(registry); return; } var pooledInstanceService = new UniqueService(); var instanceActivator = registration.ActivatorData.Activator; if (registration.ResolvePipeline.Middleware.Any(c => c is CoreEventMiddleware ev && ev.EventType == ResolveEventType.OnRelease)) { // OnRelease shouldn't be used with pooled instances, because if a policy chooses not to return them to the pool, // the Disposal will be fired, not the OnRelease call; this means that OnRelease wouldn't fire until the container is disposed, // which is not what we want. throw new NotSupportedException(RegistrationExtensionsResources.OnReleaseNotSupported); } // First, we going to create a pooled instance activator, that will be resolved when we want to // **actually** resolve a new instance (during 'Create'). // The instances themselves are owned by the pool, and will be disposed when the pool disposes // (or when the instance is not returned to the pool). var pooledInstanceRegistration = new ComponentRegistration( Guid.NewGuid(), instanceActivator, RootScopeLifetime.Instance, InstanceSharing.None, InstanceOwnership.ExternallyOwned, registration.ResolvePipeline, new[] { pooledInstanceService }, new Dictionary <string, object?>()); registry.Register(pooledInstanceRegistration); var poolService = new PoolService(pooledInstanceRegistration); var poolRegistration = new ComponentRegistration( Guid.NewGuid(), new PoolActivator <TLimit>(pooledInstanceService, registrationPolicy), RootScopeLifetime.Instance, InstanceSharing.Shared, InstanceOwnership.OwnedByLifetimeScope, new[] { poolService }, new Dictionary <string, object?>()); registry.Register(poolRegistration); var pooledGetLifetime = tags is null ? CurrentScopeLifetime.Instance : new MatchingScopeLifetime(tags); // Next, create a new registration with a custom activator, that copies metadata and services from // the original registration. This registration will access the pool and return an instance from it. var poolGetRegistration = new ComponentRegistration( Guid.NewGuid(), new PoolGetActivator <TLimit>(poolService, registrationPolicy), pooledGetLifetime, InstanceSharing.Shared, InstanceOwnership.OwnedByLifetimeScope, regData.Services, regData.Metadata); registry.Register(poolGetRegistration); // Finally, add a service pipeline stage to just before the sharing middleware, for each supported service, to extract the pooled instance from the pool instance container. foreach (var srv in regData.Services) { registry.RegisterServiceMiddleware(srv, new PooledInstanceUnpackMiddleware <TLimit>(), MiddlewareInsertionMode.StartOfPhase); } }; callback.Callback = newCallback; }