Транзакция ADO.NET 2.0 на SQL Server 2005 автоматически откатывается при исключении? - PullRequest
1 голос
/ 26 апреля 2009

Если мы начнем транзакцию, то произойдет какое-нибудь действие db, скажем, вставка, и произойдет исключение, будет ли транзакция автоматически откатываться, если я не вызову транзакции.rollback () в обработчике catch / exception?

Даже если откат транзакции приводит к утечке памяти, то есть объект транзакции и объект соединения зависают в памяти в своем первоначальном состоянии до тех пор, пока не включится сборщик мусора?


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 ]

3 голосов
/ 26 апреля 2009

Транзакция будет отменена, если она будет удалена без фиксации. Так что, пока вы используете using () для начала транзакции или имеете блок finally, который вызывает Dispose (), он будет автоматически отменен в случае исключения.

Если вы не утилизируете транзакцию / соединение, оно в конечном итоге будет восстановлено сборщиком мусора, но до этого будет зависать в памяти. (На самом деле управляемый объект SqlTransaction будет зависать в памяти до тех пор, пока GC все равно не заработает; но утилизация обеспечивает раннюю очистку неуправляемого ресурса транзакции / соединения, освобождая ресурсы на стороне сервера и освобождая соединение для повторного использования.)

1 голос
/ 26 апреля 2009
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;
        }

    }
}

Я не смог опубликовать код в разделе комментариев из-за ограничения на количество слов и из-за того, что он испортил все форматирование.

...