简体   繁体   中英

Ninject WCF self hosted injection

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

So after some more debugging and testing i managed to solve this myself. here's a what i did and what i found:

I started looking at the ninject scope and tried all the available options, none of them worked. next step was skip the binderClasses and manually link all my Interfaces and implementations. At first this was also no go, so i started playing with the scope setting again.

I got the whole thing working with the manual binding an in RequestScope. Of course manual binding was not what i wanted.

after some more testing i have this

    private static StandardKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        ConfigurationAction scope = bind => bind.InRequestScope();

        /* this works*/
       scope(
            kernel.Bind(typeof(SecurityContext))
                .ToSelf());

        /*
         * This works
         * 
         * kernel.Bind(typeof(SecurityContext))
            .ToSelf()
            .InRequestScope();*/
        /*
         * This does not work
        kernel.Load(new ContextBinder(scope));
         */
        kernel.Load(new UnitOfWorkBinder(scope));
        kernel.Load(new RepositoryBinder(scope));
        kernel.Load(new ServiceBinder(scope));



        return kernel;
    }

I have no Idea why binding the context in the contextbinder create a separate context for every instance it needs. So if anyone could clarify.

I marked this as resolved because the code above is working for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM