How would I use Autofac as the primary container in Catel? - autofac

Is there a way I can use Autofac as my primary container for Catel?
I've seen that there is support for Unity, Ninject, MEF, Windsor, and Unity, but there is no mention of Autofac integration.

We haven't created an helper for Autofac, nor are we planning to write one.
You have the following options:
1) Write the Helper class yourself (see the others for examples) and register it, then the ServiceLocator can sync it
2) In the latest prerelease of Catel (3.7 prerelease) we have introduced the IDependencyResolver. You can implement this interface on your Autofac container and register that in the ServiceLocator. Note though that we don't have full support for this so it might cause side-effects.
3) Use the ServiceLocator in Catel
edit on 2013-09-14
4) If you are interested in the latest version of Catel (nightly build), you can now replace the default service locator with your implementation:
Issue details: https://catelproject.atlassian.net/browse/CTL-175
Documentation: https://catelproject.atlassian.net/wiki/pages/viewpage.action?pageId=622682#IoC(ServiceLocatorandTypeFactory)-Replacingthedefaultcomponents

Here is my solution for Catel 3.8 and Autofac (should work for other 3rd party containers too).
I wrote an implementation for IDependencyResolver backed by an Autofac container and an implementation for IServiceLocator. The former contains the applications 3rd party IoC configuration and logic. The latter is used to grab all type and instance registrations of Catels modules and foreward them to Autofac.
The basic strategy is
Create and configure a 3rd party container
Use it to instanciate (partially customized) Catel IoC components and introduce them to Catels IoC configuration
Let Catel do its type and instance registration using the previously created (customized) implementations and foreward them to the third party container
So here is my implementation for the IServiceLocator:
internal class CustomServiceLocator : IServiceLocator
{
private readonly CustomDependencyResolver _dependencyResolver;
public CustomServiceLocator(CustomDependencyResolver dependencyResolver)
{
_dependencyResolver = dependencyResolver;
}
public object GetService(Type serviceType)
{
throw new NotImplementedException();
}
public RegistrationInfo GetRegistrationInfo(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public bool IsTypeRegistered(Type serviceType, object tag = null)
{
return _dependencyResolver.CanResolve(serviceType, tag);
}
public bool IsTypeRegisteredAsSingleton(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public void RegisterInstance(Type serviceType, object instance, object tag = null)
{
var builder = new ContainerBuilder();
IRegistrationBuilder<object, SimpleActivatorData, SingleRegistrationStyle> registrationBuilder = builder.RegisterInstance(instance);
if (tag != null)
{
registrationBuilder.Keyed(tag, serviceType);
}
_dependencyResolver.UpdateContainer(builder);
}
public void RegisterType(Type serviceType, Type serviceImplementationType, object tag = null, RegistrationType registrationType = RegistrationType.Singleton,
bool registerIfAlreadyRegistered = true)
{
var builder = new ContainerBuilder();
IRegistrationBuilder<object, ConcreteReflectionActivatorData, SingleRegistrationStyle> registrationBuilder = builder.RegisterType(serviceImplementationType).As(serviceType);
if (tag != null)
{
registrationBuilder.Keyed(tag, serviceType);
}
switch (registrationType)
{
case RegistrationType.Singleton:
registrationBuilder.SingleInstance();
break;
case RegistrationType.Transient:
registrationBuilder.InstancePerDependency();
break;
default:
registrationBuilder.InstancePerDependency();
break;
}
_dependencyResolver.UpdateContainer(builder);
TypeRegistered(this, new TypeRegisteredEventArgs(serviceType, serviceImplementationType, tag, registrationType));
}
public object ResolveType(Type serviceType, object tag = null)
{
// Must be implemented. Catels ViewModelBase resolves the DependencyResolver in ctor using the ServiceLocator...Why???
return _dependencyResolver.Resolve(serviceType, tag);
}
public IEnumerable<object> ResolveTypes(Type serviceType)
{
throw new NotImplementedException();
}
public void RemoveInstance(Type serviceType, object tag = null)
{
throw new NotImplementedException();
}
public void RemoveAllInstances(Type serviceType)
{
throw new NotImplementedException();
}
public void RemoveAllInstances(object tag = null)
{
throw new NotImplementedException();
}
public bool IsExternalContainerSupported(object externalContainer)
{
throw new NotImplementedException();
}
public void RegisterExternalContainer(object externalContainer)
{
throw new NotImplementedException();
}
public void RegisterExternalContainerHelper(IExternalContainerHelper externalContainerHelper)
{
throw new NotImplementedException();
}
public void ExportInstancesToExternalContainers()
{
throw new NotImplementedException();
}
public void ExportToExternalContainers()
{
throw new NotImplementedException();
}
public bool AreAllTypesRegistered(params Type[] types)
{
return _dependencyResolver.CanResolveAll(types);
}
public object[] ResolveAllTypes(params Type[] types)
{
return _dependencyResolver.ResolveAll(types);
}
public bool AutomaticallyKeepContainersSynchronized { get; set; }
public bool CanResolveNonAbstractTypesWithoutRegistration { get; set; }
public bool SupportDependencyInjection { get; set; }
public bool AutoRegisterTypesViaAttributes { get; set; }
public bool IgnoreRuntimeIncorrectUsageOfRegisterAttribute { get; set; }
public event EventHandler<MissingTypeEventArgs> MissingType;
public event EventHandler<TypeRegisteredEventArgs> TypeRegistered;
}
And here the implementation for IDepencyResolver.
internal class CustomDependencyResolver : IDependencyResolver
{
private readonly IContainer _container;
public CustomDependencyResolver()
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies());
builder.RegisterInstance(this).SingleInstance(); // dependency of CustomServiceLocator
builder.RegisterInstance(this).As<IDependencyResolver>().SingleInstance(); // Dependency of ViewModelBase. Catels ViewModelBase resolves the DependencyResolver in ctor using the ServiceLocator...Why???
builder.RegisterType<CustomServiceLocator>().As<IServiceLocator>().SingleInstance(); // dependency of TypeFactory (subscribes to TypeRegistered event to clear its cache)
builder.RegisterType<TypeFactory>().As<ITypeFactory>().SingleInstance(); // dependency of ViewModelFactory
_container = builder.Build();
}
public bool CanResolve(Type type, object tag = null)
{
return _container.IsRegistered(type);
}
public bool CanResolveAll(Type[] types)
{
return types.All(type => _container.IsRegistered(type));
}
public object Resolve(Type type, object tag = null)
{
object obj;
if (tag == null)
{
if (_container.TryResolve(type, out obj))
return obj;
}
else
{
if (_container.TryResolveKeyed(tag, type, out obj))
return obj;
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", tag ?? type.Name));
}
public object[] ResolveAll(Type[] types, object tag = null)
{
var objects = new ArrayList();
if (tag == null)
{
foreach (Type type in types)
{
object obj;
if (_container.TryResolve(type, out obj))
{
objects.Add(obj);
}
else
{
throw new Exception(string.Format("Could not locate any instances of contract {0}.", type.Name));
}
}
}
else
{
foreach (Type type in types)
{
object obj;
if (_container.TryResolveKeyed(tag, type, out obj))
{
objects.Add(obj);
}
else
{
throw new Exception(string.Format("Could not locate any instances of contract {0}.", tag));
}
}
}
return objects.ToArray();
}
public void UpdateContainer(ContainerBuilder builder)
{
builder.Update(_container);
}
}
Putting it all together at startup:
public partial class App : Application
{
/// <summary>
/// Raises the <see cref="E:System.Windows.Application.Startup"/> event.
/// </summary>
/// <param name="e">A <see cref="T:System.Windows.StartupEventArgs"/> that contains the event data.</param>
protected override void OnStartup(StartupEventArgs e)
{
#if DEBUG
Catel.Logging.LogManager.AddDebugListener();
#endif
StyleHelper.CreateStyleForwardersForDefaultStyles();
// create the DependencyResolver and do Catel IoC configuration
CustomDependencyResolver dependencyResolver = new CustomDependencyResolver();
DependencyResolverManager.Default.DefaultDependencyResolver = dependencyResolver;
IoCConfiguration.DefaultDependencyResolver = dependencyResolver;
IoCConfiguration.DefaultServiceLocator = dependencyResolver.Resolve<IServiceLocator>();
IoCConfiguration.DefaultTypeFactory = dependencyResolver.Resolve<ITypeFactory>();
// let Catel register its dependencies
Catel.Core.ModuleInitializer.Initialize();
Catel.MVVM.ModuleInitializer.Initialize();
base.OnStartup(e);
}
}
Tradoffs and side effects: In my opinion Catels IoC implementation is a litte bit fuzzy. e.g. Sometimes the ServiceLocator is used to resolve the DependencyResolver, sometimes the other way around. I tried to figure out the most common internally used ways and cover them with my solution. A possible side effect may occure, if a type is registered to Autofacs container after application startup and the TypeFactory is not notified to clear its cache (i did not analyse the TypeFactory itself). I suggest an implementation for a TypeRegistered event in CustomDependencyResolver, subscribed by the CustomServiceLocator to foreward it to the TypeFactory.

Related

Activation error occurred while trying to get instance of type IBackupTableStorageService, key "" StructureMap library

I am facing issue of mapping. My solution is as below.
I have two projects
1. Azure Function (v1) project with Framework: .Net Framework 4.7.2
2. Class Library with Framework: .Net Framework 4.7.2
In 1st project i have an azure function which is triggering using Queue and call my function BeginBackup(CancellationToken token, DateTime.Now()) which exist in my concreate class.
I am calling my function as below from my Azure function.
DependencyContext.Instance.Locator.GetInstance<IBackupTableStorageService>().BeginBackup(cancellationToken, SystemTime.UtcNow());
My DependencyContext.cs class is below.
using ClassLibrary1.BackupTableStorage;
using CommonServiceLocator;
using StructureMap;
using System;
using System.Collections.Generic;
namespace AzureFunctionsProject.Functions
{
public interface IDependencyContext
{
IServiceLocator Locator { get; }
}
public class DependencyContext : IDependencyContext
{
private static volatile IDependencyContext _instance;
private static readonly object SyncRoot = new Object();
public static IDependencyContext Instance
{
get
{
if (_instance == null)
{
lock (SyncRoot)
{
if (_instance == null)
{
_instance = new DependencyContext();
}
}
}
return _instance;
}
set => _instance = value;
}
public IServiceLocator Locator { get; private set; }
public DependencyContext()
{
Build();
}
private void Build()
{
var registry = new Registry();//Registry is the class belongs to StructureMap.
var container = new Container();
var serviceLocatorProvider = new StructureMapServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => serviceLocatorProvider);
Locator = serviceLocatorProvider;
// register the container itself
registry.For<IServiceLocator>().Use(ServiceLocator.Current);
// Apply the registry to the container
container.Configure(x =>
{
x.AddRegistry<Domain.Registry>();
x.AddRegistry(registry);
});
}
}
public class StructureMapServiceLocator : ServiceLocatorImplBase
{
private IContainer Container { get; set; }
public StructureMapServiceLocator(IContainer container)
{
Container = container;
}
protected override object DoGetInstance(Type serviceType, string key)
{
if (string.IsNullOrEmpty(key))
{
return Container.GetInstance(serviceType);
}
return Container.GetInstance(serviceType, key);
}
protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
{
foreach (object obj in Container.GetAllInstances(serviceType))
{
yield return obj;
}
}
}
}
I have another Registery class which i have created which have mapping of my interface with my concrete class.
Registery.cs file is below.
namespace ClassLibrary1.Domain
{
public class Registry: StructureMap.Registry
{
public Registry()
{
// The Localization Service needs to be passed a function to record exceptions
For<ILocalizationService>().Use<LocalizationService>()
.Ctor<LocalizationService.LogException>().Is(context => CreateExceptionLogger(context));
For<ICloudStorageWrapper>().Use<CloudStorageWrapper>();
For<IBackupTableStorageService>().Use<BackupTableStorageService>();
}
private LocalizationService.LogException CreateExceptionLogger(IContext context)
{
return (ex, c, m) =>
{
var logger = context.GetInstance<ILogicalOperationsLogger>();
logger.ErrorException(m, ex);
};
}
}
}
Like wise i have my Interface and respected to concreate class which has the definition of my method BeginBackup(CancellationToken token, DateTime.Now());
I am getting exception in DoGetInstance method in DependencyContext.cs class.
I am new in IoC and dependency injection, please let me know what i am doing wrong, if anything required to make question more clear please let me know.
Try to move the registration for IServiceLocator into the configure action. You can also remove the Registry-object you created:
private void Build()
{
var container = new Container();
var serviceLocatorProvider = new StructureMapServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => serviceLocatorProvider);
Locator = serviceLocatorProvider;
// Apply the registry to the container
container.Configure(x =>
{
// register the container itself
x.For<IServiceLocator>().Use(serviceLocatorProvider);
x.AddRegistry<Domain.Registry>();
});
}

