簡體   English   中英

將多個DbContext與通用存儲庫和工作單元一起使用

[英]Using multiple DbContexts with a generic repository and unit of work

我的應用程序越來越大,到目前為止,我只有一個MyDbContext ,其中包含我的應用程序中需要的所有表。 我希望(出於概述的目的)將它們分成多個DbContext ,例如MainDbContextEstateModuleDbContextAnotherModuleDbContextUserDbContext

我不確定如何完成此操作,因為我現在正在使用依賴注入(ninject)將DbContext放在UnitOfWork類上,例如:

kernel.Bind(typeof(IUnitOfWork)).To(typeof(UnitOfWork<MyDbContext>));

我應該放棄依賴注入這種方法,並顯式設置要在服務中使用的DbContext例如:

private readonly EstateService _estateService;

public HomeController()
{
    IUnitOfWork uow = new UnitOfWork<MyDbContext>();
    _estateService = new EstateService(uow);
}

代替:

private readonly EstateService _estateService;

public HomeController(IUnitOfWork uow)
{
    _estateService = new EstateService(uow);
}

還是這還有另一種更好的方法? 還有一個附帶的問題,我不喜歡將uow通知我-服務是否有另一種(更好的)方法?

我有這個IDbContext和MyDbContext:

public interface IDbContext
{
    DbSet<T> Set<T>() where T : class;

    DbEntityEntry<T> Entry<T>(T entity) where T : class;

    int SaveChanges();

    void Dispose();
}

public class MyDbContext : DbContext, IDbContext
{
    public DbSet<Table1> Table1 { get; set; }
    public DbSet<Table2> Table1 { get; set; }
    public DbSet<Table3> Table1 { get; set; }
    public DbSet<Table4> Table1 { get; set; }
    public DbSet<Table5> Table1 { get; set; }
    /* and so on */

    static MyDbContext()
    {
        Database.SetInitializer<MyDbContext>(new CreateDatabaseIfNotExists<MyDbContext>());
    }

    public MyDbContext()
        : base("MyDbContext")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

    }
}

然后我有這個IRepository和實現:

public interface IRepository<T> where T : class
{
    IQueryable<T> GetAll();

    void Add(T entity);

    void Delete(T entity);

    void DeleteAll(IEnumerable<T> entity);

    void Update(T entity);

    bool Any();
}

public class Repository<T> : IRepository<T> where T : class
{
    private readonly IDbContext _context;
    private readonly IDbSet<T> _dbset;

    public Repository(IDbContext context)
    {
        _context = context;
        _dbset = context.Set<T>();
    }

    public virtual IQueryable<T> GetAll()
    {
        return _dbset;
    }

    public virtual void Add(T entity)
    {
        _dbset.Add(entity);
    }

    public virtual void Delete(T entity)
    {
        var entry = _context.Entry(entity);
        entry.State = EntityState.Deleted;
        _dbset.Remove(entity);
    }

    public virtual void DeleteAll(IEnumerable<T> entity)
    {
        foreach (var ent in entity)
        {
            var entry = _context.Entry(ent);
            entry.State = EntityState.Deleted;
            _dbset.Remove(ent);
        }
    }

    public virtual void Update(T entity)
    {
        var entry = _context.Entry(entity);
        _dbset.Attach(entity);
        entry.State = EntityState.Modified;
    }

    public virtual bool Any()
    {
        return _dbset.Any();
    }
}

還有IUnitOfWork和實現,它處理DbContext完成的工作

public interface IUnitOfWork : IDisposable
{
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;

    void Save();
}

public class UnitOfWork<TContext> : IUnitOfWork where TContext : IDbContext, new()
{
    private readonly IDbContext _ctx;
    private readonly Dictionary<Type, object> _repositories;
    private bool _disposed;

    public UnitOfWork()
    {
        _ctx = new TContext();
        _repositories = new Dictionary<Type, object>();
        _disposed = false;
    }

    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        // Checks if the Dictionary Key contains the Model class
        if (_repositories.Keys.Contains(typeof(TEntity)))
        {
            // Return the repository for that Model class
            return _repositories[typeof(TEntity)] as IRepository<TEntity>;
        }

        // If the repository for that Model class doesn't exist, create it
        var repository = new Repository<TEntity>(_ctx);

        // Add it to the dictionary
        _repositories.Add(typeof(TEntity), repository);

