Scenario
Application has multiple parts.
Each part is in separate dll and implements interface IFoo
All such dlls are present in same directory (plugins)
The application can instantiate multiple instances of each part
Below is the code snippet for the interfaces, part(export) and the import. The problem I am running into is, the "factories" object is initialized with empty list.
However, if I try container.Resolve(typeof(IEnumerable< IFoo >)) I do get object with the part. But that doesn't serve my purpose (point 4). Can anyone point what I am doing wrong here?
public interface IFoo
{
string Name { get; }
}
public interface IFooMeta
{
string CompType { get; }
}
Implementation of IFoo in separate Dll
[ExportMetadata("CompType", "Foo1")]
[Export(typeof(IFoo)), PartCreationPolicy(CreationPolicy.NonShared)]
public class Foo1 : IFoo
{
public string Name
{
get { return this.GetType().ToString(); }
}
}
Main application that loads all the parts and instantiate them as needed
class PartsManager
{
[ImportMany]
private IEnumerable<ExportFactory<IFoo, IFooMeta>> factories;
public PartsManager()
{
IContainer container = ConstructContainer();
factories = (IEnumerable<ExportFactory<IFoo, IFooMeta>>)
container.Resolve(typeof(IEnumerable<ExportFactory<IFoo, IFooMeta>>));
}
private static IContainer ConstructContainer()
{
var catalog = new DirectoryCatalog(#"C:\plugins\");
var builder = new ContainerBuilder();
builder.RegisterComposablePartCatalog(catalog);
return builder.Build();
}
public IFoo GetPart(string compType)
{
var matchingFactory = factories.FirstOrDefault(
x => x.Metadata.CompType == compType);
if (factories == null)
{
return null;
}
else
{
IFoo foo = matchingFactory.CreateExport().Value;
return foo;
}
}
}
It seems to be an issue with ContainerBuilder. I tried alternate approach with CompositionContainer and it worked without hurdles. Pasting the code snippet for modified methods.
public PartsManager()
{
ConstructContainer();
}
private void ConstructContainer()
{
var catalog = new DirectoryCatalog(#"C:\plugins\");
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
container.SatisfyImportsOnce(this);
}
It appears that it's a known issue in Autofac, that is currently closed with "won't fix" resolution.
If you remove dependency from Autofac like below it will work:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.ComponentModel.Composition;
using System.Reflection;
namespace SO24132313
{
public interface IFoo
{
string Name { get; }
}
public interface IFooMeta
{
string CompType { get; }
}
[ExportMetadata("CompType", "Foo1")]
[Export(typeof(IFoo)), PartCreationPolicy(CreationPolicy.NonShared)]
public class Foo1 : IFoo
{
public string Name
{
get { return GetType().ToString(); }
}
}
class PartsManager
{
[ImportMany]
private IEnumerable<ExportFactory<IFoo, IFooMeta>> factories;
public PartsManager()
{
ConstructContainer(this);
}
private static void ConstructContainer(PartsManager p)
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var c = new CompositionContainer(catalog);
c.ComposeParts(p);
}
public IFoo GetPart(string compType)
{
var matchingFactory = factories.FirstOrDefault(
x => x.Metadata.CompType == compType);
if (factories == null)
{
return null;
}
else
{
IFoo foo = matchingFactory.CreateExport().Value;
return foo;
}
}
}
class Program
{
static void Main(string[] args)
{
PartsManager a = new PartsManager();
IFoo bla = a.GetPart("Foo1");
Console.WriteLine(bla);
}
}
}
Related
I have a public method ValidateWords inside FooService.To test the ValidateWord method, I created IAppSettingWrapper and AppSettingWrapper which returns the Instance of AppSettings.
Inside the test method, I want to substitute NotAllowedWords using NSubstitute. However, it throws an object reference exception. Is there any way for substitution? If it's not possible, how can I refactor my static instance?
public sealed class AppSettings
{
private static object _lockObject = new object();
private static volatile AppSettings? _instance;
private static DateTime _cacheTime;
private Settings[] _settings;
public AppSettings()
{
try
{
_settings = GetSettings();
}
catch { }
}
public static AppSettings Instance
{
get
{
lock (_lockObject)
{
if (_instance == null)
{
_instance = new AppSettings();
}
}
return _instance;
}
}
public List<string> NotAllowedWords
{
get
{
return new List<string>() {
"index",
"change"
};
}
}
public T GetValues<T>(string key,T defaultValue)
{
T result = defaultValue;
var settings = _settings.Where(i => i.Key == key).FirstOrDefault();
result = (T)Convert.ChangeType(settings.Value, typeof(T));
return result;
}
private Settings[]? GetSettings()
{
//gets data from web services
return base.Channel.GetSettings();
}
}
public class Settings
{
public string Key { get; set; }
public string Value { get; set; }
}
public interface IAppSettingsWrapper
{
public AppSettings Instance();
}
public class AppSettingsWrapper : IAppSettingsWrapper
{
public AppSettings Instance()
{
return AppSettings.Instance;
}
}
[TestClass]
public class FooServiceTest{
private IAppSettingsWrapper _appSettingsWrapper;
[TestInitialize]
public void TestInitialize(IAppSettingsWrapper appSettingsWrapper)
{
_appSettingsWrapper = Substitute.For<IAppSettingsWrapper>();
}
private FooService CreateFooService()
{
return new FooService(_appSettingsWrapper);
}
[TestMethod]
public void Throw_Exception_When_Given_Word_Not_Allowed() {
var service = this.CreateFooService();
_appSettingsWrapper.Instance().NotAllowedWords.Returns(new List<string> { "index" });
var word = "index";
Exception ex = Assert.ThrowsException<Exception>(() => service.ValidateWords(word));
Assert.AreEqual("this word is not allowed", ex.Message);
}
}
public class FooService
{
private IAppSettingsWrapper _appSettingsWrapper;
public FooService(IAppSettingsWrapper appSettingsWrapper)
{
_appSettingsWrapper = appSettingsWrapper;
}
public void ValidateWords(string word)
{
if (_appSettingsWrapper.Instance().NotAllowedWords.Contains(word))
{
throw new Exception("this word is not allowed");
}
}
}
The AppSettings.NotAllowedWords property is not substitutable due to it not being virtual and the class being sealed. If you add NSubstitute.Analyzers to your test project it will help you find these cases. (The How NSubstitute Works documentation outlines why this is the case.)
One option is to make AppSettings implement an IAppSettings interface and inject that into FooService (rather than the wrapper). Then you can use a substitute for tests, and AppSettings.Instance for your real code.
Is it possible to register same interface twice, where the first resolves to a default implementation and the second has a name and resolves to another type.
Example:
container.RegisterType(typeof(IMyInterface), typeof(MyDefaultImplementation));
container.RegisterType(typeof(IMyInterface), typeof(MySecondImplementation),"Second Implementations name");
So,
Resolve<IMyInterface>("non existing name")
Should resolve MyDefaultImplementation.
If you're OK with using the container you can do an extension method:
public static class UnityExtensions
{
public static T TryResolve<T>(this IUnityContainer container, string name)
{
if (container.IsRegistered<T>(name))
return container.Resolve<T>(name);
return container.Resolve<T>();
}
}
And use it like:
container.RegisterType<IMyInterface, Foo>();
container.RegisterType<IMyInterface, Bar>("bar");
var foo = container.TryResolve<IMyInterface>("non-existing");
// foo will be Foo
var bar = container.TryResolve<IMyInterface>("bar");
// bar will be Bar.
public interface IMyInterface { }
public class Foo : IMyInterface { }
public class Bar : IMyInterface { }
The downside is that you'll need to know when to use the extension and when not to... Otherwise you can build your own BuilderStrategy.
Heavily influensed by:
Unity - loadConfiguration, how to resolve only those configured
Is there TryResolve in Unity?
I'm not familiar enough with unity, but you cat start with creating you own configuration, add it as extension:
public class DefaultRegistrationFallbackConfiguration : UnityContainerExtension
{
protected override void Initialize()
{
this.Context.Registering += this.AppendRemapPolicy;
}
public override void Remove()
{
this.Context.Registering -= this.AppendRemapPolicy;
}
private void AppendRemapPolicy(object sender, RegisterEventArgs e)
{
if (e.Name != null)
return;
if (e.TypeFrom != null && e.TypeTo != null)
this.Context.Policies.SetDefault<IBuildKeyMappingPolicy>(new MapBuildKeyToDefaultPolicy(e.TypeFrom, e.TypeTo));
if (e.LifetimeManager == null)
return;
throw new NotImplementedException("TODO: lifetime management");
}
}
Create your own IBuildKeyMappingPolicy:
public class MapBuildKeyToDefaultPolicy : IBuildKeyMappingPolicy
{
private readonly Type _typeFrom;
private readonly Type _typeTo;
public MapBuildKeyToDefaultPolicy(Type typeFrom, Type typeTo)
{
this._typeFrom = typeFrom;
this._typeTo = typeTo;
}
public NamedTypeBuildKey Map(NamedTypeBuildKey buildKey, IBuilderContext context)
{
if (buildKey.Type == this._typeFrom)
return new NamedTypeBuildKey(this._typeTo);
throw new InvalidOperationException();
}
}
Test classes:
public interface IFoo
{
void Bar();
}
public class FooNamed : IFoo
{
public void Bar()
{
Console.WriteLine("named one");
}
}
public class FooDefault : IFoo
{
public void Bar()
{
Console.WriteLine("default one");
}
}
Test:
public static class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
// register extension before use container!
container.AddExtension(new DefaultRegistrationFallbackConfiguration());
container.RegisterType(typeof(IFoo), typeof(FooDefault));
container.RegisterType(typeof(IFoo), typeof(FooNamed), "named");
container.Resolve<IFoo>() .Bar(); // default one
container.Resolve<IFoo>("named") .Bar(); // named one
container.Resolve<IFoo>("unknown").Bar(); // default one
}
}
Output:
default one
named one
default one
Introduction
Class SessionModel is a service locator providing several services (I am going to elaborate my system architecture in the future, but for now I need to do it that way).
Code
I edited the following code part to be a Short, Self Contained, Correct (Compilable), Example (SSCCE):
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var sessionModel = new SessionModel(3);
// first case (see text down below):
var compositionContainer = new CompositionContainer();
// second case (see text down below):
//var typeCatalog = new TypeCatalog(typeof (SessionModel));
//var compositionContainer = new CompositionContainer(typeCatalog);
compositionContainer.ComposeExportedValue(sessionModel);
var someService = compositionContainer.GetExportedValue<ISomeService>();
someService.DoSomething();
}
}
public class SessionModel
{
private int AValue { get; set; }
[Export]
public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
SomeService = new SomeService();
}
}
public interface ISomeService
{
void DoSomething();
}
public class SomeService : ISomeService
{
public void DoSomething()
{
Console.WriteLine("DoSomething called");
}
}
}
Problem
I would like MEF to consider the parts (i.e. SomeService) exported by the service locator when composing other parts, but unfortunately this does not work.
First Case
When I try to get the exported value for ISomeService there is a System.ComponentModel.Composition.ImportCardinalityMismatchException telling me there are no exports with this contract name and required type identity (ConsoleApplication1.ISomeService).
Second Case
If I create the CompositionContainer using the TypeCatalog the exception is slightly different. It is a System.ComponentModel.Composition.CompositionException telling me MEF doesn't find a way to create a ConsoleApplication1.SessionModel (which is right and the reason why I am doing it myself).
Additional Information
mefx says for both cases:
[Part] ConsoleApplication1.SessionModel from: DirectoryCatalog (Path=".")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
[Part] ConsoleApplication1.SessionModel from: AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
What do I have to do? Is this possible with MEF or do I have to use Unity or StructureMap, or something else? Can this be done implementing an ExportProvider?
OK, that's how I did it:
I implemented my own SessionModelExportProvider finding exports in my SessionModel (see code below). Class SessionModelExport is just for holding the export data and – instead of creating an instance of a service – returning the value of the property of the SessionModel.
public class SessionModelExportProvider : ExportProvider
{
private List<Export> Exports { get; set; }
public SessionModelExportProvider(SessionModel sessionModel)
{
// get all the properties of the session model having an Export attribute
var typeOfSessionModel = typeof (SessionModel);
PropertyInfo[] properties = typeOfSessionModel.GetProperties();
var propertiesHavingAnExportAttribute =
from p in properties
let exportAttributes = p.GetCustomAttributes(typeof (ExportAttribute), false)
where exportAttributes.Length > 0
select new
{
PropertyInfo = p,
ExportAttributes = exportAttributes
};
// creating Export objects for each export
var exports = new List<Export>();
foreach (var propertyHavingAnExportAttribute in propertiesHavingAnExportAttribute)
{
var propertyInfo = propertyHavingAnExportAttribute.PropertyInfo;
foreach (ExportAttribute exportAttribute in propertyHavingAnExportAttribute.ExportAttributes)
{
string contractName = exportAttribute.ContractName;
if (string.IsNullOrEmpty(contractName))
{
Type contractType = exportAttribute.ContractType ?? propertyInfo.PropertyType;
contractName = contractType.FullName;
}
var metadata = new Dictionary<string, object>
{
{CompositionConstants.ExportTypeIdentityMetadataName, contractName},
{CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.Shared}
};
var exportDefinition = new ExportDefinition(contractName, metadata);
var export = new SessionModelExport(sessionModel, propertyInfo, exportDefinition);
exports.Add(export);
}
}
Exports = exports;
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
AtomicComposition atomicComposition)
{
return Exports.Where(e => definition.IsConstraintSatisfiedBy(e.Definition));
}
}
public class SessionModelExport : Export
{
private readonly SessionModel sessionModel;
private readonly PropertyInfo propertyInfo;
private readonly ExportDefinition definition;
public SessionModelExport(SessionModel sessionModel, PropertyInfo propertyInfo, ExportDefinition definition)
{
this.sessionModel = sessionModel;
this.propertyInfo = propertyInfo;
this.definition = definition;
}
public override ExportDefinition Definition
{
get { return definition; }
}
protected override object GetExportedValueCore()
{
var value = propertyInfo.GetValue(sessionModel, null);
return value;
}
}
The problem is that the SomeService is an instance property. You could have several SessionModel objects in your system, and MEF would have no way of knowing which SessionModel is returning the ISomeService instance that is supposed to be matched to an import.
Instead, just make SessionModel a static class and SomeService a static property. Alternatively, make SessionModel a singleton. The SomeService property would still be static, but would export the service from the one-and-only instance of SessionModel.
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;
using System.Linq;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var catalogs = new AggregateCatalog();
var catalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(Assembly.GetExecutingAssembly());
catalogs.Catalogs.Add(catalog);
var sessionModel = new SessionModel(3);
var container = new CompositionContainer(catalog);
ISomeService someService = container.GetExportedValueOrDefault<ISomeService>(sessionModel.cname);
if (someService != null)
{
someService.DoSomething();
}
}
}
public class SessionModel
{
private int AValue { get; set; }
//[Import("One",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
}
public string cname { get { return "One"; } }
}
public class SessionModel1
{
private int AValue { get; set; }
//[Import("Two",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel1(int aValue)
{
AValue = aValue;
}
public string cname { get { return "Two"; } }
}
public interface ISomeService
{
void DoSomething();
}
[Export("One",typeof(ISomeService))]
public class SomeService : ISomeService
{
public SomeService()
{
Console.WriteLine("Some Service Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called");
Console.ReadKey();
}
}
[Export("Two",typeof(ISomeService))]
public class SomeService1 : ISomeService
{
public SomeService1()
{
Console.WriteLine("Some Service1 Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called 1");
Console.ReadKey();
}
}
}
First case: By passing sessionModel to ComposeExportedValue you add a part of type SessionModel and not of ISomeService. To make this case work you nee to pass the service to ComposeExportedValue.
compositionContainer.ComposeExportedValue(sessionModel.SomeService);
Second case: In this case you leave the creation of parts to the container. The container can create new parts if there is either a parameter-less constructor or a constructor with parameters decorated with the ImportingConstructorAttribute. This most probably means that you will need to change your design a bit.
Personally I would go with the first case, but try to keep this to a minimum. After all the normal (and suggested) usage of MEF is letting the container create and handle parts.
I am using the following interface as a ToFactory() binding:
public interface ISamplerFactory
{
ISampler Create(Action<EventHandler<ValueChangedEventArgs>> register, Action<EventHandler<ValueChangedEventArgs>> unregister, Func<decimal?> valueGetter);
}
When I bind ToFactory() I can successfully create the class but I then get a memory leak whereby the register, unregister and valueGetter parameters are held by a ConstructorArgument inside Ninject, which reference a target/parameter object inside the delegates. This keeps that target object from getting GC'd. I am using ContextPreservation extension too if that makes a difference. (See complete sample code below)
When I remove the "ToFactory()" bind and create a standard factory class, it works.
public class SamplerFactory : ISamplerFactory
{
private readonly IDistributionResolver _resolverFactory;
public SamplerFactory(IDistributionResolverFactory resolverFactory)
{
_resolverFactory = resolverFactory;
}
ISampler Create(Action<EventHandler<ValueChangedEventArgs>> register, Action<EventHandler<ValueChangedEventArgs>> unregister, Func<decimal?> valueGetter)
{
return new SpecificSampler(_resolverFactory, register, unregister, valueGetter);
}
}
And my target objects inside the delegates are GC'd successfully.
Is there something I am doing wrong or isn't the Factory extension meant to handle these more complex arguments? I assume if I used the .WithConstructorArgument I would get the same outcome.
EDIT: Added all necessary bindings and rewrote my sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication
{
using System;
using Ninject;
using Ninject.Extensions.ContextPreservation;
using Ninject.Extensions.Factory;
public class Program
{
static void Main(string[] args)
{
new Program().Run();
}
public void Run()
{
var kernel = new StandardKernel(new NinjectSettings() { LoadExtensions = false });
kernel.Load(new FuncModule());
kernel.Load(new ContextPreservationModule());
kernel.Bind<IDistributionResolver>().To<DistributionResolver>(); // This is a constructor-less object.
kernel.Bind<ISampler>().To<SpecificSampler>();
kernel.Bind<ISamplerFactory>().ToFactory();
kernel.Bind<IInjected>().To<Injected>();
kernel.Bind<IDistributionResolver>().To<DistributionResolver>();
kernel.Bind<IDistributionResolverFactory>().ToFactory();
var s = new SomeObject();
var weakS = new WeakReference(s);
var factory = kernel.Get<ISamplerFactory>();
var sampler = CreateInstance(factory, s);
s = null;
factory = null;
sampler = null;
GC.Collect();
if (weakS.IsAlive)
throw new Exception();
}
private ISampler CreateInstance(ISamplerFactory factory, SomeObject someObject)
{
var x = factory.Create(y => someObject.Do += y, z => someObject.Do -= z, () => someObject.Query());
if (x == null)
throw new Exception();
return x;
}
public class SomeObject
{
public event EventHandler<ValueChangedEventArgs> Do;
public decimal? Query()
{
return 0;
}
}
public class SpecificSampler : ISampler
{
private readonly IDistributionResolverFactory resolver;
private readonly Action<EventHandler<ValueChangedEventArgs>> register;
private readonly Action<EventHandler<ValueChangedEventArgs>> unregister;
private Func<decimal?> _valueGetter;
public SpecificSampler(
IDistributionResolverFactory resolver, // This is injected
Action<EventHandler<ValueChangedEventArgs>> register, // The rest come from the factory inputs
Action<EventHandler<ValueChangedEventArgs>> unregister,
Func<decimal?> valueGetter)
{
this.resolver = resolver;
this.register = register;
this.unregister = unregister;
_valueGetter = valueGetter;
// Do Stuff;
}
}
public class ValueChangedEventArgs : EventArgs
{
}
public interface ISamplerFactory
{
ISampler Create(Action<EventHandler<ValueChangedEventArgs>> register, Action<EventHandler<ValueChangedEventArgs>> unregister, Func<decimal?> valueGetter);
}
public interface IDistributionResolverFactory
{
IDistributionResolver Create(IDictionary<string, string> picked);
}
public interface IDistributionResolver
{
}
private class DistributionResolver : IDistributionResolver
{
readonly IInjected _i;
readonly IDictionary<string, string> _picked;
public DistributionResolver(IInjected i, IDictionary<string, string> picked)
{
_i = i;
_picked = picked;
}
}
public interface ISampler
{
}
}
public interface IInjected
{
}
class Injected : IInjected
{
}
}
I tracked this down to a memory leak in Ninject Core:
https://github.com/ninject/ninject/issues/74
I have a dependency being injected via Func<Owned<OwnedDependency>>. One of its dependencies requires a parameter that I will only have at the point of constructing OwnedDependency.
public class OwnedDependency
{
public OwnedDependency(IDependency1 dependency)
{
}
}
public interface IDependency1
{
}
public class Dependency1 : IDependency1
{
public Dependency1(MyParameter parameter)
{
}
}
public class MyClass
{
private readonly Func<Owned<OwnedDependency>> m_ownedDependencyFactory;
public MyClass(Func<Owned<OwnedDependency>> ownedDependencyFactory)
{
m_ownedDependencyFactory = ownedDependencyFactory;
}
public void CreateOwnedDependency()
{
var parameter = new MyParameter(...);
// ** how to setup parameter with the container? **
using (var ownedDependency = m_ownedDependencyFactory())
{
}
}
}
I can't work out a clean way of setting up the instance of MyParameter.
One approach I have explored is to inject ILifetimeScope into MyClass and then do something like:
var parameter = new MyParameter(...);
using (var newScope = m_lifetimeScope.BeginLifetimeScope())
{
newScope.Resolve<IDependency1>(new TypedParameter(typeof(MyParameter), parameter));
var ownedDependency = newScope.Resolve<OwnedDependency>();
// ...
}
but the container is becoming unnecessarily intrusive. Ideally what I would like to do is inject Func<IDependency1, Owned<OwnedDependency>> and the container be willing to use parameters passed in to satisfy any necessary dependency, not just the ones on OwnedDependency.
What about doing the resolution in two steps with using another factory for IDependency1:
public class MyClass
{
private Func<MyParameter, IDependency1> dependency1Factory;
private Func<IDependency1, Owned<OwnedDependency>> ownedDependencyFactory;
public MyClass(
Func<MyParameter, IDependency1> dependency1Factory,
Func<IDependency1, Owned<OwnedDependency>> ownedDependencyFactory)
{
this.dependency1Factory = dependency1Factory;
this.ownedDependencyFactory = ownedDependencyFactory;
}
public void CreateOwnedDependency()
{
var parameter = new MyParameter();
using (var owned = ownedDependencyFactory(dependency1Factory(parameter)))
{
}
}
}