Resolve Castle Windsor dependency by argument name

Given That
Component.For<IService>().ImplementedBy<SecretService>().Named("secretService")
Component.For<IService>().ImplementedBy<PublicService>().Named("publicService")
And
class Foo{
public Foo(IService publicService){ ...... }
}
And
class Bar{
public Bar(IService secretService){ ...... }
}
Then how can i achieve the following
Foo and Bar should get instances of publicService and secretService respectively, entirely based on name of their constructor parameters.
I have now made it to work using a custom SubDependencyResolver, i added this to the container and now it injects the implementation by name
public class ByParameterNameResolver : ISubDependencyResolver
{
private readonly IKernel kernel;
public ByParameterNameResolver(IKernel kernel)
{
this.kernel = kernel;
}
public virtual bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
{
if (dependency.TargetItemType == null)
return false;
if (string.IsNullOrWhiteSpace(dependency.DependencyKey))
return false;
return kernel.HasComponent(dependency.DependencyKey);
}
public virtual object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
{
return kernel.Resolve(dependency.DependencyKey, dependency.TargetItemType);
}
}

Injecting/Managing at runtime changing connectionstrings using Entity Framework, Dependency Injection, Unit of Work and Repository Patterns

The situation
I'm building a web application using the in the title mentioned techniques. This application will something like a CMS system for multiple clients. The client has to login to this system using his company name and login credentials.
With the provided company name, I connect to a database (static DbContext, same connection string every time) where all clients database information is stored and search for this clients specific database(every client has his own with exact same design) login information. That all works fine.
Now here is the tricky part. To continue the login procedure I need to somehow inject or lazy load the repository using the other DbContext with a connection string that is build up from the result of the other database.
What I have
2 DbContexts generated from an existing database, one static and one if possible dynamic.
Then the generic repository classes/interfaces:
public interface IRepository
{
void Submit();
}
public interface IRepository<TEntity, TContext> : IRepository
where TEntity : class
where TContext : DbContext
{
//crud stuff
}
public abstract class GenericRepository<TEntity, TContext> : IRepository<TEntity, TContext>
where TEntity : class
where TContext : DbContext
{
private TContext _dataContext;
private IUnitOfWork _unitOfWork;
private readonly IDbSet<TEntity> dbset;
protected GenericRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
_unitOfWork.Register(this);
}
}
Unit of work class/interface
public interface IUnitOfWork
{
void Register(IRepository repository);
void Commit();
}
public class UnitOfWork : IUnitOfWork
{
private readonly Dictionary<string, IRepository> _repositories;
private HttpContextBase _httpContext;
public UnitOfWork(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public void Register(IRepository repository)
{
_repositories.Add(repository.GetType().Name, repository);
}
public void Commit()
{
_repositories.ToList().ForEach(x => x.Value.Submit());
}
}
Then a context/entity specific repository
public class EmployeeRepository : GenericRepository<tbl_Medewerker, CustomerDbEntities>, IEmployeeRepository
{
public EmployeeRepository(IUnitOfWork unitOfWork)
: base(unitOfWork)
{
}
}
public interface IEmployeeRepository : IRepository<tbl_Medewerker, CustomerDbEntities>
{
}
Then the service that implements the repository
public interface IEmployeeLoginService
{
tbl_Medewerker GetEmployeeByLogin(string username, string password);
tbl_Medewerker GetEmployeeByID(Guid id);
}
public class EmployeeLoginService : IEmployeeLoginService
{
private readonly IEmployeeRepository _employeeRepository;
public EmployeeLoginService(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
public tbl_Medewerker GetEmployeeByLogin(string username, string password)
{
return _employeeRepository.Get(e => e.MedewerkerNaam.ToLower() == username.ToLower() && e.Password == password);
}
public tbl_Medewerker GetEmployeeByID(Guid id)
{
return _employeeRepository.GetById(id);
}
}
Finally the controller that implements that service and uses it in the login action
public class AccountController : BaseController
{
IConnectionService _connectionService;
IEmployeeLoginService _employeeService;
public AccountController(IConnectionService connectionService, IEmployeeLoginService employeeService)
{
_connectionService = connectionService;
_employeeService = employeeService;
}
[AllowAnonymous, HttpPost]
public ActionResult Login(LoginModel login)
{
if ((Settings)Session["Settings"] == null)
{
Settings settings = new Settings();
settings.company = _connectionService.GetCompanyName(login.CompanyName);
if (settings.company != null)
{
settings.licence = _connectionService.GetLicenceByCompanyID(settings.company.Company_id);
if (settings.licence != null)
{
settings.connectionStringOrName = string.Format(#"Data Source={0};Initial Catalog={1};User ID={2};Password={3};Application Name=EntityFrameworkMUE", settings.licence.WS_DatabaseServer, settings.licence.WS_DatabaseName, settings.licence.WS_DatabaseUID, settings.licence.WS_DatabasePWD);
Session["Settings"] = settings;
settings.user = _employeeService.GetEmployeeByLogin(login.UserName, login.Password);
if (settings.user != null)
{
FormsAuthentication.SetAuthCookie(string.Format("{0},{1}", settings.company.Company_id.ToString(), settings.user.Medewerker_ID.ToString()) , login.RememberMe);
return RedirectToAction("index", "home");
}
}
}
}
else
{
return RedirectToAction("index", "home");
}
return View();
}
}
And of course the autofac bootstrapper:
private static void SetAutoFacContainer()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerHttpRequest();
builder.RegisterAssemblyTypes(typeof(UserRepository).Assembly)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces().InstancePerHttpRequest();
builder.RegisterAssemblyTypes(typeof(ConnectionService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces().InstancePerHttpRequest();
builder.Register(c => new HttpContextWrapper(HttpContext.Current)).As<HttpContextBase>().InstancePerLifetimeScope();
builder.RegisterModule(new AutofacWebTypesModule());
builder.Register(att => new AuthorizeFilter(att.Resolve<IConnectionService>(), att.Resolve<IEmployeeLoginService>())).AsAuthorizationFilterFor<Controller>().InstancePerHttpRequest();
builder.RegisterFilterProvider();
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
My idea how to do this, is setting a session variable with the connection string after data retrieval from the one static database where the info is stored and inject session in the unit of work and somehow use it there, but I can't wrap my head around it.
The question(s):
Am I heading in the right direction trying to achieve this, or even is it possible? If not what steps would you take to achieve this
I know it's a long read I hope you guys can help me, I'm quite new to using these techniques all together. Thanks in advance - I really appreciate it!
Your on the right track, I have used
var mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(mtc));
The identification strategy would be based on the logged in user. With defaults for when they aren't logged in.
public class CompanyNameIdentificationStrategy : ITenantIdentificationStrategy
{
public bool TryIdentifyTenant(out object tenantId)
{
var context = HttpContext.Current;
if(context != null)
{
var myUser = context.User as MyUserObject;
if(myUser != null)
{
tenantId = myUser.CompanyName;
return true;
}
}
return false;
}
}
Then you add to your autofact setup:
var s = c.Resolve<ITenantIdentificationStrategy>();
object id;
if (s.TryIdentifyTenant(out id) && id != null)
{
return id;
}
return "default";
}).Keyed<string>("CompanyName");
builder.Register<Settings>(c =>
{
var companyName = c.ResolveKeyed<string>("companyName");
if (companyName == "default")
{
return new DefaultSettings();
}
var settings = new Settings();
return settings;
}).InstancePerLifetimeScope();
You can resolve stuff inside these code blocks. I would probably setup a keyed default settings, and then when the user is logged in the settings would switch to their setup and the rest of the application should work.

Instance in Caliburn Micro

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.

WPF Custom Container Class

Well im trying to create a custom container class that contains in my case ViewMappingEntry's, so i created a class that inherits Freezable and implements ICollection.. but when creating an instance in Blend and browsing it i get this error: Cannot add content to an object of type 'ViewModelMapping'..
Note: It compiles fine and i get no error in Visual studio, so what am i missing?
public class ViewModelMapping : Freezable, ICollection<MappingEntry>
{
object locker = new object();
List<MappingEntry> internalList = new List<MappingEntry>();
#region ICollection<MappingEntry> Members
public void Add(MappingEntry item)
{
if (this.IsFrozen || this.IsSealed)
throw new InvalidOperationException("ViewModelMapping is frozen");
lock (locker)
internalList.Add(item);
}
public void Clear()
{
if (this.IsFrozen || this.IsSealed)
throw new InvalidOperationException("ViewModelMapping is frozen");
lock (locker)
internalList.Clear();
}
public bool Contains(MappingEntry item)
{
lock (locker)
return internalList.Contains(item);
}
public void CopyTo(MappingEntry[] array, int arrayIndex)
{
lock (locker)
internalList.CopyTo(array, arrayIndex);
}
public int Count
{
get
{
lock (locker)
return internalList.Count;
}
}
public bool IsReadOnly
{
get { return base.IsFrozen || base.IsSealed; }
}
public bool Remove(MappingEntry item)
{
if (this.IsFrozen || this.IsSealed)
throw new InvalidOperationException("ViewModelMapping is frozen");
lock (locker)
return internalList.Remove(item);
}
#endregion
#region IEnumerable<MappingEntry> Members
public IEnumerator<MappingEntry> GetEnumerator()
{
lock (locker)
return (IEnumerator<MappingEntry>)internalList.ToArray().GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
lock (locker)
return internalList.ToArray().GetEnumerator();
}
#endregion
protected override Freezable CreateInstanceCore()
{
ViewModelMapping viewModelMapping = new ViewModelMapping();
lock(locker)
viewModelMapping.internalList = new List<MappingEntry>(this.internalList.ToArray());
return viewModelMapping;
}
}
after trying around i found a solution if i implemented all these interfaces -> ICollection, IList, IList, ICollection, IEnumerable it started working in blend.