MEF isn't loading my plugin - c#

I've implemented a very small plugin system in C# with MEF. But my plugins won't be loaded. In the Aggregate-Catalog I can see my plugin listed. But, after I'll compose these parts, there isn't my plugin in the plugin list, what I'm doing wrong?
Here's a snippet of my code:
Plugin-Loader:
[ImportMany(typeof(IFetchService))]
private IFetchService[] _pluginList;
private AggregateCatalog _pluginCatalog;
private const string pluginPathKey = "PluginPath";
...
public PluginManager(ApplicationContext context)
{
var dirCatalog = new DirectoryCatalog(ConfigurationManager.AppSettings[pluginPathKey]);
//Here's my plugin listed...
_pluginCatalog = new AggregateCatalog(dirCatalog);
var compositionContainer = new CompositionContainer(_pluginCatalog);
compositionContainer.ComposeParts(this);
}
...
And here, the plugin itself:
[Export(typeof(IFetchService))]
public class MySamplePlugin : IFetchService
{
public MySamplePlugin()
{
Console.WriteLine("Plugin entered");
}
...
}

You're doing this wrong. The ImportMany attribute of the _pluginList field doesn't make any sense, since the the plugin manager instance will be created by you, not by the DI container.
You have to create another class that will import all your plugins.
[Export]
class SomeClass
{
readonly IFetchService[] pluginList;
[ImportingConstructor]
public SomeClass([ImportMany(typeof(IFetchService))]IFetchService[] pluginList)
{
this.pluginList = pluginList;
}
}
Now, you can let the DI container compose an instance of this SomeClass for you. You will see that its pluginList field contains your plugin reference.

Related

Dependency Injection into Roslyn analyzers

I have a custom Roslyn analyzer which inherits from DiagnosticAnalyzer. It is packed in VSIX extension which contains custom Package class. I'd like to pass an instance of a class with the settings (CodeAnalysisSettings instance) from the package to my DiagnosticAnalyzer.
I've tried to use MEF for that purpose. I've registered an instance of my settings class in VS Package using the following code:
protected override void Initialize()
{
base.Initialize();
IComponentModel componentModel = Package.GetGlobalService(typeof(SComponentModel)) as IComponentModel;
new CompositionContainer(componentModel.DefaultCatalog).ComposeExportedValue(
new CodeAnalysisSettings(...));
}
Analyzer looks as follows:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MyAnalyzer : DiagnosticAnalyzer
{
[Import]
public CodeAnalysisSettings Settings { get; set; }
}
Settings class:
[Export]
public class CodeAnalysisSettings
{
public CodeAnalysisSettings()
{
}
public bool RecursiveAnalysisEnabled { get; }
}
For some reason Settings property is not imported - its value is always null.
Please help.
I've ended up using a service locator (CommonServiceLocator package) that consumes MEF container as a source.
Since VS infrastructure doesn't allow adding new registrations (IComponentModel.DefaultCatalog throws an exception which tells that this functionality is not supported anymore), I've created a new container inside Package.Initialize():
var container = new CompositionContainer(CompositionOptions.Default, componentModel.DefaultExportProvider);
container.ComposeExportedValue<CodeAnalysisSettings>(new CodeAnalysisSettings(...));
var serviceLocator = new MefServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => serviceLocator);
Roslyn diagnostic consumes this dependency:
var settings = CodeAnalysisSettings.Default;
if (ServiceLocator.IsLocationProviderSet)
settings = ServiceLocator.GetInstance<CodeAnalysisSettings>();

MEF: Creating a ViewModel in my UserControls

