module dependencies in prism wpf - wpf

i am trying to code something similar to the project 'Modularity with unity'.
i built 2 modules: Module A (loaded via code) and Module D (loaded from directory).
both are loaded successfully.
but when i try to set dependency it's not working.
i can't really figure out where the dependency is mentioned in this project.
(i set the dependency as attribute in ModuleA class, moduleD is copied after build)
this is my implementation:
Bootstrapper.cs
protected override IModuleCatalog CreateModuleCatalog()
{
return new AggregateModuleCatalog();
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
Type moduleAType = typeof(ModuleAModule);
ModuleCatalog.AddModule(new ModuleInfo()
{
ModuleName = ModuleNames.ModuleA,
ModuleType = moduleAType.AssemblyQualifiedName
});
DirectoryModuleCatalog directoryCatalog = new DirectoryModuleCatalog() { ModulePath = #".\Modules" };
((AggregateModuleCatalog)ModuleCatalog).AddCatalog(directoryCatalog);
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.RegisterTypeIfMissing(typeof(IModuleTracker), typeof(ModuleTracker), true);
}
ModuleA.cs
[Module(ModuleName = ModuleNames.ModuleA)]
[ModuleDependency(ModuleNames.ModuleD)]
public class ModuleAModule : IModule
{
private ILoggerFacade _logger;
private IModuleTracker _moduleTracker;
public ModuleAModule(ILoggerFacade logger, IModuleTracker moduleTracker)
{
_logger = logger;
_moduleTracker = moduleTracker;
_moduleTracker.ModuleConstructed("ModuleA");
}
public void Initialize()
{
_logger.Log("ModuleA demonstrates logging during Initialize().", Category.Info, Priority.Medium);
_moduleTracker.ModuleInitialized("ModuleA");
}
}
ModuleD.cs
[Module(ModuleName = ModuleNames.ModuleD)]
public class ModuleDModule : IModule
{
private ILoggerFacade _logger;
private IModuleTracker _moduleTracker;
public ModuleDModule(ILoggerFacade logger, IModuleTracker moduleTracker)
{
_logger = logger;
_moduleTracker = moduleTracker;
_moduleTracker.ModuleConstructed("ModuleD");
}
public void Initialize()
{
_moduleTracker.ModuleInitialized("ModuleD");
}
}

perhaps that has something to do with the order in which your modules get loaded? As far as I see Module A gets loaded before ModuleD to which is has a dependency.
Don't know if that does help you but that was my first thought..
Which error message do you get?

Related

Navigation between lazy loaded modules with Prism and WPF

Hello I'm trying to setup an architecture where only one module gets booted when the app is launched. Then I'd like to lazy load other modules based on the user's actions.
To achieve this in my app.xaml.cs I have one module loaded at bootstrap time (MainModule), and an other has InitializationMode = InitializationMode.OnDemand
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
Type BlipModuleType = typeof(BlipModule);
moduleCatalog.AddModule(new ModuleInfo()
{
ModuleName = BlipModuleType.Name,
ModuleType = BlipModuleType.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand
});
moduleCatalog.AddModule<MainModule>();
}
then my main module, which displays the view correctly, has a single view registered to the only region available:
public class MainModule : IModule
{
private readonly IRegionManager _regionManager;
public MainModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewA));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
The lazy loaded module has the same structure, registering a different view (which works properly if i decide to use it as my main module)
public class BlipModule : IModule
{
private readonly IRegionManager _regionManager;
public BlipModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(ViewB));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
finally I have a Command in the viewmodel of my MainModule ViewA, that is supposed to load the new module and navigate to it.
public class ViewAViewModel : BindableBase
{
const string BlipModuleName = "BlipModule";
public ReactiveCommand ChangeRoute { get; set; } = new ReactiveCommand();
public ViewAViewModel(IRegionManager regionManager, IModuleManager moduleManager)
{
ChangeRoute.Subscribe(res =>
{
moduleManager.LoadModule(BlipModuleName);
});
moduleManager.LoadModuleCompleted += (s, e) =>
{
if (e.ModuleInfo.ModuleName == BlipModuleName)
{
regionManager.RequestNavigate(RegionNames.ContentRegion, new Uri(BlipModuleName, UriKind.Relative));
}
};
}
}
The viewB of the BlipModule is actually loaded (I get a hit if I set a breakpoint in the view's constructor), but instead of the view I get a white page with "System.Object" inside of it.
Any idea? thanks!
You want to RegisterForNavigation instead of RegisterViewWithRegion.

Nancy.Json.JsonSettings in Nancy v2

I'm upgrading my project from Nancy 1.4.5 -> 2.0
And I have errors:
public class AppBootstrapper : AutofacNancyBootstrapper
{
private readonly ILifetimeScope _scope;
public AppBootstrapper(ILifetimeScope scope)
{
_scope = scope;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _scope;
}
protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
{
JsonSettings.MaxJsonLength = int.MaxValue;
JsonSettings.RetainCasing = true;
base.ApplicationStartup(container, pipelines);
}
}
}
Error CS0103 The name 'JsonSettings' does not exist in the current context
How can I resolve this issue?
I added method to AppBootstrapper class:
public class AppBootstrapper : DefaultNancyBootstrapper
{
public override void Configure(INancyEnvironment environment)
{
environment.Json(retainCasing: true);
}
... other methods
}
And added maxJsonLength to web.config:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483647"/>
</webServices>
</scripting>
</system.web.extensions>

Properly Disposing a context with Unit of Work Pattern Dependency Injection in WPF

I have been trying to use DI within my Unit of Work / Repository pattern in WPF. The problem I am running into currently is if I make a call to a repository like _UserRepo.Add(User) and an exception is thrown. Every new call to the repository throws the exception because the context is never disposed of.
What I have tried
Unit Of Work
public class UnitOfWork : IUnitOfWork
{
private DbContextTransaction _trans;
private BomConfiguratorContext _context;
public UnitOfWork(BomConfiguratorContext context)
{
_context = context;
_trans = context.Database.BeginTransaction();
}
public void Dispose()
{
try
{
_context.SaveChanges();
_trans.Commit();
}
catch (Exception)
{
_trans.Rollback();
}
finally
{
_context.Dispose(); //This obviously does not work
}
}
}
Unit Of Work Factory
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private BomConfiguratorContext _context;
public UnitOfWorkFactory(BomConfiguratorContext context)
{
_context = context;
}
public UnitOfWork Create()
{
return new UnitOfWork(_context);
}
}
My Generic Repository
public interface IRepository<TEntity> where TEntity : class
{
void Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
void Remove(TEntity entity);
void RemoveRange(IEnumerable<TEntity> entities);
TEntity Get(int id);
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
void Update(TEntity entity);
}
Generic Repository Implementation
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly BomConfiguratorContext Context;
public Repository(BomConfiguratorContext context)
{
Context = context;
}
public virtual void Add(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
Context.Set<TEntity>().AddRange(entities);
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return Context.Set<TEntity>().Where(predicate);
}
public TEntity Get(int id)
{
return Context.Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
public void Remove(TEntity entity)
{
Context.Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
Context.Set<TEntity>().RemoveRange(entities);
}
public void Update(TEntity entity)
{
Context.Set<TEntity>().Attach(entity);
Context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
}
User Repository
public class UserRepository : Repository<User>,IUserRepository
{
public UserRepository(BomConfiguratorContext context)
:base(context)
{
}
}
Use Case
using (var UOW = _UnitOfWorkFactory.Create())
{
//Submit the user
_UserRepository.Add(ExampleNewUser);
}
So currently I am using MVVM Light to do all my DI work, now I understand with mvvm light you can only inject with singleton scope. So I am pretty sure I will end up having to switch over to something like Ninject so I can utilize their .InTransientScope or .InNamedScope (from what I have been reading).
Obviously the above code will not work with MVVM Light since the context is never properly disposed of.
The Question
So my question to you is if I were to swap over to using Ninject and start injecting my Context into these repositories / unit of work. How do I properly configure it to AWLAYS inject a new context within my unit of work for the repositories.
I read that Ninject MVC has .InRequestScope which would solve the issue entirely. But what about for WPF? How do you achieve the same kind of injection?
I can't seem to find the exact solution/pattern or maybe there is a better way to do this? Any suggestions and help would be greatly appreciated.
My solution to the problem was to create a ContextFactory.
Interface
public interface IContextFactory
{
BomConfiguratorContext Create();
BomConfiguratorContext Get();
}
Context Factory
The Factory allows me to either Get an existing context or create a new context.
public class ContextFactory : IContextFactory
{
private BomConfiguratorContext _context;
public ContextFactory(BomConfiguratorContext context)
{
_context = context;
}
public BomConfiguratorContext Create()
{
_context = new BomConfiguratorContext();
return _context;
}
public BomConfiguratorContext Get()
{
return _context;
}
}
New Base Repository
By calling the ContextFactory.Get() method I use the cached context instead of creating a new one.
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly IContextFactory ContextFactory;
public Repository(IContextFactory factory)
{
ContextFactory = factory;
}
public virtual void Add(TEntity entity)
{
ContextFactory.Get().Set<TEntity>().Add(entity);
}
public void AddRange(IEnumerable<TEntity> entities)
{
ContextFactory.Get().Set<TEntity>().AddRange(entities);
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return ContextFactory.Get().Set<TEntity>().Where(predicate);
}
public TEntity Get(int id)
{
return ContextFactory.Get().Set<TEntity>().Find(id);
}
public IEnumerable<TEntity> GetAll()
{
return ContextFactory.Get().Set<TEntity>().ToList();
}
public void Remove(TEntity entity)
{
ContextFactory.Get().Set<TEntity>().Remove(entity);
}
public void RemoveRange(IEnumerable<TEntity> entities)
{
ContextFactory.Get().Set<TEntity>().RemoveRange(entities);
}
public void Update(TEntity entity)
{
ContextFactory.Get().Set<TEntity>().Attach(entity);
ContextFactory.Get().Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
}
New Unit Of Work Factory
When the factory is Create() method is called I call the context factory's Create() method to create a new context.
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private IContextFactory _contextFactory;
public UnitOfWorkFactory(IContextFactory factory)
{
_contextFactory = factory;
}
public UnitOfWork Create()
{
return new UnitOfWork(_contextFactory.Create());
}
}
By doing it this way I am now able to inject my context factory into all my repositories. I attempted to use the Ninject scopes mentioned above in the original question but ended up causing issues with injecting two separate contexts, one in my unit of work factory and one in my repositories.

