SQL Server 2005上的ADO.NET 2.0事务是否会在异常时自动回滚?

时间:2009-04-26 01:54:05

标签: c# ado.net transactions

如果我们开始一个事务,然后执行一些db操作,比如说insert,并且发生异常,如果我不在catch / exception处理程序中调用transaction.rollback(),那么事务会自动回滚吗?

即使事务被回滚,是否会导致内存泄漏,即事务对象和连接对象在内存中以原始状态挂起,直到垃圾收集器启动?


CREATE TABLE [Friend]

(
[ID] [int] IDENTITY(1,1) NOT NULL,
[FullName] [varchar](50) NOT NULL,
[Phone] [varchar](50) NULL,
)

客户端:

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            DaContract Impl = new Impl();
            Impl.AddFriend(new Friend("Someone", "100"));
            Impl.AddFriend(new Friend("Someone else"));
            Console.ReadLine();
        }
    }
}

SERVER:

using System;
using MyLib.DataAccess;
using System.Data;

namespace TestTransactionAndConnectionStateOnException
{
    public class Friend
    {
        public string FullName;
        public string Phone;

        public Friend(string fullName): this(fullName, null) {}
        public Friend(string fullName, string phone)
        {
            this.FullName = fullName;
            this.Phone = phone;
        }
    }

    public interface DaContract
    {
        int AddFriend( Friend f );

        int UpdatePhone(string fullName, string phone);
    }

    public class Impl: DaContract
    {
        Mediator _m;
        public Impl() { this._m = new Mediator(); }

        public int AddFriend( Friend f )
        {
            int ret = 0;

            try
            {
                ret = this._m.AddFriend( f );
            }
            catch(Exception ex)
            {
                HandleException(ex);
            }

            return ret;
        }

        public int UpdatePhone(string fullName, string phone)
        {
            int ret = 0;

            try
            {
                ret = this._m.UpdatePhone(fullName, phone);
            }
            catch(Exception ex)
            {
                HandleException(ex);
            }

            return ret;
        }

        public void HandleException(Exception ex)
        {
            /* see the transaction state */

            /* see connection state */

            /* do nothing and let the client call another method to initiate a new
             * transaction and a new connection */

        }
    }

    public class Mediator
    {
        private string _connectionString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=MyFriends";
        private Manager _m = new Manager();

        public int AddFriend( Friend f )
        {
            int ret = 0;
            using (ISession session = SessionFactory.Create(SessionType.Ado, this._connectionString))
            {
                session.BeginTransaction();
                ret = this._m.AddFriend(f, session);
                session.CommitTransaction();
            }

            return ret;
        }

        public int UpdatePhone(string fullName, string phone)
        {
            int ret = 0;
            using (ISession session = SessionFactory.Create(SessionType.Ado, this._connectionString))
            {
                session.BeginTransaction();
                ret = this._m.UpdateFriend(fullName, phone, session);
                session.CommitTransaction();
            }

            return ret;
        }
    }

    public class Manager
    {
        public int AddFriend(Friend f, ISession session) { return Handler.Instance.AddFriend(f, session); }
        public int UpdateFriend(string fullName, string phone, ISession session) { return Handler.Instance.UpdatePhone(fullName, phone, session); }
    }

    public class Handler
    {
        private static Handler _handler = null;
        private Handler() {}
        public static Handler Instance
        { 
            get
            {
                if (_handler == null)
                    _handler = new Handler();

                return _handler;
            }
        }

        public int AddFriend( Friend f, ISession session )
        {
            /* check session, transaction and connection states here */
            Console.WriteLine(session.Connection.State.ToString());

            int ret = 0;

            /* Add the friend */
            IDbCommand command = session.CreateCommand();
            command.CommandType = CommandType.Text;
            command.CommandText = string.Format("INSERT INTO Friend(FullName, Phone) values('{0}', '{1}')", f.FullName, f.Phone);
            ret = command.ExecuteNonQuery();

            /* throw an exception just for the heck of it (don't dispose off ISession yet) */
            if (string.Compare(f.FullName, "Someone", true) == 0)
                throw new Exception("Fake exception. Friend value can't be 'Someone'");

            return ret;
        }

        public int UpdatePhone(string fullName, string phone, ISession session )
        {
            return 0;
        }

    }
}

由于字数限制,我无法在评论部分发布代码,因为它搞砸了所有格式。

2 个答案:

答案 0 :(得分:3)

