I think I am losing my mind. :)
I've been struggling with this for two days now. The code looks right. But for some reason when I try to access the [ImportMany] field, it is null, or at least not returning any values.
It get the 3 parts in the catalog, but they don't get applied to the Lazy[] import I am defining.
Here's my code:
using System;
using System.Linq;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace MefTest
{
// Extension interface and metadata
public interface IUIExtension
{
void DoSomething();
}
public interface IUIExtensionDetails
{
string Name { get; }
string Uri { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class UIExtensionAttribute : ExportAttribute
{
public UIExtensionAttribute() : base(typeof(IUIExtensionDetails)) { }
public string Name { get; set; }
public string Uri { get; set; }
}
// Extensions
[UIExtension(Name="Test 01", Uri="http://www.yourmomma.com/")]
public class Test1Extension : IUIExtension
{
public void DoSomething() { }
}
[UIExtension(Name = "Test 02", Uri = "http://www.yourdaddy.com/")]
public class Test2Extension : IUIExtension
{
public void DoSomething() { }
}
[UIExtension(Name = "Test 03", Uri = "http://www.youruncle.com/")]
public class Test3Extension : IUIExtension
{
public void DoSomething() { }
}
// Main program
public class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
[ImportMany]
public Lazy<IUIExtension, IUIExtensionDetails>[] Senders { get; set; }
public void Run()
{
Compose();
}
public void Compose()
{
var catalog = new AssemblyCatalog(
System.Reflection.Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
// This is always 3
Console.WriteLine(
(from g in container.Catalog.Parts select g).Count());
// This is always 0
Console.WriteLine(Senders.Length);
Console.ReadKey();
}
}
}
Your error is here:
public UIExtensionAttribute() : base(typeof(IUIExtensionDetails))
You should pass the contract type there, not the metadata type:
public UIExtensionAttribute() : base(typeof(IUIExtension))
(Also, in order to make sure that your custom export class has the right properties as expected by the import with metadata, I would make it implement the IUIExtensionDetails interface. But that is not mandatory.)
Your metadata attribute is defining the exports as typeof(IUIExtensionDetails) which is your metadata contract, not your actual extension. Change the custom attribute constructor to:
public UIExtensionAttribute() : base(typeof(IUIExtension)) { }
Related
I want to use IOC with my service and I want to instead inject a class not an interface in the constructor as below in the services layer but I do not want to create a new object from the calling layer like var service = new InvoiceService(new ChangeInvoiceDueDateCommand()) instead I want to create something like this from my controller in MVC where the IInvoiceService is injected into the controller constructor but the problem I see is that
public InvoiceController(IInvoiceService invoiceService, IMapper mapper)
{
_invoiceService = invoiceService;
_mapper = mapper;
}
and then called like this
public ActionResult ChangeInvoiceDueDate(InvoiceChangeDueDateViewModel invoiceChangeDueDateViewModel )
{
var request = _mapper.Map<InvoiceChangeDueDateViewModel, ChangeInvoiceDuedateRequest>(invoiceChangeDueDateViewModel);
InvoiceChangeDueDateResponse response = _invoiceService.ChangeDueDate(request);
return View();
}
Service Layer
public class InvoiceService : IInvoiceService
{
private readonly ChangeInvoiceDueDateCommand _changeInvoiceDueDateCommand;
public InvoiceService(ChangeInvoiceDueDateCommand changeInvoiceDueDateCommand)
{
_changeInvoiceDueDateCommand = changeInvoiceDueDateCommand;
}
public InvoiceChangeDueDateResponse ChangeDueDate(ChangeInvoiceDuedateRequest invoiceChangeDueDateRequest)
{
_changeInvoiceDueDateCommand.Execute(invoiceChangeDueDateRequest);
return new InvoiceChangeDueDateResponse {Status = new Status()};
}
}
Command
public class ChangeInvoiceDueDateCommand : ICommand<ChangeInvoiceDuedateRequest>
{
private readonly IRepository<Invoice> _invoiceRepository;
readonly InvoiceDueDateChangeValidator _validator;
public ChangeInvoiceDueDateCommand(IRepository<Invoice> invoiceRepository)
{
_invoiceRepository = invoiceRepository;
_validator = new InvoiceDueDateChangeValidator();
}
public void Execute(ChangeInvoiceDuedateRequest request)
{
if (_validator.IsDuedateValid(request.NewDuedate))
{
Invoice invoice = _invoiceRepository.GetById(request.Id);
invoice.ChangedDueDate(request.NewDuedate);
_invoiceRepository.SaveOrUpdate(invoice);
}
else
{
throw new InvalidDueDateException();
}
}
}
ICommand
public interface ICommand<T> where T : IRequest
{
void Execute(T request);
}
IRequest
public interface IRequest
{
int Id { get; set; }
}
I worked it out. It was just a Windsor syntax issue. It ended up being as simple as registering the Command using the container.Register(Component.For<ChangeInvoiceDueDateCommand>());
Working code before upgrade. HandleBeginMessage() is called automatically:
public class OracleMessageModule : IMessageModule
{
public OracleMessageModule()
{
Factory = new OracleSagaSessionFactory();
}
public OracleSagaSessionFactory Factory { get; set; }
public void HandleBeginMessage()
{
Factory.Begin();
}
public void HandleEndMessage()
{
Factory.Complete();
}
public void HandleError()
{
Factory.Complete();
}
}
Code not working anymore after upgrade to v4. Begin() is not called autommatically:
public class OracleMessageModule : UnitOfWork.IManageUnitsOfWork
{
public OracleMessageModule()
{
Factory = new OracleSagaSessionFactory();
}
public OracleSagaSessionFactory Factory { get; set; }
public void Begin()
{
Factory.Begin();
}
public void End(System.Exception ex = null)
{
Factory.Complete();
}
}
IManageUnitsOfWork are not autoregistered. You need to register your unit of work explicitly.
For more details on how to, see here:
http://docs.particular.net/nservicebus/unit-of-work-in-nservicebus#registering-your-unit-of-work
IPlugin Calss Library
namespace IPlugin
{
public interface IPlugin
{
string Name { get; set; }
void Start();
void Stop();
}
[Export(typeof(IPlugin))]
public abstract class BasePlugin:IPlugin
{
private string _name;
public BasePlugin()
{
Name = "Base Plugin";
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public virtual void Start()
{
fnDowWork();
}
protected abstract void fnDowWork();
public virtual void Stop()
{
}
}
}
Test Plugin Class Library
namespace TestPlugin
{
public class TestPlugin:IPlugin.BasePlugin
{
public TestPlugin()
{
Name = "Test Plugin";
}
protected override void fnDowWork()
{
Console.WriteLine("Do Work !");
}
}
}
Console Application
class Program
{
static void Main(string[] args)
{
var app = new MyApp();
foreach (var p in app._Plugins)
{
p.Start();
}
}
}
public class MyApp
{
[ImportMany(typeof(IPlugin.IPlugin))]
public IEnumerable<IPlugin.IPlugin> _Plugins;
public string _PluginFolder { get; set; }
public string _StartupPath { get; set; }
public MyApp()
{
_StartupPath = Environment.CurrentDirectory;
var pluginFolderName = System.Configuration.ConfigurationManager.AppSettings["PluginFolder"];
_PluginFolder = System.IO.Path.Combine(_StartupPath, pluginFolderName);
InitializeMEF();
}
private void InitializeMEF()
{
var dirCatalog = new DirectoryCatalog(_PluginFolder, "*.dll");
CompositionContainer container = new CompositionContainer(dirCatalog);
container.ComposeParts(this);
}
}
the DirectoryCatalog find tow Assembly IPlugin.dll and TestPlugin.dll and after Compose parts
the myApp._Plugins is not null but its empty , i don't know where i am doing wrong!
You will need to use the InheritedExportAttribute instead of the ExportAttribute:
[InheritedExport(typeof(IPlugin))]
public abstract class BasePlugin:IPlugin
Note that this will only work for plugins that derive from BasePlugin. Other implementations of IPlugin will not be marked for export. To do this you will have to decorate the interface instead.
We are using Caliburn Micro for the first time.
We have a AppBootstrapper inherited from ShellViewModel.
Situvation is that VieModels should have the same instance unless it is reset.
we are able to achieve shared or not shared everytime, but releasing the export whenever needed is still a mystery.
public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
private static CompositionContainer _container;
protected override void Configure()
{
try
{
_container = new CompositionContainer(
new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x))));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(_container);
StyleManager.ApplicationTheme = ThemeManager.FromName("Summer");
_container.Compose(batch);
}
catch (Exception exception)
{
}
}
public static void ReleaseAll()
{
}
protected override object GetInstance(Type serviceType, string key)
{
try
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = _container.GetExportedValues<object>(contract);
if (exports.Any())
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (ReflectionTypeLoadException ex)
{
foreach (Exception inner in ex.LoaderExceptions)
{
// write details of "inner", in particular inner.Message
}
return null;
}
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
try
{
return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
catch (Exception exception)
{
return null;
}
}
protected override void BuildUp(object instance)
{
_container.SatisfyImportsOnce(instance);
}
}
ShellViewModel
[Export(typeof(ShellViewModel))]
public sealed class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public ShellViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
Items.Add(compositionContainer.GetExportedValue<AViewModel>());
Items.Add(compositionContainer.GetExportedValue<BViewModel>());
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
public void B()
{
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.B.ToString()));
}
public void A()
{
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public void RESET()
{
AppBootstrapper.ReleaseAll();
ActivateItem(Items.Single(p => p.DisplayName == AppMessageType.A.ToString()));
}
public enum AppMessageType
{
A,
B
}
}
AViewModel
[Export(typeof(AViewModel))]
public sealed class AViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public AViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
DisplayName = ShellViewModel.AppMessageType.A.ToString();
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
}
BViewModel
[Export(typeof(BViewModel))]
public sealed class BViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<object>
{
[ImportingConstructor]
public BViewModel(CompositionContainer compositionContainer, IEventAggregator eventAggregator)
{
DisplayName = ShellViewModel.AppMessageType.B.ToString();
CompositionContainer = compositionContainer;
EventAggregator = eventAggregator;
eventAggregator.Subscribe(this);
}
public IEventAggregator EventAggregator { get; set; }
public CompositionContainer CompositionContainer { get; set; }
public void Handle(object message)
{
//throw new System.NotImplementedException();
}
}
Now AViewModel and BViewModel have single instance.
Whenever Release Button is clicked i want to have new instance of AViewModel and BViewModel.
Hoping to get a reply soon.
Regards,
Vivek
When working with an IoC container, the only part of your code that should take it as a dependency should be your composition root (i.e. your AppBootstrapper in this case). You shouldn't be injecting or referencing the container anywhere else in your code (except possibly factories).
If you want your ShellViewModel to control the lifetime of your child view models (A and B), then you should consider injecting view model factories into your ShellViewModel (via constructor injection if they are required dependencies).
Your AViewModelFactory would just have a single Create method that returns a new instance of AViewModel, likewise with the BViewModelFactory. You can simply new up your view models directly in the factories. If your view models have large dependency chains themselves, then you could consider adding a reference to your container in the factories, although preferably consider looking into the MEF ExportFactory<T> type.
I've been studying this accepted answer to a similar question in which what I believe is a concrete factory returns an implementation based on a string argument on the factory method matching a named binding on the concrete implementation.
I'm struggling to get a slightly more complex example to work properly when the factory is an abstract factory, and I wish to use Ninject convention-based binding. Consider the following test:
[Fact]
public void VehicleBuilderFactory_Creates_Correct_Builder_For_Specified_Client()
{
// arrange
StandardKernel kernel = new StandardKernel();
kernel.Bind(typeof (IVehicleBuilderFactory<,>))
.ToFactory(() => new UseFirstArgumentAsNameInstanceProvider())
.InSingletonScope();
kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof(IVehicleBuilder<>))
.BindAllInterfaces());
var bicycleBuilderFactory =
kernel.Get<IVehicleBuilderFactory<IVehicleBuilder<BlueBicycle>, BlueBicycle>>();
string country = "Germany";
string localizedColor = "blau";
// act
var builder = bicycleBuilderFactory.Create<IVehicleBuilder<BlueBicycle>>(country);
Bicycle Bicycle = builder.Build(localizedColor);
// assert
Assert.IsType<BlueBicycleBuilder_Germany>(builder);
Assert.IsType<BlueBicycle>(Bicycle);
Assert.Equal(localizedColor, Bicycle.Color);
}
Here's where I try juggling with torches & knives 'cause I saw it on the internet once:
public class UseFirstArgumentAsNameInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments) {
return methodInfo.GetGenericArguments()[0].Name + "Builder_" + (string)arguments[0];
// ex: Germany -> 'BlueBicycle' + 'Builder_' + 'Germany' = 'BlueBicyleBuilder_Germany'
}
protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments) {
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
I get stabbed and set ablaze when I try to assign bicycleBuilderFactory with this error:
System.InvalidCastException was unhandled by user code
Message=Unable to cast object of type 'Castle.Proxies.ObjectProxy' to type 'Ninject.Extensions.Conventions.Tests.IVehicleBuilderFactory`2[Ninject.Extensions.Conventions.Tests.IVehicleBuilder`1[Ninject.Extensions.Conventions.Tests.BlueBicycle],Ninject.Extensions.Conventions.Tests.BlueBicycle]'.
Source=System.Core
StackTrace:
at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 37
at Ninject.Extensions.Conventions.Tests.NinjectFactoryConventionsTests.VehicleBuilderFactory_Creates_Correct_Builder_For_Specified_Client() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryConventionsTests.cs:line 40
InnerException:
Is it possible to bind using the ToFactory() method and custom provider, using the factory method argument ("Germany") along with the generic type argument (IVehicleBiulder<BlueBicycle>, BlueBicycle) to resolve the type?
Here's the rest of the code for the test, as compact and readable as I could make it.
public interface IVehicleBuilderFactory<T, TVehicle>
where T : IVehicleBuilder<TVehicle> where TVehicle : IVehicle
{
T Create<T>(string country);
}
VehicleBuilder implementations
public interface IVehicleBuilder<T> where T : IVehicle { T Build(string localizedColor); }
abstract class BicycleBuilder<T> : IVehicleBuilder<T> where T : Bicycle
{
public abstract T Build(string localizedColor);
}
public abstract class RedBicycleBuilder : IVehicleBuilder<RedBicycle>
{
private readonly RedBicycle _Bicycle;
public RedBicycleBuilder(RedBicycle Bicycle) { _Bicycle = Bicycle; }
public RedBicycle Build(string localizedColor)
{
_Bicycle.Color = localizedColor;
return _Bicycle;
}
}
public abstract class GreenBicycleBuilder : IVehicleBuilder<GreenBicycle>
{
private readonly GreenBicycle _Bicycle;
public GreenBicycleBuilder(GreenBicycle Bicycle) { _Bicycle = Bicycle; }
public GreenBicycle Build(string localizedColor)
{
_Bicycle.Color = localizedColor;
return _Bicycle;
}
}
public abstract class BlueBicycleBuilder : IVehicleBuilder<BlueBicycle>
{
private readonly BlueBicycle _Bicycle;
public BlueBicycleBuilder(BlueBicycle Bicycle) { _Bicycle = Bicycle; }
public BlueBicycle Build(string localizedColor)
{
_Bicycle.Color = localizedColor;
return _Bicycle;
}
}
public class RedBicycleBuilder_USA : RedBicycleBuilder {
public RedBicycleBuilder_USA(RedBicycle Bicycle) : base(Bicycle) { }
}
public class RedBicycleBuilder_Germany : RedBicycleBuilder {
public RedBicycleBuilder_Germany(RedBicycle Bicycle) : base(Bicycle) { }
}
public class RedBicycleBuilder_France : RedBicycleBuilder {
public RedBicycleBuilder_France(RedBicycle Bicycle) : base(Bicycle) { }
}
public class RedBicycleBuilder_Default : RedBicycleBuilder {
public RedBicycleBuilder_Default(RedBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_USA : GreenBicycleBuilder {
public GreenBicycleBuilder_USA(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_Germany : GreenBicycleBuilder {
public GreenBicycleBuilder_Germany(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_France : GreenBicycleBuilder {
public GreenBicycleBuilder_France(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_Default : GreenBicycleBuilder {
public GreenBicycleBuilder_Default(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_USA : BlueBicycleBuilder
{
public BlueBicycleBuilder_USA(BlueBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_Germany : BlueBicycleBuilder {
public BlueBicycleBuilder_Germany(BlueBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_France : BlueBicycleBuilder
{
public BlueBicycleBuilder_France(BlueBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_Default : BlueBicycleBuilder
{
public BlueBicycleBuilder_Default(BlueBicycle Bicycle) : base(Bicycle) { }
}
Vehicle implementations:
public interface IVehicle { string Color { get; set; } }
public abstract class Vehicle : IVehicle { public string Color { get; set; } }
public abstract class Bicycle : Vehicle { }
public class RedBicycle : Bicycle { }
public class GreenBicycle : Bicycle { }
public class BlueBicycle : Bicycle { }
Based on comments from #LukeN, I've refactored the Bicycle class, so that its color is set through constructor injection with an IColorSetter. The IColorSetter implementation has a generic Color type, and each of the Color implementations are 'localized' by way of constructor injection with an IColorLocalizer<T>.
This way, no class seems to have knowledge of anything beyond what is logically its responsibility (I think).
However, I'll need to think about this more to see how the refactored classes shown below can be used to show how to use a Ninject custom instance provider could be used to pick the property IColorLocalizer<T> now, since it's the only class that will know about colors and languages; the color coming from its generic type, and the language coming from the name of the implementation itself.
Since asking the original post, I've moved away from using an IoC container to make choices like this, choosing instead to programmatically put in code a switch for picking an implementation, with a default implementation selected for any unhandled outlier cases. But I'm not sure if it's mainly to get beyond something that's stumped me, or because it's a poor choice to lean on an IoC container in this way.
I'll need to update this answer more as I think about it.
Vehicles
public abstract class Vehicle {
public abstract string Color { get; internal set; }
public abstract string Move();
}
public class Bicycle : Vehicle {
public Bicycle(IColorSetter colorSetter) { colorSetter.SetColor(this); }
public override string Color { get; internal set; }
public override string Move() { return "Pedaling!"; }
}
Color setters
public interface IColorSetter { void SetColor(Vehicle vehicle); }
public class ColorSetter<T> : IColorSetter where T : Color
{
private readonly T _color;
public ColorSetter(T color) { _color = color; }
public void SetColor(Vehicle vehicle) { vehicle.Color = _color.Name; }
}
Color localizers
public interface IColorLocalizer<in T> where T : Color {
void LocalizeColor(T color);
}
public class GermanBlueLocalizer : IColorLocalizer<Blue> {
public void LocalizeColor(Blue color) { color.Name = "blau"; }
}
public class EnglishBlueLocalizer : IColorLocalizer<Blue> {
public void LocalizeColor(Blue color) { color.Name = "blue"; }
}
Colors
public abstract class Color { public string Name { get; internal set; } }
public class Red : Color {
public Red(IColorLocalizer<Red> colorLocalizer) {
colorLocalizer.LocalizeColor(this); }
}
public class Green : Color {
public Green(IColorLocalizer<Green> colorLocalizer) {
colorLocalizer.LocalizeColor(this); }
}
public class Blue : Color {
public Blue(IColorLocalizer<Blue> colorLocalizer) {
colorLocalizer.LocalizeColor(this); }
}