【问题标题】:Simultaneous data operation in SQLite and SQL Server databases using Entity Framework and Repository Pattern使用实体框架和存储库模式在 SQLite 和 SQL Server 数据库中同时进行数据操作
【发布时间】:2021-02-05 08:41:23
【问题描述】:

我正在开发一个 .net 核心项目,该项目的要求是同时维护一个 SQLite 数据库和一个 SQL Server 数据库。我创建了两个 DbContext 文件 SqlServerContext 和 SqliteContext 并为它们创建了单独的迁移文件夹。这些文件派生自 DbContext 派生的 WorkerContext 文件。迁移工作正常,因为在两个数据库中都创建了表。但我无法同时进行数据操作。

这是 IKeyboardMouseActivityRepository。使用 SqliteContext 和 SqlServerContext 有不同的部分。使用另一部分时,我必须注释掉其中一部分。所以我现在可以一次在一个数据库中输入数据。

public interface IKeyboardMouseActivityRepository : 
    IRepository<KeyboardMouseActivity, Guid, SqlServerContext> 
//     IRepository<KeyboardMouseActivity, Guid, SqliteContext>
{ 
}

public class KeyboardMouseActivityRepository :  
    IKeyboardMouseActivityRepository,
    Repository<KeyboardMouseActivity, Guid, SqlServerContext>
//  Repository<KeyboardMouseActivity, Guid, SqliteContext>
{
    public KeyboardMouseActivityRepository(SqlServerContext dbContext)
        : base(dbContext)
    {
    }

    //  public KeyboardMouseActivityRepository(SqliteContext dbContext)
    //      : base(dbContext)
    //  {

    //  }
}

这是主要的 Repository 类。

public abstract class Repository<TEntity, TKey, TContext>
    : IRepository<TEntity, TKey, TContext>
    where TEntity : class, IEntity<TKey>
    where TContext : DbContext
{
    protected TContext _dbContext;
    protected DbSet<TEntity> _dbSet;

    public Repository(TContext context)
    {
        _dbContext = context;
        _dbSet = _dbContext.Set<TEntity>();
    }
    
    // other methods such as Add, Remove etc.
}

我的理解是,由于在 KeyboardMouseActivityRepository 中指定了 context 参数,因此它仅适用于指定的上下文。如何修改它以使其适用于两个 DbContext 文件,并且我可以同时在两个数据库中进行数据操作?

【问题讨论】:

    标签: entity-framework .net-core repository-pattern


    【解决方案1】:

    您定义的存储库是按 DbContext 键入的。如果您想要一个可以更新两个已知 DbContext 实现的存储库,那么您可以放弃 DbContexts 的通用方法并实现存储库以在构造函数中接受每个:

    public abstract class Repository<TEntity, TKey>
        : IRepository<TEntity, TKey>
        where TEntity : class, IEntity<TKey>
    {
        protected SqlAppDbContext _sqlContext;
        protected SqlLiteAppDbContext _sqlLiteContext;
        protected DbSet<TEntity> _sqlDbSet;
        protected DbSet<TEntity> _sqlLiteDbSet;
    
        public Repository(SqlAppDbContext sqlContext, SqlLiteAppDbContext sqlLiteContext)
        {
            _sqlContext = sqlContext ?? throw new ArgumentNullException("sqlContext");
            _sqlLiteContext = sqlLiteContext ?? throw new ArgumentNullException("sqlLiteContext");
            _sqlDbSet = _sqlContext.Set<TEntity>();
            _sqlLiteDbSet = _sqlLiteContext.Set<TEntity>();
        }
        
        // other methods such as Add, Remove etc.
    }
    

    请注意,您需要调查和实施类似TransactionScope 的内容,以帮助确保通过存储库完成的操作相互提交或回滚。例如,如果您的代码尝试更新 DbSets 和 SaveChanges 中的数据,如果一个成功而另一个因任何原因失败,通常期望它们都回滚。我希望读取将一个 DbSet 优先于另一个,但是如果您想要支持来自一台服务器或另一台服务器的故障转移或情境负载等功能,那么如果您有可能从其中获取实体,您将会遇到问题一个 DbContext 曾经与从另一个获取的实体结合在一起。 (_sqlContext 加载的实体不能与 _sqlLiteContext 加载的实体相关联)当更新实体并通过导航属性关联它们时,您将加载所有内容两次,或者在 DbContext 之间进行分离和重新连接实体的非常危险的错误游戏。

    我建议不要使用通用存储库模式 /w EF。这将使您陷入各种困境,这将限制 EF 可以提供的用于优化查询、处理投影以及高效执行分页、过滤、排序等操作的许多功能,而无需大量额外代码或引入相当复杂的代码进入存储库。

    总体而言,我祝您项目顺利,但这样的需求和设计将成为您的时间和理智的饥饿龙巢。 :)

    【讨论】:

    • 你对龙的看法是完全正确的!非常感谢,我现在已经可以使用了。
    猜你喜欢
    • 2013-11-03
    • 1970-01-01
    • 2014-06-04
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多