WPF, Prism, Unitybootstrapper, and Enterprise Library Logging setup throwing LogWriter exception

I'm just trying to get up off the ground and get used to working with Prism, in my bootstrapper I have:
public class Bootstrapper : UnityBootstrapper
{
private readonly EnterpriseLibraryLoggerAdapter _logger = new EnterpriseLibraryLoggerAdapter();
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Shell)this.Shell;
Application.Current.MainWindow.Show();
}
protected override DependencyObject CreateShell()
{
return this.Container.Resolve<Shell>();
}
protected override ILoggerFacade CreateLogger()
{
return _logger;
}
for my App OnStartup:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}
And the logging adapter
public class EnterpriseLibraryLoggerAdapter : ILoggerFacade
{
#region ILoggerFacade Members
public void Log(string message, Category category, Priority priority)
{
Logger.Write(message, category.ToString(), (int)priority); // <--Blows up here
}
#endregion
}
When the bootstrapper Runs, it hits the Logger.Write and throws an exception:
The type LogWriter does not have an accessible constructor.
I'm following from the StockTraderRI sample app. Am I missing a registration somewhere?
moved the configuration to my bootstrapper constructor and things seem to be working
var builder = new ConfigurationSourceBuilder();
builder.ConfigureLogging()
.WithOptions
.DoNotRevertImpersonation()
.LogToCategoryNamed("Debug")
.SendTo.FlatFile("Basic Log File")
.FormatWith(new FormatterBuilder()
.TextFormatterNamed("Text Formatter")
.UsingTemplate(
"Timestamp: {timestamp}{newline}Message: {message}{newline}Category: {category}{newline}"))
.ToFile("core.log")
.SendTo.RollingFile("Rolling Log files")
.RollAfterSize(1024)
.ToFile("RollingTest.log")
.LogToCategoryNamed("General")
.WithOptions.SetAsDefaultCategory()
.SendTo.SharedListenerNamed("Basic Log File");
var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);
EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);