如果事务在未提交的情况下处理,则将回滚该事务。因此,只要你使用using()开始你的事务,或者有一个调用Dispose()的finally块,它就会在异常的情况下自动回滚。

如果你没有处理事务/连接,它最终将由垃圾收集器回收,但在此之前会在内存中挂起。 (实际上,托管的SqlTransaction对象将在内存中挂起,直到GC无论如何都会出现;但是处理可以确保尽早清理非托管事务/连接资源,释放服务器端资源并释放连接以便重用。)

答案 1 :(得分:1)

CREATE TABLE [Friend]

(
[ID] [int] IDENTITY(1,1) NOT NULL,
[FullName] [varchar](50) NOT NULL,
[Phone] [varchar](50) NULL,
)

客户端

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            DaContract Impl = new Impl();
            Impl.AddFriend(new Friend("Someone", "100"));
            Impl.AddFriend(new Friend("Someone else"));
            Console.ReadLine();
        }
    }
}

SERVER

using System;
using MyLib.DataAccess;
using System.Data;

namespace TestTransactionAndConnectionStateOnException
{
    public class Friend
    {
        public string FullName;
        public string Phone;

        public Friend(string fullName): this(fullName, null) {}
        public Friend(string fullName, string phone)
        {
            this.FullName = fullName;
            this.Phone = phone;
        }
    }

    public interface DaContract
    {
        int AddFriend( Friend f );

        int UpdatePhone(string fullName, string phone);
    }

    public class Impl: DaContract
    {
        Mediator _m;
        public Impl() { this._m = new Mediator(); }

        public int AddFriend( Friend f )
        {
            int ret = 0;

            try
            {
                ret = this._m.AddFriend( f );
            }
            catch(Exception ex)
            {
                HandleException(ex);
            }

            return ret;
        }

        public int UpdatePhone(string fullName, string phone)
        {
            int ret = 0;

            try
            {
                ret = this._m.UpdatePhone(fullName, phone);
            }
            catch(Exception ex)
            {
                HandleException(ex);
            }

            return ret;
        }

        public void HandleException(Exception ex)
        {
            /* see the transaction state */

            /* see connection state */

            /* do nothing and let the client call another method to initiate a new
             * transaction and a new connection */

        }
    }

    public class Mediator
    {
        private string _connectionString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=MyFriends";
        private Manager _m = new Manager();

        public int AddFriend( Friend f )
        {
            int ret = 0;
            using (ISession session = SessionFactory.Create(SessionType.Ado, this._connectionString))
            {
                session.BeginTransaction();
                ret = this._m.AddFriend(f, session);
                session.CommitTransaction();
            }

            return ret;
        }

        public int UpdatePhone(string fullName, string phone)
        {
            int ret = 0;
            using (ISession session = SessionFactory.Create(SessionType.Ado, this._connectionString))
            {
                session.BeginTransaction();
                ret = this._m.UpdateFriend(fullName, phone, session);
                session.CommitTransaction();
            }

            return ret;
        }
    }

    public class Manager
    {
        public int AddFriend(Friend f, ISession session) { return Handler.Instance.AddFriend(f, session); }
        public int UpdateFriend(string fullName, string phone, ISession session) { return Handler.Instance.UpdatePhone(fullName, phone, session); }
    }

    public class Handler
    {
        private static Handler _handler = null;
        private Handler() {}
        public static Handler Instance
        { 
            get
            {
                if (_handler == null)
                    _handler = new Handler();

                return _handler;
            }
        }

        public int AddFriend( Friend f, ISession session )
        {
            /* check session, transaction and connection states here */
            Console.WriteLine(session.Connection.State.ToString());

            int ret = 0;

            /* Add the friend */
            IDbCommand command = session.CreateCommand();
            command.CommandType = CommandType.Text;
            command.CommandText = string.Format("INSERT INTO Friend(FullName, Phone) values('{0}', '{1}')", f.FullName, f.Phone);
            ret = command.ExecuteNonQuery();

            /* throw an exception just for the heck of it (don't dispose off ISession yet) */
            if (string.Compare(f.FullName, "Someone", true) == 0)
                throw new Exception("Fake exception. Friend value can't be 'Someone'");

            return ret;
        }

        public int UpdatePhone(string fullName, string phone, ISession session )
        {
            return 0;
        }

    }
}

由于字数限制,我无法在评论部分发布代码,因为它搞砸了所有格式。