web-dev-qa-db-ja.com

エンティティフレームワーク:トランザクションとロールバックを使用しています...可能ですか?

問題:(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);
}

PDATEこれはロールバックとチェックで機能しましたが、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);
18
Programmin Tool

から [〜#〜] msdn [〜#〜] ;

「SaveChangesはトランザクション内で動作します。ダーティなObjectStateEntryオブジェクトのいずれかを永続化できない場合、SaveChangesはそのトランザクションをロールバックし、例外をスローします。」

したがって、TransactionScopeを介して独自のトランザクション処理を明示的に追加する必要はないようです。

15
Jeroen Huinink

私の場合、EFはこの機能を提供していないため、プレーンSQLを使用してテーブルからすべてのレコードを削除します。その後、いくつかの新しいエンティティを追加しますが、失敗した場合、テーブルは空であってはなりません。 MSDTC(TransactionScope)の使用は私には不可能のようです。トランザクションをDBに減らしました:

私のコード:

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;
    }
7
Sven

私はこのコードフラグメントを使用して非常に類似した問題を解決することができました:

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はEFDataModelです。重要な部分は、トランザクションの設定です:System.Data.IsolationLevel.ReadUncommitted
この分離レベルを使用すると、SQLクエリでトランザクション内で行われた変更を読み取ることができます。また、1行目と2行目で行うように、明示的に接続を作成する必要があります。残念ながら、TransactionScopeを使用して動作させることはできませんでした。

5
ViktorZ