【问题标题】:How to inject same dbcontext instance between Service and Repository?如何在 Service 和 Repository 之间注入相同的 dbcontext 实例?
【发布时间】:2015-09-05 23:23:41
【问题描述】:

嗨,我正在做一个项目,目前我正在使用实体框架构建数据层

数据库上下文

public interface IDatabaseContext : IDisposable 
    {        
        IDbSet<TestEntity> TestEntitys { get; }
        int SaveChanges();
    }



public class DatabaseContext : DbContext, IDatabaseContext
    {
        static DatabaseContext()
        {
            System.Data.Entity.Database.SetInitializer<DatabaseContext>(null);
        }

        public DatabaseContext(string connectionString) :
            base(connectionString)
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {          

            modelBuilder.Configurations.Add(new TestEntityConfiguration());
            base.OnModelCreating(modelBuilder);
        }                  

        public IDbSet<TestEntity> TestEntitys
        {
            get;
            set;
        }

        public override int SaveChanges()
        {           

            return base.SaveChanges();
        }

    }

工作单元:

public interface IUnitOfWork : IDisposable
{   
    int Commit();
    IDatabaseContext Context { get; }
}

  public sealed class UnitOfWork : IUnitOfWork, IDisposable
{

    private  IDatabaseContext _dbContext;
    private bool disposed = false;

    public UnitOfWork(IDatabaseContext mydbContext)
    {    
         //var builder=DataBaseConnectionStringBuilder();
         //var factory = new DataBaseConnectionFactory(builder);      

        _dbContext = mydbContext;

    }

    public int Commit()
    {

        return _dbContext.SaveChanges();
    }

    public IDatabaseContext Context
    {
        get { return _dbContext; }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    private void Dispose(bool disposing)
    {     
        if (!disposed)
        {
            if (disposing)
            {
                if (_dbContext != null)
                {
                    _dbContext.Dispose();
                    _dbContext = null;
                }
            }
        }
        disposed = true;
    }
}

服务:

public class Baseservice()
{
    protacted IUnitOfWork unitofwork;
    public BaseService(IUnitOfWork uow)
    {
        unitofwork= uow;
    }
}
public class TestEntityservice() : BaseService
{
    private ITestEntityReopsitory _testEntityReopsitory;
    public TestEntityservice(IUnitOfWork uow,ITestEntityReopsitory testEntityReopsitory):base(uow)
    {
        _testEntityReopsitory=testEntityReopsitory;
    }
    public int AddNewData(somedto dto){

        var result=_testEntityReopsitory.AddTEst(dto);
        return  unitofwork.Commit();
    }
}

存储库:

 public BaseRepository(IDatabaseContext context)
        {
            _dbContext = context;            
        }
public class TestEntityReopsitory() : BaseRepository, ITestEntityReopsitory
{

    public TestEntityReopsitory(IDatabaseContext context) :base(context) {        
    }
    public int AddTEst(somedto dto){
        var entity = new TestEntity()
        {
            a= dto.a,
            b= dto.b
        }
        context.Add(entity);
    }
}   

我担心的是,我在存储库中注入了我的数据库上下文(DatabaseContext),所以当存储库解析时,它将有一个单独的实例 存储库中的 dbcontext 然后服务 .所以这意味着我将实体添加到单独的上下文并使用不同的上下文进行保存。

如何确保我在存储库和服务中使用相同的实例,并且仍然保持相同的实现。

【问题讨论】:

  • 您也可以尝试NInject 或任何其他依赖注入器来归档与工厂类相同的想法。

标签: c# entity-framework dependency-injection repository-pattern


【解决方案1】:

工作单元存储库等模式有多种实现方式。 F.e.您可以实现返回活动记录的只读存储库,这些存储库具有UpdateDelete 方法。或者您可以实现返回普通旧对象的存储库,因此具有方法UpdateDelete。最后,您可以实现返回普通旧对象的只读存储库,因此方法UpdateDelete 应在工作单元中声明。

如何组织存储库和工作单元的交互?这可以通过结合模式 registryunit of work 来实现:

public interface IUnitOfWork
{
    IUserRepository UserRepository { get; }

