System.ComponentModel.Composition.ImportCardinalityMismatchException
'No exports were found that match the constraint: ContractName IntroToMEF.DigitalMonitor
RequiredTypeIdentity IntroToMEF.IMonitor'
'
My types are correct. I am able to Get exported value if I have only one instance inheriting from IMonitor. Whenever I need to use contract name I get an error. I am out of ideas.
Clearing Visual Studio Cache does not work.
[Export(typeof(IMonitor))]
public class DigitalMonitor : IMonitor
{
public void start()
{
Console.WriteLine("Digital Monitor: Started monitoring");
}
}
internal class HostMefInApp
{
[Import]
public ILogger logger { get; set; }
[Import]
public IMonitor monitor { get; set; }
private void ComposeMultiple()
{
Console.WriteLine("DEBUG:" + GetType().Assembly);
var assemblyCatalog = new AssemblyCatalog(GetType().Assembly);
var container = new CompositionContainer(assemblyCatalog);
// var contractName = AttributedModelServices.GetContractName(typeof(FileLogger));
try
{
var instancesOfFileLogger = container.GetExports<ILogger>(); //"IntroToMEF.FileLogger"
}
catch (ImportCardinalityMismatchException ex)
{
Console.WriteLine(ex);
}
CompositionBatch batch = new CompositionBatch();
batch.AddPart(AttributedModelServices.CreatePart(new FileLogger()));
batch.AddPart(AttributedModelServices.CreatePart(new DigitalMonitor()));
batch.AddPart(AttributedModelServices.CreatePart(new AnalogMonitor()));
container.Compose(batch);
var digitalMonitorContractName = AttributedModelServices.GetContractName(typeof(DigitalMonitor));
var currentLogger = container.GetExportedValue<ILogger>();
var digitalMonitor = container.GetExportedValue<IMonitor>(digitalMonitorContractName);
logger = currentLogger;
monitor = digitalMonitor;
}
I think that you should:
[Export(typeof(IMonitor))]
[Export(typeof(DigitalMonitor))]
public class DigitalMonitor : IMonitor
{
public void start()
{
Console.WriteLine("Digital Monitor: Started monitoring");
}
}
meaning, export DigitalMonitor as IMonitor and DigitalMonitor. This will give you possibility to import it using both ways. Then of course your code will work with:
var digitalMonitor = container.GetExportedValue<DigitalMonitor>();
Some time ago I worked on a project that I THINK used LightInject. I no longer have access, so I can't just go look for myself. It seemed like once the ServiceContainer was instantiated, something triggered reflection across all assemblies, and any properties of a certain interface type were automatically instantiated. Something like this:
A C# class library that contains a logger class; the logger is what should be injected.
namespace Common {
public interface ILogger { void Log(string msg); }
public class Logger : ILogger {
public Logger() { }
public void Log(string msg) { Console.WriteLine(msg); }
}
}
A C# console app that references the class library. Some things that didn't seem to help are commented out.
namespace TestLightInject {
class Program {
private static ServiceContainer container;
static void Main(string[] args) {
container = new ServiceContainer();
//container.EnableAnnotatedPropertyInjection();
container.Register<ILogger, Logger>();
//container.RegisterPropertyDependency<ILogger>((factory, propertyInfo) => new Logger());
var worker = new Worker();
worker.DoSomething();
}
}
public class Worker {
//[Inject]
ILogger logger { get; set; } = null; // THIS IS THE PROPERTY THAT NEEDS TO BE SET
public Worker() { }
public void DoSomething() { logger.Log("It works!"); }
}
}
I guess I could allow public access to the service container, and change the Worker ctor to something like
public Worker() { logger = Program.container.GetInstance<ILogger>(); }
but it was simpler when any ILogger property was automatically instantiated.
Is there a way to do this with LightInject, or was it some other DI framework that did it? Or am I just imagining it all?
public class BaseSteps : Steps
{
[BeforeFeature]
public static void BeforeFeatureStep()
{
var otherStep = new OtherStep();
otherStep.ExecuteStep();
}
}
public class OtherStep : Steps
{
public void ExecuteStep()
{
var key = 'key';
var val = 'val';
this.FeatureContext.Add(key, val);
}
}
This is a sample snippet. When I try to access this.FeatureContext.Add(), I get an exception stating Container of the steps class has not been initialized
Any help on this is appreciated.
The FeatureContext is not initialized, because the Step class is not resolved by the SpecFlow DI Container. So the SetObjectContainer method is not called (https://github.com/techtalk/SpecFlow/blob/master/TechTalk.SpecFlow/Steps.cs#L10).
As a general rule, you should not instantiate the steps classes on your own, but get them via Context Injection (http://specflow.org/documentation/Context-Injection).
But that is not possible in your case because you are in a BeforeFeature hook.
A possible solution would be, that you use the latest pre-release of SpecFlow (https://www.nuget.org/packages/SpecFlow/2.2.0-preview20170523).
There you can get the FeatureContext via a parameter in the hook method.
It looks like this:
[BeforeFeature]
public static void BeforeFeatureHook(FeatureContext featureContext)
{
//your code
}
Your code could then look like this:
public class FeatureContextDriver
{
public void FeatureContextChanging(FeatureContext featureContext)
{
var key = 'key';
var val = 'val';
featureContext.Add(key, val);
}
}
[Binding]
public class BaseSteps : Steps
{
[BeforeFeature]
public static void BeforeFeatureStep(FeatureContext featureContext)
{
var featureContextDriver = new FeatureContextDriver();
featureContextDriver.FeatureContextChanging(featureContext);
}
}
[Binding]
public class OtherStep : Steps
{
private FeatureContextDriver _featureContextDriver;
public OtherStep(FeatureContextDriver featureContextDriver)
{
_featureContextDriver = featureContextDriver;
}
public void ExecuteStep()
{
_featureContextDriver.FeatureContextChanging(this.FeatureContext);
}
}
Code is not tested/tried out and applies the Driver Pattern.
Full Disclosure: I am one of the maintainers of SpecFlow and SpecFlow+.
I want to resolve some dependencies which will only be known at runtime. I am using configuration file to configure Unity (not programmatically).
Here is some code to show what I want to achieve.
Classes:
internal class WorkflowFactory : IWorkflowFactory
{
public IItemWorkflow GetWorkflow(string discriminator)
{
// return an implementation of IItemWorkflow as specified in config file
return null;
}
}
public interface IWorkflowFactory
{
IItemWorkflow GetWorkflow(string discriminator);
}
public interface IItemWorkflow
{
void Handle(int id);
// More methods
}
Usage:
internal class Program
{
private static void Main(string[] args)
{
IWorkflowFactory factory = new WorkflowFactory();
// I am using args to show I do not know the string until runtime
var wf1 = factory.GetWorkflow(args[0]);
var wf2 = factory.GetWorkflow(args[1]);
}
}
If you know a better way, I am totally open and invite suggestions.
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.