Невозможно использовать DbContext.Query внутри транзакции

Я использую EF6 для запроса внутренней базы данных. Пользователь может настроить временную таблицу и запросить данные из временной таблицы. я использую

DataTable result = context.Query(queryStatement);

чтобы получить результат, и он работал нормально.

Теперь запрос необходим среди множества других sqlcommand и необходима транзакция. Так что я

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/сохранение/транзакции В нем говорится:

Entity Framework does not wrap queries in a transaction.

Является ли это причиной этой проблемы?

Как я могу использовать 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;
            }
        }
    }

IDE: Visual Studio 2015. Ядро ASP.Net: 1.0

Tony 11.04.2019 00:31

Я приложил минимальный код, который может воспроизвести проблему.

Tony 11.04.2019 14:48
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
1 281
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Возможно вы пропустили этот раздел -

you are free to execute database operations either directly on the SqlConnection itself, or on the DbContext. All such operations are executed within one transaction. You take responsibility for committing or rolling back the transaction and for calling Dispose() on it, as well as for closing and disposing the database connection

И затем эта кодовая база -

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);

В этом примере показано, как использовать внешнюю транзакцию для DbContext. Я не использую внешнюю транзакцию, а использую внутреннюю (tran = context.Database.BeginTranasction(). Поэтому использовать транзакцию (транзакция, созданная мной) не имеет смысла.

Tony 11.04.2019 00:53

У меня такое ощущение, что context.Query не входит в область контекста. Транзакция базы данных, но я не знаю, как указать ту же транзакцию на уровне контекста..... В худшем случае я могу попытаться создать транзакцию снаружи и НЕ используйте dbContext.Database.BeginTransaction(), вместо этого используйте dbContext.Database.UseTransaction(OutsideTran) и посмотрите, будет ли это работать или нет.

Tony 11.04.2019 00:59

пробовал, не повезло. кажется, даже я установил транзакцию извне, dbcontext всегда может ее обойти, и кажется, что функция не может выполняться внутри какой-либо транзакции.... странно...

Tony 11.04.2019 02:02

Вместо того, чтобы удалять некоторые строки, не могли бы вы обновить вопрос с фактическим кодом. У меня ощущение, что что-то еще не так.

brainless coder 11.04.2019 02:05

Тем не менее, есть одна вещь, на которую следует обратить внимание: вы как бы смешиваете коды из мира, необработанных SQL-запросов и ORM. Обычно это плохой дизайн. Либо не используйте EF и используйте ADO.net, либо используйте EF и используйте его с POCO.

brainless coder 11.04.2019 02:08

Я использую EF везде, но для запроса неизвестной таблицы EF не может ее архивировать. я должен использовать Query() напрямую.

Tony 11.04.2019 02:28
Ответ принят как подходящий

Извините, ребята, как упоминалось выше, я думал, что метод 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;
        }
    }
}

Другие вопросы по теме