使用 Entity Framework 和存储库模式在 SQLite 和 SQL 服务器数据库中同时进行数据操作

Simultaneous data operation in SQLite and SQL Server databases using Entity Framework and Repository Pattern

我正在从事一个 .net 核心项目,该项目的要求是同时维护一个 SQLite 数据库和一个 SQL 服务器数据库。我创建了两个 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)
    //  {

    //  }
}

这是主存储库 class。

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.
}

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

您定义的存储库是按 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 的内容,以帮助确保通过存储库完成的操作相互提交或回滚。例如,如果您的代码尝试更新 DbSet 和 SaveChanges 中的数据,如果一个成功而另一个因任何原因失败,通常期望它们都回滚。阅读我希望优先考虑一个 DbSet 而不是另一个,但是如果你想要支持来自一台服务器或另一台服务器的故障转移或情境负载之类的东西,你将 运行 遇到问题,如果可能的话从一个 DbContext 中获取的实体曾经与从另一个 DbContext 中获取的实体结合在一起。 (_sqlContext 加载的实体不能与 _sqlLiteContext 加载的实体相关联)更新实体并通过导航属性关联它们时,您将加载所有内容两次,或者玩一个非常危险的容易出错的游戏,即在 DbContext 之间分离和重新附加实体。

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

总的来说,我祝你在这个项目中好运,但是像这样的需求和设计将成为你时间和理智的饥饿龙的巢穴。 :)