0

我正在使用 EF6 查询后端数据库。用户可以自定义临时表并从临时表中查询数据。我在用

DataTable result = context.Query(queryStatement);

得到结果,它一直工作正常。

现在需要在一系列其他sql命令中进行查询,并且需要一个事务。所以我有

public static DataTable GetData()
{
    using (MyDbContext context = new MyDbContext())
    using (DbContextTransaction tran = context.Database.BeginTransaction())
    {
        try
        {
            int rowAffected = context.Database.ExecuteSqlCommand(
                "UPDATE [MyDb].dbo.[TableLocks] SET RefCount = RefCount + 1 WHERE TableName = 'TESTTABLE1'");
            if (rowAffected != 1)
                throw new Exception("Cannot find 'TestTable1'");

            //The following line will raise an exception
            DataTable result = context.Query("SELECT TOP 100 * FROM [MyDb].dbo.[TestTable1]");
            //This line will work if I change it to 
            //context.Database.ExecuteSqlCommand("SELECT TOP 100 * FROM [MyDb].dbo.[TestTable1]");
            //but I don't know how to get the result out of it.
            context.Database.ExecuteSqlCommand(
                "UPDATE [MyDb].dbo.[TableLocks] SET RefCount = RefCount - 1 WHERE TableName = 'TestTable1'");
            tran.Commit();

            return result;
        }
        catch (Exception ex)
        {
            tran.Rollback();
            throw (ex);
        }
    }
}

但这会在执行 context.Query 时引发异常

ExecuteReader requires the command to have a transaction when the connection 
assigned to the command is in a pending local transaction.  The Transaction 
property of the command has not been initialized.

当我阅读这篇文章时:https ://docs.microsoft.com/en-us/ef/ef6/saving/transactions 它说:

实体框架不会将查询包装在事务中。

是造成这个问题的原因吗?

如何context.Query()在交易中使用?

我还能用什么?

我尝试了所有其他方法,但它们都不起作用 - 因为无法事先预测返回数据类型。

我刚刚意识到,Query 方法是在 MyDbContext 中定义的!

    public DataTable Query(string sqlQuery)
    {
        DbProviderFactory dbFactory = DbProviderFactories.GetFactory(Database.Connection);

        using (var cmd = dbFactory.CreateCommand())
        {
            cmd.Connection = Database.Connection;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sqlQuery;
            using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
            {
                adapter.SelectCommand = cmd;

                DataTable dt = new DataTable();
                adapter.Fill(dt);

                return dt;
            }
        }
    }
4

2 回答 2

1

可能你错过了这个部分 -

您可以直接在 SqlConnection 本身或 DbContext 上自由执行数据库操作。所有这些操作都在一个事务中执行。您负责提交或回滚事务并对其调用 Dispose() 以及关闭和释放数据库连接

然后这个代码库 -

using (var conn = new SqlConnection("..."))
{
    conn.Open();

    using (var sqlTxn = 
    conn.BeginTransaction(System.Data.IsolationLevel.Snapshot))
    {
        try
        {
            var sqlCommand = new SqlCommand();
            sqlCommand.Connection = conn;
            sqlCommand.Transaction = sqlTxn;
            sqlCommand.CommandText =
                       @"UPDATE Blogs SET Rating = 5" +
                        " WHERE Name LIKE '%Entity Framework%'";
            sqlCommand.ExecuteNonQuery();

            using (var context =  
                new BloggingContext(conn, contextOwnsConnection: false))
            {
                        context.Database.UseTransaction(sqlTxn);

                        var query =  context.Posts.Where(p => p.Blog.Rating >= 5);
                        foreach (var post in query)
                        {
                            post.Title += "[Cool Blog]";
                        }
                       context.SaveChanges();
                    }

                    sqlTxn.Commit();
        }
        catch (Exception)
        {
             sqlTxn.Rollback();
        }
    }
}

特别是这个——

context.Database.UseTransaction(sqlTxn);
于 2019-04-10T22:47:19.327 回答
0

抱歉,如上所述,我认为 Query 方法来自 EF,但我检查了代码,发现它实际上是由另一个开发人员编写的,在 MyDbContext 类中定义。由于这个类是由 EF 生成的,我从不认为有人添加了一个方法。

这是

public DataTable Query(string sqlQuery)
{
    DbProviderFactory dbFactory = DbProviderFactories.GetFactory(Database.Connection);

    using (var cmd = dbFactory.CreateCommand())
    {
        cmd.Connection = Database.Connection;
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = sqlQuery;

        //And I added this line, then problem solved.
        if (Database.CurrentTransaction != null)
            cmd.Transaction = Database.CurrentTransaction.UnderlyingTransaction;

        using (DbDataAdapter adapter = dbFactory.CreateDataAdapter())
        {
            adapter.SelectCommand = cmd;

            DataTable dt = new DataTable();
            adapter.Fill(dt);

            return dt;
        }
    }
}
于 2019-04-11T15:13:16.833 回答