I have build a self hosted WCF service which consumes a unit of work with all my repositories in it. The repositories use code first EF to connect to the database. I am using the Ninject.Extensions.Wcf.SelfHost package to start the service and get the injection working.
Everything works just fine until i want to commit something to the database. I can read records from the database, but writing does not work. After digging and debugging i found that my db context is not shared between the unit of work and the repositories. So when i commit in my unit of work the context has no changes to commit.
any advice?
And here the code:
Startup code for the service
private static void StartNinjectSelfHosted(string address)
{
var service =
NinjectWcfConfiguration.Create<SecurityService, NinjectServiceSelfHostFactory>(
serviceHost =>
serviceHost.AddServiceEndpoint(typeof(ISecurityService), new BasicHttpBinding(), address));
selfHosted = new NinjectSelfHostBootstrapper(CreateKernel, service);
selfHosted.Start();
serviceAddress = address;
}
private static StandardKernel CreateKernel()
{
var kernel = new StandardKernel();
ConfigurationAction scope = bind => bind.InRequestScope();
kernel.Load((new NinjectModule[]
{
new ContextBinder(scope),
new ServiceBinder(scope) ,
new UnitOfWorkBinder(scope),
new RepositoryBinder(scope),
}));
return kernel;
}
Binders
public class ContextBinder : NinjectModule
{
private readonly ConfigurationAction _bindInScope;
public ContextBinder(ConfigurationAction bindInScope)
{
_bindInScope = bindInScope;
}
public override void Load()
{
Kernel.Bind(typeof(SecurityContext)).ToSelf().InSingletonScope();
}
}
public class ServiceBinder : NinjectModule
{
private readonly ConfigurationAction _configurationAction;
public ServiceBinder(ConfigurationAction configurationAction)
{
_configurationAction = configurationAction;
}
public override void Load()
{
Kernel.Bind(
x => x.FromAssembliesMatching("WcfInterfaces*")
.SelectAllInterfaces()
.Join.FromAssembliesMatching("*Facade*")
.SelectAllClasses()
.BindDefaultInterface()
.Configure(_configurationAction));
}
}
public class UnitOfWorkBinder : NinjectModule
{
private readonly ConfigurationAction _configurationAction;
public UnitOfWorkBinder(ConfigurationAction configurationAction)
{
_configurationAction = configurationAction;
}
public override void Load()
{
Kernel.Bind(x => x
/** Select all unit of work interfaces */
.FromAssembliesMatching("SecurityDomain*")
.SelectAllUnitOfWorkInterfaces()
/** Select all unit of work implementations */
.Join.FromAssembliesMatching("SecurityImplementation*")
.SelectAllUnitOfWorkImplementations()
/** Bind interfaces to implementations */
.BindDefaultInterface()
/** Configure the scope */
.Configure(_configurationAction));
}
}
public class RepositoryBinder : NinjectModule
{
private readonly ConfigurationAction _configurationAction;
public RepositoryBinder(ConfigurationAction configurationAction)
{
_configurationAction = configurationAction;
}
public override void Load()
{
Kernel.Bind(x => x
/** Select all default repository interfaces */
.FromAssembliesMatching("SecurityDomain*")
.SelectAllRepositoryInterfaces()
/** Select all repository implementations */
.Join.FromAssembliesMatching("SecurityImplementation*")
.SelectAllRepositoryImplementations()
/** Bind interfaces to implementations */
.BindDefaultInterface()
/** Configure the scope */
.Configure(_configurationAction));
}
}
Unit of work
public class UnitOfWork : IUnitOfWork
{
private readonly SecurityContext _context;
public UnitOfWork(SecurityContext context, ISecurityUnitOfWork security)
{
Console.WriteLine("*** Unit Of Work ContextHash: {0}***", context.Hash);
_context = context;
Security = security;
}
public void Commit(int userId)
{
Console.WriteLine("Context hash {0}", _context.Hash);
using (var transaction = _context.Database.BeginTransaction())
{
try
{
DateTime now = DateTime.Now;
foreach (var entry in _context.ChangeTracker.Entries<Entity>())
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreationDate = now;
entry.Entity.CreationUserId = userId;
break;
case EntityState.Modified:
entry.Entity.ModificationDate = now;
entry.Entity.ModificationUserId = userId;
break;
case EntityState.Deleted:
entry.State = EntityState.Modified;
entry.Entity.Deleted = true;
break;
}
}
_context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
}
}
public ISecurityUnitOfWork Security { get; private set; }
}
Security Unit of work
public class SecurityUnitOfWork : ISecurityUnitOfWork
{
public SecurityUnitOfWork(IAccountRepository accounts, IRoleRepository roles, IRightRepository rights, IUserRepository users, IApplicationRepository applications)
{
Applications = applications;
Users = users;
Rights = rights;
Roles = roles;
Accounts = accounts;
}
public IAccountRepository Accounts { get; private set; }
public IRoleRepository Roles { get; private set; }
public IRightRepository Rights { get; private set; }
public IUserRepository Users { get; private set; }
public IApplicationRepository Applications { get; private set; }
}
Repositories
public class AccountRepository : GenericRepository<SecurityContext, Account>, IAccountRepository
{
public AccountRepository(SecurityContext context)
: base(context)
{
}
}
public class GenericRepository<TContext, TEntity> : IGenericRepository<TEntity>
where TContext : DbContext
where TEntity : class, IDeletable, IIdentifiable
{
private readonly TContext _context;
private readonly DbSet<TEntity> _entitySet;
private IQueryable<TEntity> _entities;
public GenericRepository(TContext context)
{
_context = context;
_entitySet = context.Set<TEntity>();
_entities = _entitySet;
}
/// <summary>
/// Gets the DbContext
/// </summary>
protected virtual TContext Context
{
get { return _context; }
}
/// <summary>
/// Gets the entities
/// </summary>
protected virtual IQueryable<TEntity> Entities
{
get { return _entities; }
set { _entities = value; }
}
/// <summary>
/// Gets the editable dbset
/// </summary>
public virtual IDbSet<TEntity> EntitySet
{
get { return _entitySet; }
}
/// <summary>
/// Gets the entities
/// </summary>
protected virtual IQueryable<TEntity> Process(IEntityFilter<TEntity> filter = null, IEntitySorter<TEntity> sorter = null, IEntityIncluder<TEntity> includer = null)
{
var entities = _entities.Where(x => !x.Deleted);
if (includer != null)
entities = includer.AddInclusions(entities);
if (filter != null)
entities = filter.Filter(entities);
if (sorter != null)
entities = sorter.Sort(entities);
return entities;
}
public virtual IQueryable<TEntity> List(IEntitySorter<TEntity> sorter = null, IEntityFilter<TEntity> filter = null, int? page = null, int? pageSize = null, IEntityIncluder<TEntity> includer = null)
{
if ((page.HasValue || pageSize.HasValue) && sorter == null)
{
throw new ArgumentException("You have to define a sorting order if you specify a page or pageSize! (IEntitySorter was null)");
}
if (page.HasValue && !pageSize.HasValue)
{
throw new ArgumentException("You have to define a pageSize if you specify a page!");
}
var entities = Process(filter, sorter, includer);
if (page != null)
entities = entities.Skip(pageSize.Value * page.Value);
if (pageSize != null)
entities = entities.Take(pageSize.Value);
return entities;
}
public virtual int Count(IEntityFilter<TEntity> filter = null)
{
return Process(filter).Count();
}
public bool Any(IEntityFilter<TEntity> filter = null)
{
return Process(filter).Any();
}
public TEntity SingleOrDefault(IEntityFilter<TEntity> filter = null, IEntityIncluder<TEntity> includer = null)
{
return Process(filter, includer: includer).SingleOrDefault();
}
public TEntity Single(IEntityFilter<TEntity> filter = null, IEntityIncluder<TEntity> includer = null)
{
return Process(filter, includer: includer).Single();
}
public TEntity FirstOrDefault(IEntityFilter<TEntity> filter = null, IEntitySorter<TEntity> sorter = null, IEntityIncluder<TEntity> includer = null)
{
return Process(filter, sorter, includer).FirstOrDefault();
}
public TEntity First(IEntityFilter<TEntity> filter = null, IEntitySorter<TEntity> sorter = null, IEntityIncluder<TEntity> includer = null)
{
return Process(filter, sorter, includer).First();
}
public virtual TEntity Find(int id)
{
var entity = EntitySet.FirstOrDefault(x => x.Id == id);
if (entity != null && entity.Deleted)
{
return null;
}
return entity;
}
public virtual void AddOrUpdate(TEntity entity)
{
if (entity.Id == 0)
{
Add(entity);
}
else
{
Update(entity);
}
}
public virtual void Delete(TEntity entity)
{
entity.Deleted = true;
Update(entity);
}
public virtual void Delete(IEnumerable<TEntity> entities)
{
foreach (TEntity entity in entities)
{
Delete(entity);
}
}
public virtual void Delete(int id)
{
TEntity entity = Find(id);
if (entity != null)
Delete(entity);
}
public virtual void HardDelete(TEntity entity)
{
DbEntityEntry entry = Context.Entry(entity);
if (entry.State != EntityState.Deleted)
{
entry.State = EntityState.Deleted;
}
else
{
EntitySet.Attach(entity);
}
}
public virtual void HardDelete(int id)
{
TEntity entity = Find(id);
if (entity != null)
HardDelete(entity);
}
public TResult Query<TResult>(Func<IQueryable<TEntity>, TResult> query)
{
return query(Entities);
}
/// <summary>
/// Gets the queryable entities
/// </summary>
public IQueryable<TEntity> QueryableEntities
{
get
{
return _entitySet;
}
}
protected virtual void Add(TEntity entity)
{
DbEntityEntry entry = Context.Entry(entity);
if (entry.State != EntityState.Detached)
{
entry.State = EntityState.Added;
}
else
{
EntitySet.Add(entity);
}
}
protected virtual void Update(TEntity entity)
{
DbEntityEntry entry = Context.Entry(entity);
if (entry.State == EntityState.Detached)
{
EntitySet.Attach(entity);
}
entry.State = EntityState.Modified;
}
}
when i start the service this is the output
Starting service
**** CONTEXT CONSTRUCTED, HASH:63174400 ****
**** CONTEXT CONSTRUCTED, HASH:24275713 ****
**** CONTEXT CONSTRUCTED, HASH:34631232 ****
**** CONTEXT CONSTRUCTED, HASH:66590816 ****
**** CONTEXT CONSTRUCTED, HASH:24695352 ****
**** CONTEXT CONSTRUCTED, HASH:11985038 ****
*** Unit Of Work ContextHash: 63174400***
--------------------------------
Security service is running @ http://localhost/security