I am currently converting my WPF/MVVM application from Ninject to MEF to take advantage of some plugin architecture. There is no Prism or Unity, nor do I want to go down that path. I'm using VS2015 and .Net 4.6.
I was using the technique that was popular with MVVM Light where you instantiate the ViewModel inside of the XAML with Ninject.
public static ImageViewModel ImageVM => Kernel.Get<ImageViewModel>();
But now that I'm moving to MEF, I would like to see if there are some alternatives. Most of the currently answered posts on Stack Exchange are fairly old and I'm hoping there are some new alternatives available now that .NET 4.5 is available.
tl;dr
I have a Window that contains 10 UserControls. Each user control has a new instance of a ViewModel attached to it. Since the window is creating the user controls in xaml, how can I get a unique instance of my ViewModel into each control?
public partial class FingerprintControl{
public FingerprintControl() {
InitializeComponent();
}
[Import]
public FingerprintControlViewModel ViewModel
{
get { return DataContext as FingerprintControlViewModel; }
set { DataContext = value; }
}
One suggestion that I saw said to add
CompositionInitializer.SatisfyImports(this);
after the InitializeComponent(). But that is a Silverlight only class.
I looked at https://www.nuget.org/packages/Microsoft.Composition/ but the documentation for MEF 2 is just incredibly non-existent on the site.
I also saw that ExportFactory was added to MEF 2, but not sure if that would help either.
I did find in MEF 2 the static method CompositionContextExtensions.SatisfyImports, but I don't know what to do with it. The documentation only says, "Satisfies the imports of the specified object from the specified context." (Not real useful...)
We use a class wrapper for Mef with static methods for all our apps:
public class Mef
{
private static CompositionContainer container = null;
public static CompositionContainer Container { get { return container; } }
private static AggregateCatalog catalog;
public static void Initialize()
{
catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(path: ".", searchPattern: "*.exe"));
catalog.Catalogs.Add(new DirectoryCatalog(path: ".", searchPattern: "*.dll"));
container = new CompositionContainer(catalog);
StartWatch();
}
private static void StartWatch()
{
var watcher = new FileSystemWatcher() { Path = ".", NotifyFilter = NotifyFilters.LastWrite };
watcher.Changed += (s, e) =>
{
string lName = e.Name.ToLower();
if (lName.EndsWith(".dll") || lName.EndsWith(".exe"))
Refresh();
};
watcher.EnableRaisingEvents = true;
}
public static void Refresh()
{
foreach (DirectoryCatalog dCatalog in catalog.Catalogs)
dCatalog.Refresh();
}
}
(Note: We use the above to dynamically load plugins on-demand into our app as required by the user. You may want to use a different catalog system - several to choose from)
Then we initialize the class early on in the app life-cycle, usually in the App.Xaml code-behind constructor:
public App()
{
this.InitializeComponent();
Mef.Initialize();
}
and when I have a base-level mef-import, in the class/code-behind constructor call:
Mef.Container.SatisfyImports(this);
Hope this helps.

Load Prism module with stand-alone module manager

