Entity Framework: Использование транзакций и откатов ... Возможно? - PullRequest
18 голосов
/ 17 января 2009

Выпуск: (с Sql 2005)

  • Как запросить базу данных, когда транзакция завершена? (Так как он блокирует стол)
  • Как вызвать откат транзакции, а затем закрыть себя, чтобы можно было запросить таблицу?

Итак, я нашел это очень много:

[TestMethod]
public void CreateUser()
{
    TransactionScope transactionScope = new TransactionScope();

    DataContextHandler.Context.AddToForumUser(userToTest);
    DataContextHandler.Context.SaveChanges();

    DataContextHandler.Context.Dispose();
}

Где DataContextHandler - это простой синглтон, который предоставляет объект контекста для моих объектов. Кажется, это работает так, как вы думаете. Он создает пользователя, сохраняет, а затем откатывается, когда программа заканчивается. (IE тест завершен)

Проблема: как заставить транзакцию откатиться и уничтожить себя, чтобы я мог запросить таблицу?

Причина: в целях тестирования я хочу убедиться, что пользователь:

  • Сохранено
  • Может быть запрошен правильно, чтобы доказать его существование
  • Удалено (Нежелательные данные)
  • Можно запросить, чтобы убедиться, что оно было удалено.

На данный момент я могу получить откат транзакции только после завершения теста И не могу понять, как выполнить запрос с транзакцией вверх:

[TestMethod]
public void CreateUser()
{
    ForumUser userToTest = new ForumUser();

    TransactionScope transactionScope = new TransactionScope();

    DataContextHandler.Context.AddToForumUser(userToTest);
    DataContextHandler.Context.SaveChanges();     

    Assert.IsTrue(userToTest.UserID > 0);

    var foundUser = (from user in DataContextHandler.Context.ForumUser
                    where user.UserID == userToTest.UserID
                    select user).Count();  //KABOOM Can't query since the 
                                           //transaction has the table locked.

    Assert.IsTrue(foundUser == 1);

    DataContextHandler.Context.Dispose();

    var after = (from user in DataContextHandler.Context.ForumUser
                 where user.UserID == userToTest.UserID
                 select user).Count(); //KABOOM Can't query since the 
                                       //transaction has the table locked.

    Assert.IsTrue(after == 0);
}

ОБНОВЛЕНИЕ Это работало для отката и проверки, но все еще не может выполнить запрос в секции using:

using(TransactionScope transactionScope = new TransactionScope())
{
    DataContextHandler.Context.AddToForumUser(userToTest);
    DataContextHandler.Context.SaveChanges();
    Assert.IsTrue(userToTest.UserID > 0);
    //Still can't query here.

}

var after = (from user in DataContextHandler.Context.ForumUser
            where user.UserID == userToTest.UserID
            select user).Count();

Assert.IsTrue(after == 0);

Ответы [ 3 ]

15 голосов
/ 26 января 2009

С MSDN ;

"SaveChanges работает в транзакции. SaveChanges откатит эту транзакцию и выдаст исключение, если какой-либо из грязных объектов ObjectStateEntry не может быть сохранен."

Так что, похоже, нет необходимости явно добавлять свою собственную обработку транзакций через TransactionScope.

7 голосов
/ 17 декабря 2010

В моем случае я удаляю все записи из таблицы с помощью простого SQL, поскольку EF не предоставляет для этого функциональности. После этого я добавляю несколько новых сущностей - но когда это не удается, таблица не должна быть пустой. Использование MSDTC (TransactionScope) кажется мне невозможным. Я сократил транзакцию до БД:

Мой код:

using (var transaction = context.Connection.BeginTransaction())
{
      // delete all
      base.DeleteAll<TESTEntity>(context);

      // add all
      foreach (var item in items)
      {
           context.TESTEntity.AddObject(item);
      }

      try
      {
           context.SaveChanges();
           transaction.Commit();
           return true;
      }
      catch (Exception ex)
      {
           Logger.Write("Error in Save: " + ex, "Error");
           transaction.Rollback();
           return false;
      }
}

А вот вспомогательные функции

    protected void DeleteAll<TEntity>(ObjectContext context) where TEntity : class
    {
        string tableName = GetTableName<TEntity>(context);
        int rc = context.ExecuteStoreCommand(string.Format(CultureInfo.InvariantCulture, "DELETE FROM {0}", tableName));
    }

    protected string GetTableName<TEntity>(ObjectContext context) where TEntity : class
    {
        string snippet = "FROM [dbo].[";

        string sql = context.CreateObjectSet<TEntity>().ToTraceString();
        string sqlFirstPart = sql.Substring(sql.IndexOf(snippet) + snippet.Length);
        string tableName = sqlFirstPart.Substring(0, sqlFirstPart.IndexOf("]"));
        return tableName;
    }
5 голосов
/ 28 октября 2011

Мне удалось решить очень похожую проблему, используя этот фрагмент кода:

var connection = new EntityConnection("name=MyEntities");
connection.Open();
var tran = connection.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted);

try
{
    var dataContext = new MyEntities(connection);

    //CRUD operation1

    //CRUD operation2

    //CRUD operation3 ...

    Assert.AreEqual(expected, actual);
}
catch
{
    throw;
}
finally
{
    tran.Rollback();
    connection.Close();
}

Где MyEntities - EF DataModel. Важной частью является настройка транзакции: System.Data.IsolationLevel.ReadUncommitted.
Используя этот уровень изоляции, SQL-запросы могут считывать изменения, сделанные внутри транзакции. Также вам нужно явно создать соединение, как я делаю в первой и второй строке. Я не мог заставить его работать, используя, к сожалению, TransactionScope 1008 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...