MEF problem with import

models from shell-view-model with abstract factory pattern. I need inject in view-models classes from external assembly. If I use abstract factory pattern on creation view-models. Problem is imported classes in view-models are null.
Shell-view-models look like this:
public interface IViewModelFactory
{
ILogOnViewModel CreateLogOnViewModel(IShellViewModel shellViewModel);
IMessengerViewModel CreateMessengerViewModel(IShellViewModel shellViewModel);
}
[Export(typeof(IViewModelFactory))]
public class DefaulFactoryViewModel:IViewModelFactory
{
#region Implementation of IViewModelFactory
public ILogOnViewModel CreateLogOnViewModel(IShellViewModel shellViewModel)
{
return new LogOnViewModel(shellViewModel);
}
public IMessengerViewModel CreateMessengerViewModel(IShellViewModel shellViewModel)
{
return new MessengerViewModel(shellViewModel);
}
#endregion
}
public interface IShellViewModel
{
void ShowLogOnView();
void ShowMessengerView();
}
[Export(typeof(IShellViewModel))]
public class ShellViewModel : Conductor<IScreen>, IShellViewModel
{
private readonly IViewModelFactory _factory;
[ImportingConstructor]
public ShellViewModel(IViewModelFactory factory)
{
_factory = factory;
ShowLogOnView();
}
public void ShowLogOnView()
{
var model = _factory.CreateLogOnViewModel(this);
// var model = IoC.Get<LogOnViewModel>();
ActivateItem(model);
}
public void ShowMessengerView()
{
var model = _factory.CreateMessengerViewModel(this);
ActivateItem(model);
}
}
Some view-model.:
public class LogOnViewModel : Screen,ILogOnViewModel
{
[Import]//inject class from external assembly
private IPokecConnection _pokecConn;
private readonly IShellViewModel _shellViewModel=null;
private User _user=null;
public LogOnViewModel(IShellViewModel shellViewModel)
{
_shellViewModel = shellViewModel;
_user = new User();
}
}
variable _pokecConn are null becasuse I use abstract factory on creation new view-models.
if I use in shell-view model this:
var model = IoC.Get<LogOnViewModel>();
instead this:
var model = _factory.CreateLogOnViewModel(this);
and add Export attribute on view-models classes it works good, but I would like use abstract factory, and inject in view-model only classes from extrenal assembly.
It exist solution on this problem, or I must create view-models from IoC and export all class? Thanl for advance.
EDITED :
MEF BOOTSTRAPER CLASS:
public class MefBootStrapper : Bootstrapper<IShellViewModel>
{
#region Fields
private CompositionContainer _container;
#endregion
#region Overrides
protected override void Configure()
{ // configure container
#if SILVERLIGHT
_container = CompositionHost.Initialize(
new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
#else
var catalog =
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
//add external DLL
catalog.Catalogs.Add(
new AssemblyCatalog(string.Format(
CultureInfo.InvariantCulture, "{0}{1}", System.IO.Directory.GetCurrentDirectory(), #"\Pokec_Toolkit.dll")));
_container = new CompositionContainer(catalog);
#endif
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(_container);
_container.Compose(batch);
_container.SatisfyImportsOnce(this);
}
protected override object GetInstance(Type serviceType, string key)
{
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = _container.GetExportedValues<object>(contract);
if (exports.Count() > 0)
return exports.First();
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}
protected override void BuildUp(object instance)
{
_container.SatisfyImportsOnce(instance);
}
#endregion
}
Did you forget the attribute ImportingConstructor for the LogOnViewModel constructor?
EDIT: Import property always null (MEF import issue)

Resources