DbContext has been disposed when using Ninject - c#

I'm still getting error like this:
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code.
Additional information: The operation cannot be completed because the DbContext has been disposed.
I'm using Ninject and when I use injected repository (InRequestScope) to HomeController everything work good, but when I use injected class InSingletonScope, which has another repository injected by InRequestScope, then I get this error.
My InSingletonScope class:
public class SettingsManager : ISettingsManager
{
private readonly IConfigurationRepository _configurationRepository;
private readonly Lazy<ApplicationSettings> _applicationSettings;
public ApplicationSettings Application { get { return _applicationSettings.Value; } }
private SettingsManager(IConfigurationRepositorycontext)
{
_configurationService = context;
_applicationSettings = new Lazy<ApplicationSettings>(() => new ApplicationSettings(context));
}
}
ApplicationSettings class:
public class ApplicationSettings : ApplicationSettingsBase
{
private readonly IConfigurationRepository _configurationRepository;
public ApplicationSettings(IConfigurationRepository context)
{
_configurationRepository= context;
}
public string GetItem()
{
return _configurationService.GetApplicationConfiguration(4).First().Value.ToString();
}
}
My ConfigurationRepository:
public class ConfigurationRepository : BaseRepository<Configuration>, IConfigurationRepository
{
public ConfigurationRepository(DbContext _dbContext)
: base(_dbContext)
{
}
public IQueryable<ConfigurationDTO> GetApplicationConfiguration(int domainId)
{
return _dbContext.Set<Configuration>()
.Where(x => x.DomainId == 5)
.Select(x => new ConfigurationDTO
{
Name = x.Name,
Value = x.Value
});
}
}
And in this repository I get an error.
My NinjectWebCommon.RegisterServices method:
kernel.Bind<ISettingsManager>().To<SettingsManager>().InSingletonScope();
kernel.Bind<IConfigurationRepository>().To<ConfigurationRepository>().InRequestScope();
kernel.Bind<DbContext>().To<EntitiesDbContext>().InRequestScope();
Where could be the problem?
EDIT
The problem disappears when I change DbContext to EntitiesDbContext:
public class ConfigurationRepository : BaseRepository<Configuration>, IConfigurationRepository
{
public ConfigurationRepository(EntitiesDbContext _dbContext)
: base(_dbContext)
{
}
}
So my binding in Ninject is wrong?
kernel.Bind<DbContext>().To<EntitiesDbContext>().InRequestScope();
Why?
I have new model:
public partial class EntitiesDbContext : DbContext
{
public EntitiesDbContext ()
: base("name=EntitiesDbContext ")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Configuration> Configuration { get; set; }
}
}

Related

C# Entity Framework - Context Cannot Be Used Error with Unit of Work Repository Pattern

I attempting to implement the Unit of Work and Repository Pattern in my ASP.NET MVC app as described here.
I was receiving the following error:
Value cannot be null.
Parameter name: entitySet
during a query. After doing some debugging, I noticed that my DBSet<T> classes were throwing the following error:
{
"The context cannot be used while the model is being created. This exception may be
thrown if the context is used inside the OnModelCreating method or if the same context
instance is accessed by multiple threads concurrently. Note that instance members of
DbContext and related classes are not guaranteed to be thread safe."
}
System.SystemException { System.InvalidOperationException }
I have looked around Stack Overflow but cannot find a solution. I used the "Code First from Database Approach." I checked my connection string and it seems right. My version of Entity, as defined in the packages.config file, is 6.1.3. I have tried commenting out some of the relationships within the DbContext class. The definition of which is as follows:
public partial class BKTrainerContext : DbContext
{
public BKTrainerContext()
: base("name=BKTrainerContext")
{
}
public virtual DbSet<AccessLevel> AccessLevels { get; set; }
public virtual DbSet<BuildCardCategory> BuildCardCategories { get; set; }
public virtual DbSet<BuildCard> BuildCards { get; set; }
public virtual DbSet<Manager> Managers { get; set; }
public virtual DbSet<SliderImage> SliderImages { get; set; }
public virtual DbSet<StoreMessage> StoreMessages { get; set; }
public virtual DbSet<Store> Stores { get; set; }
public virtual DbSet<Video> Videos { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AccessLevel>()
.HasMany(e => e.Managers)
.WithRequired(e => e.AccessLevel1)
.HasForeignKey(e => e.AccessLevel)
.WillCascadeOnDelete(false);
modelBuilder.Entity<BuildCardCategory>()
.HasMany(e => e.BuildCards)
.WithRequired(e => e.BuildCardCategory1)
.HasForeignKey(e => e.BuildCardCategory)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Manager>()
.HasMany(e => e.StoreMessages)
.WithRequired(e => e.Manager)
.HasForeignKey(e => e.MessageAuthor)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Manager>()
.HasMany(e => e.Stores)
.WithRequired(e => e.Manager)
.WillCascadeOnDelete(false);
modelBuilder.Entity<StoreMessage>()
.Property(e => e.MessageBody)
.IsUnicode(false);
modelBuilder.Entity<StoreMessage>()
.HasMany(e => e.Stores)
.WithMany(e => e.StoreMessages)
.Map(m => m.ToTable("MessagesStores").MapLeftKey("MessageID").MapRightKey("StoreID"));
}
}
This is my GenericRepo class
public class GenericRepo<T> : IGenericRepo<T> where T : class
{
internal BKTrainerContext dbContext;
internal DbSet<T> db;
public GenericRepo(BKTrainerContext context)
{
this.dbContext = context;
this.db = context.Set<T>();
}
public virtual IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
IQueryable<T> query = db;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
public virtual T GetEntity(object id)
{
return db.Find(id);
}
public virtual void Insert(T newEntity)
{
db.Add(newEntity);
}
public virtual void Delete(object id)
{
T enity = db.Find(id);
Delete(enity);
}
public virtual void Delete(T entity)
{
if (dbContext.Entry(entity).State == EntityState.Detached)
{
db.Attach(entity);
}
db.Remove(entity);
}
public virtual void Update(T entity)
{
db.Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
}
}
The original error is thrown by the lines within Get(); those that call a query.xx method.
My Unit of Work class is as follows:
public class UnitOfWork : IUnitOfWork
{
private BKTrainerContext dbContext = new BKTrainerContext();
private GenericRepo<BuildCard> buildCardRepo;
private GenericRepo<BuildCardCategory> buildCardCategoryRepo;
private GenericRepo<Manager> managerRepo;
private GenericRepo<StoreMessage> messageRepo;
private GenericRepo<SliderImage> sliderRepo;
private GenericRepo<Store> storeRepo;
private GenericRepo<Video> videoRepo;
private bool disposed = false;
public GenericRepo<BuildCard> BuildCardRepo
{
get
{
if (this.buildCardRepo == null) { buildCardRepo = new GenericRepo<BuildCard>(dbContext); }
return buildCardRepo;
}
}
public GenericRepo<BuildCardCategory> BuildCardCategoriesRepo
{
get
{
if (this.buildCardCategoryRepo == null) { buildCardCategoryRepo = new GenericRepo<BuildCardCategory>(dbContext); }
return buildCardCategoryRepo;
}
}
public GenericRepo<Manager> ManagerRepo
{
get
{
if (this.managerRepo == null) { managerRepo = new GenericRepo<Manager>(dbContext); }
return managerRepo;
}
}
public GenericRepo<StoreMessage> MessageRepo
{
get
{
if (this.messageRepo == null) { messageRepo = new GenericRepo<StoreMessage>(dbContext); }
return messageRepo;
}
}
public GenericRepo<SliderImage> SliderRepo
{
get
{
if (this.sliderRepo == null) { sliderRepo= new GenericRepo<SliderImage>(dbContext); }
return sliderRepo;
}
}
public GenericRepo<Store> StoreRepo
{
get
{
if (this.storeRepo == null) { storeRepo= new GenericRepo<Store>(dbContext); }
return storeRepo;
}
}
public GenericRepo<Video> VideoRepo
{
get
{
if (this.videoRepo == null) {videoRepo = new GenericRepo<Video>(dbContext); }
return videoRepo;
}
}
public void Save()
{
dbContext.SaveChanges();
}
protected virtual void Dispose(bool disposing) {
if (!this.disposed)
{
if (disposing)
{
dbContext.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
I am using Unity to inject it into my Controller.
public class HomeController : Controller
{
private IUnitOfWork worker;
public HomeController(IUnitOfWork unit)
{
this.worker = unit;
}
public ActionResult Index(bool failedLogin = false)
{
IndexViewModel vm = new IndexViewModel();
vm.invalidLogin = failedLogin;
return View(vm);
}
[HttpPost]
public ActionResult AttemptLogin(string userName, string password)
{
List<Store> stores = worker.StoreRepo.Get().ToList();
Store store = worker.StoreRepo.Get(u => u.Username == userName && u.Password == password).FirstOrDefault();
if (stores != null)
{
Session["UserLevel"] = 0;
Session["UserID"] = store.ID;
return RedirectToAction("Index", "User");
}
else
{
Manager manager = worker.ManagerRepo.Get(m => m.Username == userName && m.Password == password).FirstOrDefault();
if (manager != null)
{
Session["UserLevel"] = manager.AccessLevel;
Session["UserID"] = manager.ID;
return RedirectToAction("Index", "Admin");
}
else
{
return RedirectToAction("Index", new { failedLogin = true });
}
}
}
}
I would really appreciate any help. Thanks!
EDIT:
Injection configuration:
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Gets the configured Unity container.
/// </summary>
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
/// <summary>Registers the type mappings with the Unity container.</summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>There is no need to register concrete types such as controllers or API controllers (unless you want to
/// change the defaults), as Unity allows resolving a concrete type even if it was not previously registered.</remarks>
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
container.RegisterType<IUnitOfWork, UnitOfWork>();
// container.RegisterType<IProductRepository, ProductRepository>();
}
}
}
The UnityMVCActivator class
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(BKBuildCard.App_Start.UnityWebActivator), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(BKBuildCard.App_Start.UnityWebActivator), "Shutdown")]
namespace BKBuildCard.App_Start
{
/// <summary>Provides the bootstrapping for integrating Unity with ASP.NET MVC.</summary>
public static class UnityWebActivator
{
/// <summary>Integrates Unity when the application starts.</summary>
public static void Start()
{
var container = UnityConfig.GetConfiguredContainer();
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
/// <summary>Disposes the Unity container when the application is shut down.</summary>
public static void Shutdown()
{
var container = UnityConfig.GetConfiguredContainer();
container.Dispose();
}
}
}
EDIT
I attempted to run the application by using a concrete implementation of the class, but the problem persists.
private UnitOfWork worker;
public HomeController() {
this.worker = new UnitOfWork();
}
Just change one line in your RegisterTypes method:
container.RegisterType<IUnitOfWork, UnitOfWork>(new TransientLifetimeManager());
or try changing to
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerThreadLifetimeManager());
What is happening here is that multiple threads are trying to share the same DbContext. The second one tries querying the model while the model is still being built on first thread. The solution would be to make sure that no 2 threads share the same DbContext. If the DbContext would be different on different threads, each one would build and query model on its own.
Finally figured it out. I neglected to mark a customer property on my class as [NotMapped].
public HttpPostedFileBase cardImg { get; set; }
The correction simply involved adding the attribute.
[NotMapped]
public HttpPostedFileBase cardImg { get; set; }
I was under the impression that the following error:
Value cannot be null.
Parameter name: entitySet
was caused by the context issues. Evidently, it was the reverse; the null parameter issue was causing "Context cannot be used" error. This is a good summary of resolving issues related to null entitySet parameters (which can cause the context issues noted above).
The solution was simple, but, it would not have been my initial guess based upon the error message. As such, I hope somebody may be able to gain some use insight from my foolish mistake.

