I'm thinking of using MEF to solve a plugin management requirement. In the blurb it says "no hard dependencies" but as far as I can see, there is a hard dependency on the import/export interface.
My concern is this. My extendable app is written by me. Plugins are written by third parties. So lets say we all start off with V1. My app defines a IPlugin interface that the plugin 'parts' need to implement. We deploy the app and users install a bunch of third party plugins. All well and good.
Now I upgrade my app and I want to add a new method to the plugin interface. The way I see it I have 2 choices:
Edit the interface - probably bad, and this would break existing plugins because they would no longer correctly implement the interface.
Create a new 'V2' interface, that inherits from the original
public interface IPluginV2 : IPlugin {}
Now I have a problem. My users all have a bunch of 3rd party plugins implementing IPlugin, but I now require them to implement IPluginV2. I presume these 3rd party plugins will no longer work, until the developers implement the new interface.
Does MEF have a way to handle this situation? I'm really looking for a way that lets me evolve my app while having old plugins continue to work without having to be rebuilt. Whats the best way of handling that?
For versioning, you will probably want an interface for each version and the adapter pattern to go between them. It is how System.AddIn handles versioning, and it works for MEF, too.
Let's say we have the following types for the V1 of your application:
public interface IPlugin
{
string Name { get; }
string Publisher { get; }
string Version { get; }
void Init();
}
This is the only contract for our V1 plugin-aware app. It is contained in assembly Contracts.v1.
Then we have a V1 plugin:
[Export(typeof(IPlugin))]
public class SomePlugin : IPlugin
{
public string Name { get { return "Some Plugin"; } }
public string Publisher { get { return "Publisher A"; } }
public string Version { get { return "1.0.0.0"; } }
public void Init() { }
public override string ToString()
{
return string.Format("{0} v.{1} from {2}", Name, Version, Publisher);
}
}
Which is exported as IPlugin. It is contained in assembly Plugin.v1 and is published on the "plugins" folder under the application base path of the host.
Finally the V1 host:
class Host : IDisposable
{
CompositionContainer _container;
[ImportMany(typeof(IPlugin))]
public IEnumerable<IPlugin> Plugins { get; private set; }
public Host()
{
var catalog = new DirectoryCatalog("plugins");
_container = new CompositionContainer(catalog);
_container.ComposeParts(this);
}
public void Dispose() { _container.Dispose(); }
}
which imports all IPlugin parts found in folder "plugins".
Then we decide to publish V2 and because we want to provide versioning we will need versionless contracts:
public interface IPluginV2
{
string Name { get; }
string Publisher { get; }
string Version { get; }
string Description { get; }
void Init(IHost host);
}
with a new property and a modified method signature. Plus we add an interface for the host:
public interface IHost
{
//Here we can add something useful for a plugin.
}
Both of these are contained in assembly Contracts.v2.
To allow versioning we add a plugin adapter from V1 to V2:
class V1toV2PluginAdapter : IPluginV2
{
IPlugin _plugin;
public string Name { get { return _plugin.Name; } }
public string Publisher { get { return _plugin.Publisher; } }
public string Version { get { return _plugin.Version; } }
public string Description { get { return "No description"; } }
public V1toV2PluginAdapter(IPlugin plugin)
{
if (plugin == null) throw new ArgumentNullException("plugin");
_plugin = plugin;
}
public void Init(IHost host) { plugin.Init(); }
public override string ToString() { return _plugin.ToString(); }
}
This simply adapts from IPlugin to IPluginV2. It returns a fixed description and in the Init it does nothing with the host argument but it calls the parameterless Init from the V1 contract.
And finally the V2 host:
class HostV2WithVersioning : IHost, IDisposable
{
CompositionContainer _container;
[ImportMany(typeof(IPluginV2))]
IEnumerable<IPluginV2> _pluginsV2;
[ImportMany(typeof(IPlugin))]
IEnumerable<IPlugin> _pluginsV1;
public IEnumerable<IPluginV2> Plugins
{
get
{
return _pluginsV1.Select(p1 => new V1toV2PluginAdapter(p1)).Concat(_pluginsV2);
}
}
public HostV2WithVersioning()
{
var catalog = new DirectoryCatalog("plugins");
_container = new CompositionContainer(catalog);
_container.ComposeParts(this);
}
public void Dispose() { _container.Dispose(); }
}
which imports both IPlugin and IPluginV2 parts, adapts each IPlugin into IPluginV2 and exposes a concatenated sequence of all discovered plugins. After the adaptation is completed, all plugins can be treated as V2 plugins.
You can also use the adapter pattern on the interface of the host to allow V2 plugins to work with V1 hosts.
Another approach would be the autofac IoC that can integrate with MEF and can support versioning using adapters.
Just a couple of suggestions (which I have not tested) that may help brainstorming a solution for you:
If using MEF, use different AggregateCatalog for each of the versions. That way you could maintain both V1 and V2 plugins available
If not using MEF, a dynamic loaded DLL from the third party should return the current interface version it has implemented and you can choose which calls you could make depending on the version number
Did that help?
Cheers, dimamura
Related
I was using CastleWindsor in my ASP.NETCore2.2 WebAPI project and was working fine. I'm migrating to ASP.NETCore3.1 now and it doesn't look like CastleWindor has offical support for that so I decided to move to Autofac with minimal changes but having some issues resolving the dependencies.
In my project, I've maintained very loose coupling between different layers in the application namely, business layer, data layer, and translation layer. All of those layers are in their own assemblies. And then in my main project, I've a folder say "dependencies" which will hold all the DLLs of differnet layers. Additionally, I've a separate project that lists all the interfaces that are implemented by the different layers and which needs to be resolved by the IoC container.
The project having all the interfaces looks like this:
namespace Shared.Interfaces
{
public interface IBusinessLayer<T>
{
....
}
public interface IDataLayer<T>
{
....
}
public interface ITranslationLayer<T>
{
....
}
}
The implementing projects looks like this:
namespace POC.Person.BusinessLayer
{
public class BusinessLayer<T> : IBusinessLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.DataLayer
{
public class DataLayer<T> : IDataLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.TranslationLayer
{
public class TranslationLayer<T> : ITranslationLayer<T> where T : Models.Person
{
...
}
}
Using Autofac in my migrated .netcore3.1 project, Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//and other codes
}
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new DependencyResolver());
}
DependencyResolver is a class that inherits from Autofac.Module, which is again in a separate assembly in different project which looks like this:
namespace IOC.Autofac
{
public class DependencyResolver: Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
// get our path to dependencies folder in the main project
var path = Directory.GetCurrentDirectory() + "\\dependencies\\";
//get all the assemblies inside that folder
List<Assembly> assemblies = new List<Assembly>();
foreach (string assemblyPath in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories))
{
var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
assemblies.Add(assembly);
}
// Register and resolve the types with the container
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsClosedTypesOf(typeof(IBusinessLayer<>))
.AsClosedTypesOf(typeof(IDataLayer<>))
.AsClosedTypesOf(typeof(ITranslationLayer<>))
.AsImplementedInterfaces()
.InstancePerRequest();
}
}
}
I'm getting this error and I've not been able to fix it:
":"Unable to resolve service for type 'Shared.Interfaces.IBusinessLayer`1[Models.Person]' while attempting to activate 'POC.Person.Controllers.PersonController'.","
Inside my controller I've injection which looks like this:
namespace POC.Person.Controllers
{
public class PersonController : ControllerBase
{
private readonly IBusinessLayer<Models.Person> _bl;
public PersonController(IBusinessLayer<Models.Person> bl)
{
_bl = bl;
}
//other codes
}
}
Program.cs looks like this:
namespace POC.Person
{
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
host.Build().Run();
}
public static IHostBuilder BuildWebHost(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.UseStartup<Startup>()
.UseIIS()
.UseIISIntegration();
;
}).ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
});
}
}
}
It looks like with autofac involving generics, registering and resolving the type is not that straight forward?
Autofac does not currently support registering open generics whilst assembly scanning. It's a long-running known issue. You can do assembly scanning, you can register open generics, you can't do both at the same time. There are some ideas in that linked issue on ways some folks have solved it.
Out of the box, the scanning logic would, thus, be reduced to:
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsImplementedInterfaces()
.InstancePerRequest();
You need to register generics separately, like:
builder
.RegisterGeneric(typeof(TranslationLayer<>))
.As(typeof(ITranslationLayer<>));
I've got three projects:
UI
PluginManager
PluginOne
PluginTwo
Inside my Plugin Manager a create a simple plugin interface:
public interface IPlugin<T>
{
void sayMessage(T message);
T createMessage();
}
So, in my other projects I've two IPlugin implementation:
In porject PluginOneProject -->
-------------------------------
public class PluginOne : IPlugin<IntMessage>
{
public void sayMessage(IntMessage message)
{
System.Console.WriteLine(message.ToString());
}
}
where:
public class IntMessage
{
private int message;
public IntMessage(int message)
{
this.message = message;
}
public override string ToString()
{
return this.message.ToString();
}
}
In porject PluginTwoProject -->
-------------------------------
public class PluginTwo : IPlugin<StringMessage>
{
public void sayMessage(StringMessage message)
{
System.Console.WriteLine(message.ToString());
}
}
where:
public class StringMessage
{
private String message;
public StringMessage(String message)
{
this.message = message;
}
public override string ToString()
{
return this.message.ToString();
}
}
Obviously, I've added the corresponding project references.
So, in my UI porject I've NInject, and I perform this convention mapping:
kernel.Bind(b => b.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom(typeof(IPlugin<>))
.BindAllInterfaces()
);
The graph is built correctly.
So, I don't know how to get an instance of PluginOne, or PluginTwo from UI project.
I'm trying to use this code, however, I've a problem with generic interfaces...
foreach (IPlugin<?> plugin in kernel.GetAll(typeof(IPlugin<>)))
{
plugin.sayMessage(plugin.createMessage());
}
Unfortunately, you must request a specific interface from Ninject, so you must request either IPlugin<IntMessage> or IPlugin<StringMessage>.
I would suggest trying to refactor your app so that you can request a more generic interface, such as IPlugin, and/or create a message interface like IMessage that each message implements. It's not clear from your question what you're trying to accomplish.
I'm building a small Nancy web project.
In a method of one of my classes (not a nancy module), I would like to basically do:
var myThing = TinyIoC.TinyIoCContainer.Current.Resolve<IMyThing>();
However, there is only one registration in .Current (non public members, _RegisteredTypes) which is:
TinyIoC.TinyIoCContainer.TypeRegistration
Naturally, in my above code, I'm getting:
Unable to resolve type: My.Namespace.IMyThing
So, I guess I'm not getting the same container registered in my bootstrapper?
Is there a way to get at it?
EDIT
To flesh out a bit more of what I'm trying to do:
Basically, my url structure looks something like:
/{myType}/{myMethod}
So, the idea being, going to: /customer/ShowAllWithTheNameAlex would load the Customer service, and execute the showAllWithTheNameAlex method
How I do this is:
public interface IService
{
void DoSomething();
IEnumerable<string> GetSomeThings();
}
I then have an abstract base class, with a method GetService that returns the service.
It's here that i'm trying to use the TinyIoC.TinyIoCContainer.Current.Resolve();
In this case, it would be TinyIoC.TinyIoCContainer.Current.Resolve("typeName");
public abstract class Service : IService
{
abstract void DoSomething();
abstract IEnumerable<string> GetSomeThings();
public static IService GetService(string type)
{
//currently, i'm doing this with reflection....
}
}
Here's my implementation of the service.
public class CustomerService : Service
{
public void DoSomething()
{
//do stuff
}
public IEnumerable<string> GetSomeThings()
{
//return stuff
}
public IEnumerable<Customer> ShowAllWithTheNameAlex()
{
//return
}
}
Finally, I have my Nancy Module, that looks like:
public class MyModule : NancyModule
{
public MyModule()
{
Get["/{typeName}/{methodName}"] = p => ExecuteMethod(p.typeName, p.methodName);
}
private dynamic ExecuteMethod(string typeName, string methodName)
{
var service = Service.GetService(typeName);
var result = service.GetType().GetMethod(methodName).Invoke(service, null);
//do stuff
return result; //or whatever
}
}
#alexjamesbrown - The short answer is, you don't. Nancy was specifically designed so that you did not deal with the container directly. You mention that the class, that you want to take a dependency on IMyThing, is not a NancyModule. Well this is not an issue, as long as one of your modules has a reference to it, then those dependencies can also have their own dependencies that will be satisfied at runtime.
public interface IGreetingMessageService
{
string GetMessage();
}
public class GreetingMessageService: IGreetingMessageService
{
public string GetMessage()
{
return "Hi!";
}
}
public interface IGreeter
{
string Greet();
}
public class Greeter
{
private readonly IGreetingMessageService service;
public Greeter(IGreetingMessageService service)
{
this.service = service;
}
public string Greet()
{
return this.service.GetMessage();
}
}
public class GreetingsModule : NancyModule
{
public GreetingModule(IGreeter greeter)
{
Get["/"] = x => greeter.Greet();
}
}
The above will work just fine and Greeter will have it's dependency on IGreetingMessageService satisfied at runtime
I have had a very similar issue, needing to "share" the container. The reason this is an issue is that my program runs as a service using Nancy self hosting to provide a REST API. My modules have dependencies which are injected by Nancy itself, but the other parts of the app which are not referenced from modules also need dependencies injected.
Multiple containers are not a sensible option here (or anywhere really), I need to share the container between Nancy and the rest of the app.
I simply did the following
(I'm using Autofac but I suspect that TinyIoC in similar)
public class Bootstrapper : AutofacNancyBootstrapper
{
private static readonly Lazy<ILifetimeScope> container = new Lazy<ILifetimeScope>(RegisterTypes);
public static ILifetimeScope Container => container.Value;
protected override ILifetimeScope GetApplicationContainer()
{
return container.Value;
}
// Create container and register my types
private static ILifetimeScope RegisterTypes()
{
var builder = new ContainerBuilder();
// Register all my own types.....
return builder.Build();
}
}
Then, in my main code, I can use the container myself
public class Program
{
public static void Main(string[] args)
{
// Resolve main service with all its dependencies
var service = Bootstrapper.Container.Resolve<Service>();
service.Run();
}
}
As my NancyHost is within the Service, the container is constructed (once) upon its first use in main, this static is then used when Nancy gets round to creating the Bootstrapper itself.
In an ideal world, I wouldn't really want a globally accessible container, normally it would be local to the main function.
In this particular case "not dealing with the container directly" is highly problematic:
public interface IFoo {}
public class Foo : IFoo { public Foo(string bar) {} }
Assume IFoo already is a constructor dependency of a Nancy module.
Note the Foo constructor's string dependency. I need to communicate to the container to use that constructor for an IFoo singleton, when encountered as a Nancy module dependency. I need to register that on the TinyIoC instance NancyFx uses, and pass in the actual value of bar.
I am trying to configure an application such that types from assemblyA can be used by my console to allow for logging in an AOP style. The JournalInterceptor will just write out method calls, input and maybe output arguments to a log file or datastore of some kind.
I can register one type at a time but I would like to register all types in one go. Once I get going I may add some filtering to the registered types but I am missing something.
I am trying to use Classes.FromAssemblyContaining but am not sure how to get at an IRegistration instance for the call to WindsorContainer::Register
Any clues?
// otherAssembly.cs
namespace assemblyA
{
public class Foo1 { public virtual void What(){} }
public class Foo2 { public virtual void Where(){} }
}
// program.cs
namespace console
{
using assemblyA;
public class JournalInterceptor : IInterceptor {}
public class Program
{
public static void Main()
{
var container = new Castle.Windsor.WindsorContainer()
.Register(
Component.For<JournalInterceptor>().LifeStyle.Transient,
// works but can't be the best way
Component.For<Foo1>().LifeStyle.Transient
.Interceptors<JournalInterceptor>(),
Component.For<Foo2>().LifeStyle.Transient,
.Interceptors<JournalInterceptor>(),
// how do I do it this way
Classes.FromAssemblyContaining<Foo1>()
.Pick()
.LifestyleTransient()
.Interceptors<JournalInterceptor>()
);
Foo1 foo = container.Resolve<Foo1>();
}
}
}
Implement a Pointcut. In Castle Windsor this is done by implementing the IModelInterceptorsSelector interface.
It would go something like this:
public class JournalPointcut : IModelInterceptorsSelector
{
public bool HasInterceptors(ComponentModel model)
{
return true; // intercept everything - probably not a good idea, though
}
public InterceptorReference[] SelectInterceptors(
ComponentModel model, InterceptorReference[] interceptors)
{
return new[]
{
InterceptorReference.ForType<JournalInterceptor>()
}.Concat(interceptors).ToArray();
}
}
Then register the Interceptor and the Pointcut with the container:
this.container.Register(Component.For<JounalInterceptor>());
this.container.Kernel.ProxyFactory.AddInterceptorSelector(new JournalPointcut());
For in-depth explanation, you may want to see this recording.
We currently have a suite of integration tests that run via MbUnit test suites. We are in the process of refactoring much of the code to use an IOC framework (StructureMap).
I'd like to configure/initialize the container ONCE when the MBUnit test runner fires up, using the same registry code that we use in production.
Is there a way of achieving this in MbUnit?
(EDIT) The version of MbUnit is 2.4.197.
Found it. The AssemblyCleanup attribute.
http://www.testingreflections.com/node/view/639
I understand that you want to spin up only one container for your entire test run and have it be the container used across test suite execution. The MBUnit docs make it look like you might be able to use a TestSuiteFixture and TestSuiteFixtureSetup to accomplish about what you want.
I wanted to speak from the point of view of a StructureMap user and Test Driven Developer.
We rarely use containers in our test suites unless we are explicitly testing pulling things out of the container. When this is necessary I use the an abstract test base class below (warning we use NUnit):
[TestFixture]
public abstract class with_container
{
protected IContainer Container;
[TestFixtureSetUp]
public void beforeAll()
{
Container = new ServiceBootstraper().GetContainer();
Container.AssertConfigurationIsValid();
}
}
public class Bootstraper
{
public Bootstraper()
{
ObjectFactory.Initialize(x =>
{
//register stuff here
});
}
public IContainer GetContainer()
{
return ObjectFactory.Container;
}}
I would recommend for normal tests that you skip the normal container and just use the automocking container included with StructureMap. Here is another handy abstract test base class we use.
public abstract class Context<T> where T : class
{
[SetUp]
public void Setup()
{
_services = new RhinoAutoMocker<T>(MockMode.AAA);
OverrideMocks();
_cut = _services.ClassUnderTest;
Given();
}
public RhinoAutoMocker<T> _services { get; private set; }
public T _cut { get; private set; }
public SERVICE MockFor<SERVICE>() where SERVICE : class
{
return _services.Get<SERVICE>();
}
public SERVICE Override<SERVICE>(SERVICE with) where SERVICE : class
{
_services.Inject(with);
return with;
}
public virtual void Given()
{
}
public virtual void OverrideMocks()
{
}
}
and here is a basic test using this context tester:
[TestFixture]
public class communication_publisher : Context<CommunicationPublisher>
{
[Test]
public void should_send_published_message_to_endpoint_retrieved_from_the_factory()
{
var message = ObjectMother.ValidOutgoingCommunicationMessage();
_cut.Publish(message);
MockFor<IEndpoint>().AssertWasCalled(a => a.Send(message));
}
}
Sorry if this is not exactly what you wanted. Just these techniques work very well for us and I wanted to share.