    IDataRepository DataRepository { get; }

    void Commit();
}

或者您可以将工作单元用作服务定位器

public interface IUnitOfWork
{
    TRepository GetRepository<TRepository>() where TRepository: IRepository;

    void Commit();
}

或者您可以为工作单元显式指定读写存储库:

public interface IUnitOfWork
{
    void Join(IRepository repository);

    void Commit();
}

在最后一种情况下,您可以使用带有工作单元的存储库,也可以不使用它们。在所有这些情况下,您都可以在存储库和工作单元之间共享相同的 DbContext

【讨论】:

  • 谢谢你,马克,但我希望我的工作单元和存储库相互独立,但仍然希望它们共享 dbcontext,你能看看我的代码吗?我已经修改了它位。
【解决方案2】:

在您的 UnitOfWork 类中,不要创建工厂然后使用它来创建 IDatabaseContext 对象,而是使用构造函数注入来注入 DatabaseContext。像这样:

public UnitOfWork(IDatabaseContext db_context)
{    
    _dbContext = db_context;
}

同样为 Baseservice(以及 TestEntityservice)做同样的事情,它们应该在它们的构造函数中注入 IUnitOfWork,它们不应该自己创建 UnitOfWork。这就是依赖注入的意义所在。

确保使用构造函数注入后,您现在可以在composition root 中组合整个对象图。执行此操作时,请确保对 UnitOfWork 和服务使用相同的 DatabaseContext 实例。 Pure DI 是一种很好的组合方式。

下面是使用 Pure DI 的方法:

    var connection_string = "some connection string";

    using (IDatabaseContext context = new DatabaseContext(connection_string))
    {
        IUnitOfWork unit_of_work = new UnitOfWork(context);

        ITestEntityReopsitory reopsitory = new TestEntityReopsitory(context);

        ITestEntityservice service = new TestEntityservice(unit_of_work, reopsitory);

        //Consume service here
        ....
    }

【讨论】:

  • 感谢您的回复。请原谅我,我不完全理解作文根。假设我使用构造函数注入了我的依赖项。但我的问题还是一样,我想我不明白如何在我的服务和存储库之间使用相同的数据库上下文实例。
  • 使用构造函数注入后,您可以使用以下内容创建对象图: var context = new DatabaseContext(); var service = new TestEntityService(new CodelookupRepository(context), new UnitOfWork(context))。这里的想法是创建一次上下文,然后多次使用同一个实例来构造对象。
  • 对了,你用的是DI容器吗?
  • 是的,我正在使用 unity,请您现在看看我的代码。我已按照您的建议进行了更改。
  • 注册 DatabaseContext 时需要使用 PerResolveLifetimeManager(当您使用 Unity Register 方法将 IDatabaseContext 映射到 DatabaseContext 时)。还要确保使用 IDatabaseContext 而不是 DatabaseContext 注入 TestEntityReopsitory。另外,在解析时,请确保容器知道如何在调用 Resolve 方法时使用 ParameterOverride 来解析 connectionString。
【解决方案3】:

这样做的一种方法是在存储库实例之间共享工作单元,这将允许您按如下方式创建代码

using (IUnitOfWork unitOfWork = _UnitOfWorkFactory.CreateInstance()) {
   var repository = _RepositoryFactory.CreateRepository<YourType>(unitOfWork);
   var repositoryTwo = _RepositoryFactory.CreateRepository<YourOtherType>(unitOfWork);
    unitOfWork.Commit();
}

存储库工厂

BaseRepository IRepositoryFactory.CreateRepository<T>(IUnitOfWork unitOfWork) {
       //Create Instance Here
}

【讨论】:

  • 感谢您的回复我已经稍微修改了我的代码,您现在可以看看。
  • @bforcode 将它们注入基类的问题是你强制你的代码只能使用一个上下文,这可能并不总是你想要的。在某些情况下,您可能希望在完全独立的上下文中调用数据库。
猜你喜欢
  • 2013-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 2019-04-03
  • 2018-08-22
相关资源
最近更新 更多