How to add stash to the actor in Akka.Net with Windsor Container

Here is my try to add a Stash to the actor:
class Program
{
static void Main(string[] args)
{
var actorSystem = ActorSystem.Create("mySystem");
var container = new WindsorContainer();
container.Install(new CommonWindsorInstaller());
// ReSharper disable once ObjectCreationAsStatement
new WindsorDependencyResolver(container, actorSystem);
var rootActor = actorSystem.ActorOf(actorSystem.DI().Props<RootActor>(), typeof(RootActor).Name);
rootActor.Tell(new CreateChildMessage());
Console.ReadLine();
}
}
internal class CreateChildMessage
{
}
internal class CommonWindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<RootActor>().Named("RootActor").LifestyleTransient());
container.Register(Component.For<ChildActor>().Named("ChildActor").LifestyleTransient());
container.Register(Component.For<IChildActor>().ImplementedBy<ChildActor>().Named("IChildActor").LifestyleTransient());
}
}
internal class RootActor : ReceiveActor
{
public RootActor()
{
Receive<CreateChildMessage>(
m =>
{
var child = ActorHelper.CreateActor(Context, typeof(IChildActor), "child");
});
}
}
public interface IChildActor
{
}
public class ChildActor : ReceiveActor, IChildActor, IWithUnboundedStash
{
public IStash Stash { get; set; }
}
public static class ActorHelper
{
public static IActorRef CreateActor(IUntypedActorContext context, Type actorType, string name)
{
var actorName = actorType.Name + "." + name;
var actor = context.Child(actorName);
if (!actor.Equals(ActorRefs.Nobody))
{
return actor;
}
var actorProps = context.DI().Props(actorType);
actor = context.ActorOf(actorProps, actorName);
return actor;
}
}
Unfortunately, I'm getting following exception:
[ERROR][17.02.2017 9:39:19][Thread 0004][ActorSystem(mySystem)] An exception occurred while trying to apply plugin of type Akka.Actor.ActorStashPlugin to the newly created actor (Type = Akka.Stash.ChildActor, Path = akka://mySystem/user/RootActor/IChildActor.child)
Cause: System.NotSupportedException: DequeBasedMailbox required, got: Mailbox
An (unbounded) deque-based mailbox can be configured as follows:
my-custom-mailbox {
mailbox-type = "Akka.Dispatch.UnboundedDequeBasedMailbox"
}
at Akka.Actor.Internal.AbstractStash..ctor(IActorContext context, Int32 capacity)
at Akka.Actor.StashFactory.CreateStash(IActorContext context, Type actorType)
at Akka.Actor.ActorStashPlugin.AfterIncarnated(ActorBase actor, IActorContext context)
at Akka.Actor.ActorProducerPipeline.AfterActorIncarnated(ActorBase actor, IActorContext context)
EDIT
I can get rid of this error by making my IChildActor descendant of IWithUnboundedStash, i.e.
public interface IChildActor: IWithUnboundedStash
{
}
public class ChildActor : ReceiveActor, IChildActor
{
public IStash Stash { get; set; }
}
But it's not the option, I don't want to all my IChildActor implementations has a stash
EDIT 2
Here is the GitHub repository to reproduce https://github.com/bonza/Akka.Net.CastleWindsor.Stash/

Automapper ValueResolver with dependency resolved via StructureMap

This is what I am currently using (simplified and run in console app):
public class SomeValueResolver : ValueResolver<DateTime, long>
{
private readonly ISomeDependency _someDependency;
public SomeValueResolver(ISomeDependency _someDependency)
{
// ...
}
protected override long ResolveCore(DateTime source)
{
// ...
}
}
public class MyRegistry : Registry
{
public MyRegistry()
{
For<ISomeDependency >()
.Singleton()
.Use<SomeDependency>();
}
}
public static class AutoMapperConfiguration
{
public static void Configure(IContainer container)
{
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(t => container.GetInstance(t));
cfg.AddProfile(new AutomapperProfile1());
});
}
}
public class AutomapperProfile1 : Profile
{
protected override void Configure()
{
CreateMap<Source, Target>()
.ForMember(dest => dest.Y, opt => opt.ResolveUsing<SomeValueResolver>().FromMember(e => e.X))
.IgnoreAllSourcePropertiesWithAnInaccessibleSetter();
}
}
public class Source
{
public DateTime X { get; set; }
}
public class Target
{
public DateTime Y { get; set; }
}
// main method
var container1 = new Container(new MyRegistry());
AutoMapperConfiguration.Configure(container1);
var source = new Source { X = DateTime.UtcNow };
var target = Mapper.Map<Target>(source);
Unfortunately, I get an exception along those lines:
Unable to create a build plan for concrete type SomeValueResolver
new SomeValueResolver(ISomeDependency)
? ISomeDependency= **Default**
1.) Attempting to create a BuildPlan for Instance of SomeValueResolver -- SomeValueResolver
2.) Container.GetInstance(SomeValueResolver)
Can this be resolved (pardon the pun).
I've tried your code with StructureMap 4.0.1.318 and Automapper 4.2.0.0.
I did get an exception which is different because related to bad conversion of DateTime to Int64.
I think you intended to write this :
public class Target
{
public long Y { get; set; }
}
By changing the type, the mapping works like a charm.
It's maybe related to the SomeDependency class which should possess a parameterless constructor to be resolved that way.

Setting the connection string of a DBContext in my repository class using Ninject

I have an MVC 5 application that uses EF 6 and implements Repository pattern with dependency injection using the DI container Ninject. The connection string for the dbcontext is stored in the Web.config file which the EF Context properly finds. Everything works fine. Lately, I have a requirement that the connection to my DBContext need to be determined at runtime and connect to different databases (but with exactly the same structure). So, I need to change the sql connectionstring part from the entity connectionstring at run-time before the repository is instantiated. I would really appreciate some help in doing it. I am not a DI guru; know just enough Ninject to get my things going.
Here is my Repository Base Interface:
public interface IRepositoryBase<T> where T : class
{
void Add(T entity, string userGuid = "");
void Delete(T entity);
// ... removed other method signatures for brevity
}
My Repository base implementation:
public abstract class RepositoryBase<D, T> : IRepositoryBase<T>, IDisposable
where T : class
where D : DbContext, new()
{
private Guid? currUserGuid = null;
private D dataContext;
protected D DataContext
{
get
{
if (dataContext == null)
dataContext = new D();
return dataContext;
}
set { dataContext = value; }
}
public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate)
{
return DataContext.Set<T>().Where(predicate);
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = DataContext.Set<T>();
return query;
}
public virtual void Delete(T entity)
{
OperationStatus stat = TryDelete(entity);
}
// .... removed rest for brevity
}
Interface and implementation for concrete class:
public interface ICustomerRepository : IRepositoryBase<Customer>
{
Customer GetCustomerAndStatus( Guid custGuid );
}
public class CustomerRepository : RepositoryBase<PCDataEFContext, Customer>, ICustomerRepository
{
public Customer GetCustomerAndStatus( Guid custGuid )
{
return DataContext.Customers.Include( x => x.CustStatusType )
.SingleOrDefault( x => x.PKGuid == custGuid );
}
}
My Ninject dependency resolver:
public class NinjectDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver()
{
kernel = new StandardKernel();
AddBindings();
}
public IKernel Kernel { get { return kernel; } }
private void AddBindings()
{
kernel.Bind<ICustomerRepository>().To<CustomerRepository>();
// ... other bindings are omitted for brevity
}
}
and finally, here is my Entity Framework generated DBContext:
public partial class PCDataEFContext : DbContext
{
public PCDataEFContext()
: base("name=PCDataEFContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Customer> Customers { get; set; }
}
All the above code works great! But as I said in the beginning, I don't know how to inject the connection string into my Repositorybase class at runtime so that I don't have to modify any of my inherited repositories (I have plenty of them). Someone please help.
Babu.
Could you do it like this?
public partial class PCDataEFContext : DbContext
{
public PCDataEFContext()
: base(Util.GetTheConnectionString())
{ }
}
public class MyDerivedContext : PCDataEFContext
{
public MyDerivedContext()
: base()
{ }
}
class Util
{
public static string GetTheConnectionString()
{
// return the correct name based on some logic...
return "name=PCDataEFContext";
}
}
Another way of doing it, could be in the RepositorBase class you defined, by altering the connectionstring after the creation of the dbcontext:
protected D DataContext
{
get
{
if (dataContext == null)
{
dataContext = new D();
dataContext.Database.Connection.ConnectionString = "the new connectionstring";
}
return dataContext;
}
set { dataContext = value; }
}

Registering different UnitOfWorks per each module for a generic CommandHandler using structuremap

I'm using CQRS pattern in my recent project, and used EF code first in my DAL, so I defined some generic CommandHandlers to do Insert/Update/Delete:
public class InsertCommandHandler<TEntity> : ICommandHandler<InsertCommandParameter<TEntity>>
where TEntity : BaseEntity, IAggregateRoot<TEntity>, new()
{
private readonly IUnitOfWork _uow;
public InsertCommandHandler(IUnitOfWork uow)
{
_uow = uow;
}
public void Handle(InsertCommandParameter<TEntity> parameter)
{
var entity = parameter.Entity;
_uow.Repository<TEntity>().Add(entity);
}
}
public interface ICommandParameter
{
}
public abstract class BaseEntityCommandParameter<T> : ICommandParameter
where T : BaseEntity, new()
{
public T Entity { get; set; }
protected BaseEntityCommandParameter()
{
Entity = new T();
}
}
public class InsertCommandParameter<T> : BaseEntityCommandParameter<T> where T : class, new()
{
}
As you see I injected the IUnitOfWork to the InsertCommandHandler constructor.
public interface IUnitOfWork : IDisposable
{
IRepository<T> Repository<T>() where T : BaseEntity, IAggregateRoot<T>,new ();
void Commit();
}
I used Structuremap 3 as my IoC Container, So I defined following conversion to resolve ICommandHandlers for each BaseEntity types(using custom registration conventions for partially closed types):
public class CRUDCommandRegistrationConvention : StructureMap.Graph.IRegistrationConvention
{
private static readonly
Type _openHandlerInterfaceType = typeof(ICommandHandler<>);
private static readonly
Type _openInsertCommandType = typeof(InsertCommandParameter<>);
private static readonly
Type _openInsertCommandHandlerType = typeof(InsertCommandHandler<>);
private static readonly
Type _openUpdateCommandType = typeof(UpdateCommandParameter<>);
private static readonly
Type _openUpdateCommandHandlerType = typeof(UpdateCommandHandler<>);
private static readonly
Type _openDeleteCommandType = typeof(DeleteCommandParameter<>);
private static readonly
Type _openDeleteCommandHandlerType = typeof(DeleteCommandHandler<>);
public void Process(Type type, Registry registry)
{
if (!type.IsAbstract && typeof(BaseEntity).IsAssignableFrom(type))
if (type.GetInterfaces()
.Any(x => x.IsGenericType && x.GetGenericTypeDefinition()
== typeof(IAggregateRoot<>)))
{
Type closedInsertCommandType = _openInsertCommandType.MakeGenericType(type);
Type closedInsertCommandHandlerType = _openInsertCommandHandlerType.MakeGenericType(type);
Type closedUpdateCommandType = _openUpdateCommandType.MakeGenericType(type);
Type closedUpdateCommandHandlerType = _openUpdateCommandHandlerType.MakeGenericType(type);
Type closedDeleteCommandType = _openDeleteCommandType.MakeGenericType(type);
Type closedDeleteCommandHandlerType = _openDeleteCommandHandlerType.MakeGenericType(type);
Type insertclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(closedInsertCommandType);
Type updateclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(closedUpdateCommandType);
Type deleteclosedHandlerInterfaceType = _openHandlerInterfaceType.MakeGenericType(closedDeleteCommandType);
registry.For(insertclosedHandlerInterfaceType).Use(closedInsertCommandHandlerType);
registry.For(updateclosedHandlerInterfaceType).Use(closedUpdateCommandHandlerType);
registry.For(deleteclosedHandlerInterfaceType).Use(closedDeleteCommandHandlerType);
}
}
}
And used it in my CompositionRoot:
public static class ApplicationConfiguration
{
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(s =>
{
s.AssemblyContainingType(typeof(ICommandHandler<>));
s.AssemblyContainingType(typeof(Order));
s.AssemblyContainingType(typeof(FindOrderByIdQueryHandler));
s.WithDefaultConventions();
x.For(typeof(IUnitOfWork))
.Use(typeof(EfUnitOfWork<SaleDBContext>))
.Named("SaleDBContext")
.SetLifecycleTo((Lifecycles.Singleton));
s.Convention<CRUDCommandRegistrationConvention>();
});
});
return ObjectFactory.Container;
}
public static T Resolve<T>()
{
return ObjectFactory.GetInstance<T>();
}
}
I registered EfUnitOfWork<SaleDBContext> for IUnitOfWork, but I want to use separate DbContext per each module in my solution(Bounded context). For example my sale module has its own DbContext, HR module has its own DbContext and etc, and above registration conversion, only register EfUnitOfWork<SaleDBContext> as my IUnitOfWork.
I have some modules(Solution Folders in Visual Studio) in my solution and each module has 3 layer(3 class library projects):
My modules has following structure(each module has 3 assemblies) for example:
SaleModule:
----Application
----Domain (Entities , ...) //Order, Customer,...
----DAL (DbContext ,...) //SaleDbContext
HRModule:
----Application
----Domain (Entities , ...) // Employee, OrganizationUnit, ...
----DAL (DbContext ,...)//HRDbContext
InfrastructureModule:
----Application (ICommandHandler,IQueryHandler,...)
----Domain
----DAL
The InsertCommandHandler<T> puts in Infrastructure Module.
When I use the InsertCommanHandler<T> I want it uses corresponding module's DbContext as IUnitOfWork. for example, I want the InsertCommandHandler<Order> uses SaleDbContext as it's IUnitOfWork and InsertCommandHandler<Employee> uses HRDbContext as it's IUnitOfWork.
[UPDATED]
This is a sample of cunsumers code that IoC containar should provide SaleDbContext for Consumer1 and HRDbContext for Consumer2:
public class Consumer1
{
ICommandHandler<InsertCommandParameter<Order>> _insertCommandHandler;
public Consumer1(ICommandHandler<InsertCommandParameter<Order>> insertCommandHandler)
{
_insertCommandHandler = insertCommandHandler;
}
public void DoInsert()
{
var command = new InsertCommandParameter<Order>();
command.Entity = new Order(){
Number = 'ord-01',
// other properties
};
insertCommandHandler.Handle(command); //this query handler should use SaleDbContext
}
}
public class Consumer2
{
ICommandHandler<InsertCommandParameter<Employee>> _insertCommandHandler;
public Consumer2(ICommandHandler<InsertCommandParameter<Employee>> insertCommandHandler)
{
_insertCommandHandler = insertCommandHandler;
}
public void DoInsert()
{
var command = new InsertCommandParameter<Employee>();
command.Entity = new Employee(){
EmployeeNumber = 'Emp1',
// other properties
};
insertCommandHandler.Handle(command); //this query handler should use HRDbContext
}
}
How could I do that in my composition root using StructureMap?
You can make IUnitOfWork generic as in IUnitOfWork<TConnection>. This allows each Repository to stipulate which UnitOfWork it requires, ideally using constructor injection, e.g.
public class InsertCommandHandler : ICommandHandler<Order>
{
public InsertCommandHandler(IUnitOfWork<SalesDbContext> salesUnitOfWork)
{
// ...
}
}
However, you probably don't want to reference the DbContext in each handler so you should define an abstraction to avoid such a dependency.
Start with a simple interface that all DbContext wrapper classes will implement
public interface IConnection
{
DbContext Context { get; }
}
Update IUnitOfWork accordingly
public interface IUnitOfWork<TConnection> where TConnection : IConnection { }
Here's an example wrapper
public class SalesConnection : IConnection
{
private readonly DbContext context;
public SalesConnection()
{
this.context = new SalesDbContext();
}
public DbContext Context { get { return this.context; } }
}
And here's what the updated command handler will look like
public class InsertCommandHandler : ICommandHandler<Order>
{
public InsertCommandHandler(IUnitOfWork<SalesConnection> salesUnitOfWork)
{
// ...
}
}
UPDATE
The logical thing to do for common handlers is to have one per logical domain (i.e. per DbContext), for example SalesInsertCommandHandler, HRInsertCommandHandler
public class SalesInsertCommandHandler<TCommand> : ICommandHandler<TCommand>
{
public SalesInsertCommandHandler(IUnitOfWork<SalesConnection> unitOfWork)
{
}
}
This adheres to the separation of concerns principle and gives you extra flexibility when you come to decorate your concerns with different aspects (tracing, retry logic etc.)
All command handlers can of course inherit from a single common (abstract) command handler.
public abstract class CommandHandler<TConnection, TCommand> :
ICommandHandler<TCommand>
where TConnection : IConnection
{
private readonly IUnitOfWork<TConnection> unitOfWork;
public CommandHandler(IUnitOfWork<TConnection> unitOfWork)
{
this.unitOfWork = unitOfWork;
}
}
public class SalesInsertCommandHandler<TCommand> :
CommandHandler<SalesConnection, TCommand>
{
}

Categories

Resources