The WPF application I am working on will have numerous add-ins to extend its functionality. Each add-in will consist of one or several assemblies (usually a "main" add-in assembly with separate assemblies for presentation-layer components and views) but will be treated by the application as a single Prism module. Prism will be used to discover and load the add-ins, with MEF used to ensure that the Prism module can access dependencies in the add-in's related assemblies. The Prism module's Initialize method will be responsible for, amongst other things, configuring the IoC container (in this case, Unity). In a deployed scenario, this will all be loaded and managed with a MefBootstrapper at startup.
My problem occurs when trying to unit test an add-in. To keep the add-in code semi-isolated from the main application, each add-in will also have its own unit test assemblies. One of these test assemblies will be responsible for checking registration of services with the IoC container. In the test scenario, I don't want to use a bootstrapper to load the Prism modules as they have dependencies that I don't want to introduce to my test assemblies. I have therefore written my test fixture base class such that it creates its own MefModuleManager to load the module to be tested.
ResolutionTestBase.cs
public abstract class ResolutionTestBase
{
[ClassInitialize]
public static void TestFixtureInitialise(TestContext context)
{
// Create the main resolution container.
var container = new UnityContainer();
// Install the service locator.
var locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
}
// Here go some helper methods for performing resolution tests.
protected IUnityContainer Container
{
get { return ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer; }
}
}
AddInResolutionTestBase.cs
public abstract class AddInResolutionTestBase:ResolutionTestBase
{
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
var aggregateCatalog = new AggregateCatalog();
foreach (var testAssembly in TestAssemblies)
{
aggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
// Load module manager.
var container = new CompositionContainer(aggregateCatalog);
var serviceLocator = new MefServiceLocatorAdapter(container);
var parts = new DownloadedPartCatalogCollection();
var moduleInitialiser = new MefModuleInitializer(serviceLocator, Logger, parts, aggregateCatalog);
var moduleManager = new MefModuleManager(moduleInitialiser, ModuleCatalog, Logger);
moduleManager.ModuleTypeLoaders = new[] { new MefFileModuleTypeLoader() };
moduleManager.Run();
}
protected static ILoggerFacade Logger { get; private set; }
protected abstract IModuleCatalog ModuleCatalog { get; }
protected abstract IEnumerable<Assembly> TestAssemblies { get; }
}
For the add-ins, they have a stand-alone class in their main assembly to implement the requirements of a Prism module, and Unity extensions to configure the container.
Module.cs
[ModuleExport("AddInModule", typeof(Module), InitializationMode = InitializationMode.OnDemand)]
public class Module : IModule
{
private readonly IEnumerable<IUnityContainerExtensionConfigurator> _extensions;
[ImportingConstructor]
public Module([ImportMany]IEnumerable<IUnityContainerExtensionConfigurator> extensions)
{
_extensions = extensions;
}
public void Initialize()
{
// Load the dependency injection container.
var container = ServiceLocator.Current.GetService(typeof(IUnityContainer)) as IUnityContainer;
if (container != null)
{
foreach (var extension in _extensions)
{
container.AddExtension((UnityContainerExtension) extension);
}
}
}
}
ContainerInstallerExtension.cs (in the add-in's main assembly)
[Export(typeof(IUnityContainerExtensionConfigurator))]
public class ContainerInstallerExtension : UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}
PresentationInstallerExtension.cs (in the add-in's presentation assembly)
[Export(typeof(IUnityContainerExtensionConfigurator))]
public class PresentationInstallerExtension:UnityContainerExtension
{
protected override void Initialize()
{
// perform container configuration here.
}
}
AddInResolutionTest.cs (in the add-in's IoC test assembly)
[TestClass]
public class AddInResolutionTest : AddInResolutionTestBase
{
private IEnumerable<Assembly> _testAssemblies;
private IModuleCatalog DoGetModuleCatalog()
{
var moduleInfo = new ModuleInfo("AddInModule", typeof (Module).AssemblyQualifiedName)
{
InitializationMode = InitializationMode.WhenAvailable,
Ref = typeof (Module).Assembly.CodeBase
};
return new ModuleCatalog(new[] {moduleInfo});
}
protected override IModuleCatalog ModuleCatalog
{
get { return DoGetModuleCatalog(); }
}
protected override IEnumerable<Assembly> TestAssemblies
{
get { return _testAssemblies ?? (_testAssemblies = new[] { typeof(ContainerInstallerExtension).Assembly, typeof(PresentationInstallerExtension).Assembly }); }
}
[TestMethod]
public void ResolveSomeService()
{
// perform resolution test here.
}
}
Of note with the resolution test fixture, the "test assemblies" are linked to the IoC test assembly with project references and referred to directly by type (rather than using a directory-scan catalog) so I could avoid having to use a post-build event to copy assemblies to a common folder for testing.
When I run the unit tests (as-is), I get an exception indicating the module manager failed to load the Prism module:
Initialization method AddInResolutionTest.TestInitialise threw exception. Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Failed to load type for module AddInModule.
If this error occurred when using MEF in a Silverlight application, please ensure that the CopyLocal property of the reference to the MefExtensions assembly is set to true in the main application/shell and false in all other assemblies.
Error was: Object reference not set to an instance of an object.. ---> System.NullReferenceException: Object reference not set to an instance of an object..
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo)
--- End of inner exception stack trace ---
at Microsoft.Practices.Prism.Modularity.ModuleManager.HandleModuleTypeLoadingError(ModuleInfo moduleInfo, Exception exception)
at Microsoft.Practices.Prism.Modularity.ModuleManager.IModuleTypeLoader_LoadModuleCompleted(Object sender, LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(LoadModuleCompletedEventArgs e)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.RaiseLoadModuleCompleted(ModuleInfo moduleInfo, Exception error)
at Microsoft.Practices.Prism.MefExtensions.Modularity.MefFileModuleTypeLoader.LoadModuleType(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.BeginRetrievingModule(ModuleInfo moduleInfo)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModuleTypes(IEnumerable`1 moduleInfos)
at Microsoft.Practices.Prism.Modularity.ModuleManager.LoadModulesWhenAvailable()
at Microsoft.Practices.Prism.Modularity.ModuleManager.Run()
at AddInResolutionTestBase.TestInitialise() in AddInResolutionTestBase.cs: line xx
At the point of calling moduleManager.Run() nothing in my code is null so it is not clear to me what the "real" problem is.
I have tried various changes to resolve the problem including:
calling moduleManager.LoadModule() instead of moduleManager.Run() in AddInResolutionTestBase.cs
manipulating the State of the ModuleInfo created in AddInResolutionTest to bypass the problem in the module manager
Any other changes I've made have resulted in different errors, but still indicate a problem with the module manager trying to load the Prism module.
Is there some additional step required to correctly configure the module manager to be able to load modules in this way, bearing in mind that some of the usual overhead (such as the logger) is not required for the unit tests?
With the aid of a de-compiler, I was able to figure out the "missing pieces" and make some changes to ensure that all the required components are registered/installed for the module manager to be able to initialise the module(s) for testing. For anyone who is interested:
public abstract class AddInResolutionTestBase:ResolutionTestBase
{
private CompositionContainer _container;
private IModuleCatalog _moduleCatalog;
private IEnumerable<object> _testEntities;
private IEnumerable<ModuleInfo> _testModuleInformation;
static AddInResolutionTestBase()
{
Logger = new EmptyLogger();
}
[TestInitialize]
public virtual void TestInitialise()
{
// Create MEF catalog.
AggregateCatalog = CreateAggregateCatalog();
ConfigureAggregateCatalog();
AggregateCatalog = DefaultPrismServiceRegistrar.RegisterRequiredPrismServicesIfMissing(AggregateCatalog);
ConfigureContainer();
// Initialise modules to be tested.
CompositionContainer.GetExportedValue<IModuleManager>().Run();
}
#region Protected Methods
protected virtual void ConfigureAggregateCatalog()
{
var testAssemblies = TestEntities.OfType<Assembly>();
foreach (var testAssembly in testAssemblies)
{
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(testAssembly));
}
if (TestEntities.Any(entity => entity is System.Type))
{
var catalog = new TypeCatalog(TestEntities.OfType<System.Type>());
AggregateCatalog.Catalogs.Add(catalog);
}
}
protected virtual void ConfigureContainer()
{
CompositionContainer.ComposeExportedValue<ILoggerFacade>(Logger);
CompositionContainer.ComposeExportedValue<IModuleCatalog>(ModuleCatalog);
CompositionContainer.ComposeExportedValue<IServiceLocator>(new MefServiceLocatorAdapter(CompositionContainer));
CompositionContainer.ComposeExportedValue<AggregateCatalog>(AggregateCatalog);
}
protected virtual AggregateCatalog CreateAggregateCatalog()
{
return new AggregateCatalog();
}
protected virtual CompositionContainer CreateContainer()
{
return new CompositionContainer(AggregateCatalog);
}
protected virtual IModuleCatalog CreateModuleCatalog()
{
return new ModuleCatalog(TestModuleInformation);
}
protected abstract IEnumerable<object> GetTestEntities();
protected abstract IEnumerable<ModuleInfo> GetTestModuleInformation();
#endregion
#region Protected Properties
protected AggregateCatalog AggregateCatalog { get; set; }
protected CompositionContainer CompositionContainer
{
get { return _container ?? (_container = CreateContainer()); }
}
protected static ILoggerFacade Logger { get; private set; }
protected IModuleCatalog ModuleCatalog
{
get { return _moduleCatalog ?? (_moduleCatalog = CreateModuleCatalog()); }
}
protected IEnumerable<object> TestEntities
{
get { return _testEntities ?? (_testEntities = GetTestEntities()); }
}
protected IEnumerable<ModuleInfo> TestModuleInformation
{
get { return _testModuleInformation ?? (_testModuleInformation = GetTestModuleInformation()); }
}
#endregion
}
This test base class now mimics to some extent what normally goes on in the boostrapper when the application normally starts. The (resolution) tests in each of the add-ins now only need to provide a list of the (exported) container extensions and module information for the Prism module that represents the add-in (in addition to the actual resolution tests!)

More than one export was found that matches the constraint for non ICollection<T>

I looked at several other other posts that talk about using:
[ImportMany]
IEnumerable<IPlugin> plugins;
but in my case it is not a collection.
Please see below for my code and error message:
[Export(typeof(ITransactionActionMasterDao))]
public class TransactionActionMasterDao : BaseDao, ITransactionActionMasterDao
public abstract class BaseTransactionGenerator : ITransactionGenerator
{
[Import(typeof(ITransactionActionMasterDao))]
private ITransactionActionMasterDao transactionActionMasterDao;
public BaseTransactionGenerator()
{
//Grab all the assemblies in the current folder
AggregateCatalog catalog = new AggregateCatalog(new AssemblyCatalog( typeof(TransactionActionMasterDao).Assembly) );
CompositionContainer container = new CompositionContainer(catalog);
// This will set the fields (aka execute the [Import])
**//below line getting error**
container.ComposeParts(this);
}
}
public class ManualTransactionGenerator : BaseTransactionGenerator
{
public ManualTransactionGenerator():base()
{
}
}
ManualTransactionGenerator and BaseTransactionGenerator are both in one same C# class library project.
TransactionGeneratorsIntegrationTest is present in another UnitTest project
public class TransactionGeneratorsIntegrationTest
{
[TestMethod]
public void IT_GenerateManualTransactions()
{
ManualTransactionGenerator generator = new ManualTransactionGenerator()
}
}
Error Message:
The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) More than one export was found that matches the constraint:
ContractName HL.DataAccess.Adjustment.Contracts.DataAccessObjects.ITransactionActionMasterDao
RequiredTypeIdentity HL.DataAccess.Adjustment.Contracts.DataAccessObjects.ITransactionActionMasterDao
UPDATE:
We changed our design and new code looks like this
[Export(typeof(ITransactionActionMasterDao))]
public class TransactionActionMasterDao : BaseDao, ITransactionActionMasterDao
[Export(typeof(ITransactionActionMasterDao))]
public class CustomerTransactionActionMasterDao : BaseDao, ITransactionActionMasterDao
public abstract class BaseTransactionGenerator : ITransactionGenerator
{
[Import(typeof(ITransactionActionMasterDao))]
private ITransactionActionMasterDao transactionActionMasterDao;
public BaseTransactionGenerator()
{
**//Below lines will throw error**
AggregateCatalog catalog = new AggregateCatalog(new AssemblyCatalog( typeof(TransactionActionMasterDao).Assembly),
new AssemblyCatalog( typeof(CustomerTransactionActionMasterDao ).Assembly) );
**//This is correct. We need to add ONLY one. Not both**
AggregateCatalog catalog = new AggregateCatalog(new AssemblyCatalog( typeof(TransactionActionMasterDao).Assembly);
CompositionContainer container = new CompositionContainer(catalog);
// This will set the fields (aka execute the [Import])
**//below line getting error**
container.ComposeParts(this);
}
}
This solved our issue.
You may be encountering a problem with side-by-side loading of multiple versions of the assembly containing the [Export] attribute. To test this theory, try the following:
Change the [Import] to an [ImportMany].
If (when) you observe more than one item in the list, examine each instance to determine the location of the assembly it was loaded from.
transactionActionMasterDao[i].GetType().Assembly.Location
If this is the case, you may need to use assembly binding redirection to ensure only one copy of the assembly containing the export is loaded.

MEF Constructor Injection

I'm trying to figure out MEF's Constructor Injection attribute. I have no idea how I tell it to load the constructor's parameters.
This is the property I'm trying to load
[ImportMany(typeof(BUsers))]
public IEnumerable<BUsers> LoadBUsers { get; set; }
Here is the code I'm using to import the assemblies.
try
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()));
catalog.Catalogs.Add(new DirectoryCatalog("DI"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
Here is the class I'm trying to load
[Serializable]
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
When you use the ImportingConstructor attribute, the parameters to the constructor become imports. By default, what you are importing (the contract name) is based on the type of the parameter or property that your are importing into. So in this case the contract type for both your imports is string, and there's no real difference between the first and second parameter.
It looks like you are trying to use imports to supply configuration values, which isn't necessarily what it was designed for. To get it to do what you want, you should override the contract name for each of the parameters, like this:
[ImportingConstructor]
public EditProfile([Import("Method")] string Method, [Import("Version")] string Version)
{ }
Then you need exports for Method and Version in your container. One way to do this is just to add them directly:
var container = new CompositionContainer(catalog);
container.ComposeExportedValue("Method", "MethodValue");
container.ComposeExportedValue("Version", "2.0");
container.ComposeParts(this);
(Note that ComposeExportedValue is actually an extension method defined on the static AttributedModelServices class.)
If you want to read these values from a configuration file of some sort, you could create your own export provider which reads the configuration and provides the values in it as exports to the container.
An alternative way to handle this would be to just import an interface that provides access to the configuration values by name, and get the values you need from the body of the constructor.
I like Daniel's solution; however, only one thing I see is the tight coupling of parameter names between the actor (who creates CompopositionContrainer()) and Export part with [ImportingConstructor] for customized CTOR. For example, "Method" has two be matched in both places. It makes hard to maintain the Export part if the actor and Export part are in difference projects.
If it is possible, I would add the second CTOR to the Export part class. For example:
[Export(typeof(BUsers))]
public class EditProfile : BUsers
{
[ImportingConstructor]
public EditProfile(EditProfileParameters ctorPars)
: this(ctorPars.Method, ctorPars.Version) {}
public EditProfile(string Method, string Version)
{
Version = "2";
Action = "Edit";
TypeName = "EditProfile";
}
The class of EditProfileParameters should be straightforward: two properties of Method and Version:
[Export]
public class EditProfileParameters{
public string Method { get; set; }
public string Version { get; set; }
}
The key point is to add Export attribute to the class. Then MEF should be able to map this class to the parameter of EditProfile's CTOR.
Here is example to add the Export part to container:
var container = new CompositionContainer(catalog);
var instance1 = new EditProfileParameters();
// set property values from config or other resources
container.ComposeExportedValue(instance1);
container.ComposeParts(this);
Although late to the game, here's another approach that leverages a lesser-known feature of MEF: Property Exports
public class ObjectMother
{
[Export]
public static EditProfile DefaultEditProfile
{
get
{
var method = ConfigurationManager.AppSettings["method"];
var version = ConfigurationManager.AppSettings["version"];
return new EditProfile(method,version);
}
}
}
No usages are required for ObjectMother for this to work, and no attributes are required on EditProfile.

Categories

Resources