使用 BeginTransaction() 的 Entity Framework

3
我正在尝试在我的应用程序中实现Entity Framework,并且我应该能够手动提交和回滚更改。
第一次执行更新语句时,它成功地更新了表格,并且我能够手动回滚更改。这是正确的。
但第二次执行更新语句时,它成功地更新了表格并提交了更改。因此,我无法手动回滚。这是错误的。
请告诉我为什么会出现这种情况以及如何解决此问题。
下面的代码只是为了重现我的问题。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Data;

namespace EFTest
{
    public class DBOperations
    {
        NorthwindEntities NorthwindContext;
        DbTransaction transObject;

        public DBOperations()
        {
        }

        public void ConnectDB()
        {
            try
            {
                if (NorthwindContext == null)
                {
                    NorthwindContext = new NorthwindEntities();
                    if (NorthwindContext != null && NorthwindContext.Connection.State != ConnectionState.Open)
                    {
                        NorthwindContext.Connection.Open();
                        transObject = NorthwindContext.Connection.BeginTransaction(IsolationLevel.ReadUncommitted);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Database Error " + ex.Message);
            }
        }

        public int disconnect()
        {
            if (NorthwindContext != null && transObject != null)
            {
                try
                {
                    transObject.Rollback();
                }
                catch (Exception)
                {
                }
                transObject.Dispose();
                NorthwindContext.Connection.Close();
                NorthwindContext.Dispose();
            }

            return 0;
        }

        public void CommitTransaction()
        {
            if (NorthwindContext != null && transObject != null)
            {
                try
                {
                    transObject.Commit();
                }
                catch (Exception)
                {
                }
            }
        }

        public void RollbackTransaction()
        {
            if (NorthwindContext != null && transObject != null)
            {
                try
                {
                    transObject.Rollback();
                }
                catch (Exception)
                {
                }
            }
        }

        public int UpdateDB()
        {
            int _returnVal = 0;


            try
            {
                NorthwindContext.ExecuteStoreCommand("UPDATE Orders SET OrderDate = GETDATE() WHERE OrderID = '10248'");
            }
            catch (Exception ex)
            {
                throw new Exception("Database Error " + ex.Message);
            }

            return _returnVal;
        }
    }

    public class program
    {
        public program()
        {
            //Establishing the connection.
            DBOperations _DBOperations = new DBOperations();
            _DBOperations.ConnectDB();

            //Update the datebase
            _DBOperations.UpdateDB();                           //Update the database but it doesn't commit the changes.                       

            //Issue Rollback to rollback the transaction.
            _DBOperations.RollbackTransaction();                //Successfully Rollbacks the database changes.


            //Again Update the datebase
            _DBOperations.UpdateDB();                           //Update the database it commits the changes. 

            //Issue Rollback to rollback the transaction.
            _DBOperations.RollbackTransaction();                //Rollback fails.

        }
    }
}

使用TransactionScope。 - Gert Arnold
@GertArnold 是的,我考虑过了,但是如何使用TransactionScope手动提交或回滚呢? - ksvimal
提交时,您只需完成事务范围中的 Complete() 操作。如果您在未完成事务范围的情况下释放了 TS,则会回滚。 - Gert Arnold
问题:你只执行存储命令吗? - Gert Arnold
@GertArnold 不是的。我也在使用Context.SaveChanges(),但在某些情况下,我会使用存储命令来检查表中是否锁定了特定行。我对EntityframeWork还很陌生,所以我正在尝试构建最佳逻辑。我认为最好有多个上下文实例,也许每个屏幕一个上下文。如果我错了,请纠正我。 - ksvimal
2个回答

1

使用 TransactionScope,您的数据库操作可以如下所示:

public class DBOperations : IDisposable
{
    NorthwindEntities _context;
    private TransactionScope _transactionScope;

    public DBOperations()
    {
        this.Initialize();
    }

    private void Initialize()
    {
        try
        {
            this.Dispose();
            this._transactionScope = new TransactionScope();
            this._context = new NorthwindEntities();
            // no need to open connection. Let EF manage that.
        }
        catch (Exception ex)
        {
            throw new Exception("Database Error " + ex.Message);
        }
    }

    public void RollbackTransaction()
    {
            try
            {
                this._transactionScope.Dispose();
                this._transactionScope = null;
                this.Dispose();
                this.Initialize();
            }
            catch (Exception)
            {
                // TODO
            }
    }

    public int UpdateDB()
    {
        int _returnVal = 0;
        try
        {
            this._context.ExecuteStoreCommand("UPDATE Orders SET OrderDate = GETDATE() WHERE OrderID = '10248'");
        }
        catch (Exception ex)
        {
            throw new Exception("Database Error " + ex.Message);
        }
        return _returnVal;
    }

    public void Dispose()
    {
        if (this._transactionScope != null)
        {
            this._transactionScope.Complete();
            this._transactionScope.Dispose();
        }
        if (this._context != null) this._context.Dispose();
    }
}

还有这个程序:

public class program
{
    public program()
    {
        using (DBOperations dbOperations = new DBOperations())
        {
            dbOperations.UpdateDB(); // Update the database no commit.

            dbOperations.RollbackTransaction(); // Rollback.

            dbOperations.UpdateDB(); // Update the database no commit.

            dbOperations.RollbackTransaction(); // Rollback.
        } // Commit on Dispose.
    }
}

在TransactionScope的范围内打开的连接会自动加入事务。只有调用Complete()才能提交事务。处理不当或未处理异常会导致回滚。
如果做的不仅是存储命令,例如更改对象并依赖于上下文的更改跟踪,则可能需要实现重试机制,而不仅仅是丢弃上下文和更改。

不,我也在使用Context.SaveChanges(),但在某种情况下,我使用存储命令来检查表中是否锁定了特定行。我对EntityframeWork还很陌生,所以我正在尝试构建最佳逻辑。我认为最好有多个上下文实例,也许每个屏幕一个上下文。这正确吗?如果我错了,请纠正我。 - ksvimal
是的,上下文实例会出现并消失。它们应该是短暂的。例如每个Web请求或每个服务调用一个。在Windows应用程序中,每个屏幕可能有效(因为通常可以进行惰性加载),但即使如此,最好加载所需内容,然后处理。 - Gert Arnold
非常感谢您的反馈。它确实帮助我理解EF。但是我对EF有很多问题。您能帮我吗? - ksvimal
在SO这里提出你的问题,任何人,包括我在内,都会看到并尽力帮助你。不过请先检查一下是否已经有答案了。 - Gert Arnold

1

在提交或回滚事务后,您需要分配新的事务。

public program()
{
    //Establishing the connection.
    DBOperations _DBOperations = new DBOperations();
    _DBOperations.ConnectDB();

    //Update the datebase
    _DBOperations.UpdateDB();    //Update the database but it doesn't commit the changes.

    //Issue Rollback to rollback the transaction.
    _DBOperations.RollbackTransaction();    //Successfully Rollbacks the database changes.

    _DBOperations.ConnectDB(); //you need to assign new transaction because your last 
                               //transaction is over when you commit or roll back 

    _DBOperations.UpdateDB();    //Update the database it commits the changes.

    //Issue Rollback to rollback the transaction.
    _DBOperations.RollbackTransaction();    //Rollback fails.
}

1
非常感谢您的反馈。所以,无论提交和回滚的次数如何,我都不能使用同一个事务对象,这是正确的吗? - ksvimal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接