        return repository;
    }

    public void Save()
    {
        _ctx.SaveChanges();
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (this._disposed) return;

        if (disposing)
        {
            _ctx.Dispose();
        }

        this._disposed = true;
    }
} 

除非有邏輯上的接縫,否則請勿將模塊化數據片段拆分為多個DbContext 從實體DbContextA不能與實體自動導航或集合屬性DbContextB 如果拆分上下文,則您的代碼必須負責手動執行約束並在上下文之間加載相關數據。

對於“概述”(也就是保持理智),您仍然可以按模塊組織CLR代碼和數據庫表。 對於POCO,請將其保存在不同名稱空間下的不同文件夾中。 對於表,可以按架構分組。 (但是,在通過SQL模式進行組織時,您可能還應該考慮安全性。例如,如果有任何數據庫用戶應限制對某些表的訪問,請根據這些規則設計模式。)然后,您可以執行此操作建立模型時:

ToTable("TableName", "SchemaName"); // put table under SchemaName, not dbo

只有一個單獨去DbContext當它的實體有在你第一次的任何實體沒有關系DbContext

我也同意Wiktor,因為我不喜歡您的界面和實現設計。 我特別不喜歡public interface IRepository<T> 另外,為什么還要聲明多個public DbSet<TableN> TableN { get; set; } public DbSet<TableN> TableN { get; set; } public DbSet<TableN> TableN { get; set; }在您的MyDbContext 請幫我一個忙,閱讀這篇文章 ,然后閱讀這篇 文章

您可以使用如下所示的EF接口設計極大地簡化代碼:

interface IUnitOfWork
{
    int SaveChanges();
}
interface IQueryEntities
{
    IQueryable<T> Query<T>(); // implementation returns Set<T>().AsNoTracking()
    IQueryable<T> EagerLoad<T>(IQueryable<T> queryable, Expression<Func<T, object>> expression); // implementation returns queryable.Include(expression)
}
interface ICommandEntities : IQueryEntities, IUnitOfWork
{
    T Find<T>(params object[] keyValues);
    IQueryable<T> FindMany<T>(); // implementation returns Set<T>() without .AsNoTracking()
    void Create<T>(T entity); // implementation changes Entry(entity).State
    void Update<T>(T entity); // implementation changes Entry(entity).State
    void Delete<T>(T entity); // implementation changes Entry(entity).State
    void Reload<T>(T entity); // implementation invokes Entry(entity).Reload
}

如果聲明MyDbContext : ICommandEntities ,則只需設置一些方法來實現該接口(通常是單行)。 然后,您可以IQueryEntities 3個接口中的任何一個注入到服務實現中:通常, ICommandEntities用於具有副作用的操作,而IQueryEntities用於沒有副作用的操作。 任何僅負責保存狀態的服務(或服務裝飾器)都可以依賴IUnitOfWork 我不同意Controller盡管應該依賴IUnitOfWork 使用以上設計,您的服務應在返回Controller之前保存更改。

如果您的應用中有多個單獨的DbContext類有意義,則可以按照Wiktor的建議進行操作 ,並使上述接口通用。 然后,您可以像這樣將依賴項注入服務中:

public SomeServiceClass(IQueryEntities<UserEntities> users,
    ICommandEntities<EstateModuleEntities> estateModule) { ... }

public SomeControllerClass(SomeServiceClass service) { ... }

// Ninject will automatically constructor inject service instance into controller
// you don't need to pass arguments to the service constructor from controller

創建寬的每個聚合(甚至每個實體更糟)的存儲庫接口可以與EF對抗,增加無聊的管道代碼,並過度注入構造函數。 相反,給您的服務更大的靈活性。 諸如.Any()類的方法不屬於該接口,您可以在服務方法中調用Query<T>FindMany<T>返回的IQueryable<T>上的擴展。

您的工作單元界面不是通用的,但是實現是通用的。 解決此問題的最簡單方法是決定並遵循相同的約定。

例如,也使您的接口通用。 這樣,您可以將三個不同的接口(具有三個不同的通用參數的同一接口)注冊到三個不同的實現:

 container.Bind( typeof<IUnitOfWork<ContextOne>> ).To( typeof<UnitOfWork<ContextOne>> );
 ...

是的,將您的工作單元注入控制器/服務中是一個好主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM