【问题标题】:TransactionScope with EntityFramework 6 and MySqlTransactionScope 与 EntityFramework 6 和 MySql
【发布时间】:2015-03-02 09:57:12
【问题描述】:

我对一个项目有以下要求:使用EntityFramework在MySql中的多个数据库上执行事务(所有数据库都在同一个mysql服务器上)。

在尝试使用 TransactionScope 解决问题时,似乎存在不同 MySql 连接字符串的问题:

“当前不支持多个同时连接或同一事务内具有不同连接字符串的连接。”

这里已经描述了这个问题(没有任何具体的解决方案): How do I use TransactionScope with MySql and Entity Framework? (getting Multiple simultaneous connections...are not currently supported error)

作为一种解决方法,我尝试从连接字符串中省略数据库参数,如果我在调用 .SaveChanges() 方法之前打开连接并设置数据库(通过在从 DbContext 继承的类中重载该方法),则该方法有效.但是为每个 select 语句调用相同的语句是不可行的。

我的自定义类如下所示:

public class ContextBase : DbContext
{
    public ContextBase(string connectionStringWithoutDatabase)
        : base(connectionStringWithoutDatabase)
    {}

    public override int SaveChanges()
    {
        Database.Connection.Open();
        Database.Connection.ChangeDatabase("MyDatabaseName");
        base.SaveChanges();
        Database.Connection.Close();
    }

    // How to handle Selects?
}

我的工作单元类:

public class UnitOfWork
{
    private IEnumerable<DbContext> ContextList
    {
        get { return _contextList; }
    }

    private readonly IEnumerable<DbContext> _contextList;
    public UnitOfWork(IEnumerable<DbContext> contextList)
    {
        _contextList = contextList;
    }

    public void Save()
    {
        var transactionScope = new TransactionScope();
        foreach (DbContext context in ContextList)
        {
            context.SaveChanges();
        }
        transactionScope.Complete();
        transactionScope.Dispose();
    }
}

另一种可能的解决方法是创建一个包装器,其中有两个 DbContext 实例 - 一个为 select 语句设置数据库,另一个不用于非查询操作。但这感觉不对。

现在我的问题:

  • 有没有更简单的方法来进行此类交易?

  • 是否可以在 select 语句发生之前设置数据库名称?在构造函数中打开连接可行吗?

  • 带有两个 DbContext 的包装器开销是否太大?

【问题讨论】:

    标签: mysql .net transactions entity-framework-6


    【解决方案1】:

    我尝试使用 Devart 库。这个对我有用。 以下是我运行的代码。

    不工作 - MySql.Data.MySqlClient.MySqlConnection

    using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required))
    {
        MySql.Data.MySqlClient.MySqlConnection connect1 = new MySql.Data.MySqlClient.MySqlConnection("Server=192.168.0.1;Database=db1;Uid=root;Pwd=root;");
        MySql.Data.MySqlClient.MySqlConnection connect2 = new MySql.Data.MySqlClient.MySqlConnection("Server=192.168.0.2;Database=db2;Uid=root;Pwd=root;");
        connect1.Open();
        connect2.Open();
    
    
        var command1 = connect1.CreateCommand();
        var command2 = connect2.CreateCommand();
    
        command1.CommandText = "INSERT INTO test01(`Value`) VALUES(SYSDATE());";
        command2.CommandText = "INSERT INTO test02(`Value`) VALUES(SYSDATE())";
    
        command2.ExecuteNonQuery();
        throw new Exception("bbbbbb");
        command1.ExecuteNonQuery();
    
        scope.Complete();
    }
    

    工作 - Devart.Data.MySql.MySqlConnection

    using (System.Transactions.TransactionScope scope = new System.Transactions.TransactionScope(TransactionScopeOption.Required))
    {
        Devart.Data.MySql.MySqlConnection connect1 = new Devart.Data.MySql.MySqlConnection("Server=192.168.0.1;Database=db1;Uid=root;Pwd=root;");
        Devart.Data.MySql.MySqlConnection connect2 = new Devart.Data.MySql.MySqlConnection("Server=192.168.0.2;Database=db2;Uid=root;Pwd=root;");
        connect1.Open();
        connect2.Open();
    
    
        var command1 = connect1.CreateCommand();
        var command2 = connect2.CreateCommand();
    
        command1.CommandText = "INSERT INTO test01(`Value`) VALUES(SYSDATE());";
        command2.CommandText = "INSERT INTO test02(`Value`) VALUES(SYSDATE())";
    
        command2.ExecuteNonQuery();
        throw new Exception("bbbbbb");
        command1.ExecuteNonQuery();
    
        scope.Complete();
    }
    

    【讨论】:

    • 感谢您提出的解决方案。但是 atm 的 Devart 解决方案对我们来说是不可行的,因为已经存在可以满足我们大部分需要的免费解决方案(除了所描述的问题)。
    【解决方案2】:

    我们最终找到了解决在每次选择之前设置数据库名称的问题。要实现这一点,必须创建一个实现IDbCommandInterceptor 的类并将其注册到您的上下文中(我们在DbContext 的构造函数中使用this.AddInterceptor(new DatabaseSetterCommandInterceptor()); 完成了它)。在该界面的不同功能中,您可以在将 SQL 发送到服务器之前更改数据库。 粗略的测试也表明没有明显的性能下降。

    【讨论】:

    • 当你说 register with context with 是什么意思? - DbConfiguration.AddInterceptor?
    • 我们在DbContext的构造函数中注册了this.AddInterceptor(new DatabaseSetterCommandInterceptor());
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    相关资源
    最近更新 更多