Я использую 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;
}
}
}
Я приложил минимальный код, который может воспроизвести проблему.
Возможно вы пропустили этот раздел -
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(). Поэтому использовать транзакцию (транзакция, созданная мной) не имеет смысла.
У меня такое ощущение, что context.Query не входит в область контекста. Транзакция базы данных, но я не знаю, как указать ту же транзакцию на уровне контекста..... В худшем случае я могу попытаться создать транзакцию снаружи и НЕ используйте dbContext.Database.BeginTransaction(), вместо этого используйте dbContext.Database.UseTransaction(OutsideTran) и посмотрите, будет ли это работать или нет.
пробовал, не повезло. кажется, даже я установил транзакцию извне, dbcontext всегда может ее обойти, и кажется, что функция не может выполняться внутри какой-либо транзакции.... странно...
Вместо того, чтобы удалять некоторые строки, не могли бы вы обновить вопрос с фактическим кодом. У меня ощущение, что что-то еще не так.
Тем не менее, есть одна вещь, на которую следует обратить внимание: вы как бы смешиваете коды из мира, необработанных SQL-запросов и ORM. Обычно это плохой дизайн. Либо не используйте EF и используйте ADO.net, либо используйте EF и используйте его с POCO.
Я использую EF везде, но для запроса неизвестной таблицы EF не может ее архивировать. я должен использовать Query() напрямую.
Извините, ребята, как упоминалось выше, я думал, что метод 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;
}
}
}
IDE: Visual Studio 2015. Ядро ASP.Net: 1.0