Избегайте включения MSDTC при использовании TransactionScope - PullRequest
1 голос
/ 31 августа 2011

[Использование: C # 3.5 + SQL Server 2005]

У меня есть некоторый код в бизнес-уровне, который оборачивает в TransactionScope создание заказа и его детали:

        DAL.DAL_OrdenDeCompra dalOrdenDeCompra = new GOA.DAL.DAL_OrdenDeCompra();
        DAL.DAL_ItemDeUnaOrden dalItemDeUnaOrden = new GOA.DAL.DAL_ItemDeUnaOrden();            
        using (TransactionScope transaccion = new TransactionScope())
        {
            //Insertion of the order
            orden.Id = dalOrdenDeCompra.InsertarOrdenDeCompra(orden.NumeroOrden, orden.PuntoDeEntregaParaLaOrden.Id, (int)orden.TipoDeCompra, orden.FechaOrden, orden.Observaciones);
            foreach (ItemDeUnaOrden item in orden.Items)
            {                       
                //Insertion of each one of its items. 
                dalItemDeUnaOrden.InsertarItemDeUnaOrden(orden.Id, item.CodigoProductoAudifarma, item.CodigoProductoJanssen, item.CodigoEAN13, item.Descripcion, item.CantidadOriginal, item.ValorUnitario);
            }
            transaccion.Complete();
        }
        return true;

А вот код DAL, который выполняет вставки:

public int InsertarOrdenDeCompra(string pNumeroOrden, int pPuntoEntregaId, int pTipoDeCompra, DateTime pFechaOrden, string pObservaciones)
    {
        try
        {
            DataTable dataTable = new DataTable();
            using (SqlConnection conexion = new SqlConnection())
            {
                using (SqlCommand comando = new SqlCommand())
                {
                    ConnectionStringSettings conString = ConfigurationManager.ConnectionStrings["CSMARTDB"];
                    conexion.ConnectionString = conString.ConnectionString;
                    conexion.Open();                        
                    comando.Connection = conexion;
                    comando.CommandType = CommandType.StoredProcedure;
                    comando.CommandText = "GOA_InsertarOrdenDeCompra";
                    //...parameters setting
                    return (int)comando.ExecuteScalar();                  
                 ...

public int InsertarItemDeUnaOrden(int pOrdenDeCompraId, string pCodigoProductoAudifarma, string pCodigoProductoJanssen, string pCodigoEAN13, string pDescripcion, int pCantidadOriginal, decimal pValorUnitario)
{
    try
    {
        DataTable dataTable = new DataTable();
        using (SqlConnection conexion = new SqlConnection())
        {
            using (SqlCommand comando = new SqlCommand())
            {
                ConnectionStringSettings conString = ConfigurationManager.ConnectionStrings["CSMARTDB"];
                conexion.ConnectionString = conString.ConnectionString;                        
                conexion.Open();
                comando.Connection = conexion;
                comando.CommandType = CommandType.StoredProcedure;
                comando.CommandText = "GOA_InsertarItemDeUnaOrden";
                //... parameters setting
                return comando.ExecuteNonQuery();

Теперь моя проблема в вставке предметов; когда InsertarItemDeUnaOrden пытается открыть новое соединение, возникает исключение, потому что это приведет к тому, что TransactionScope попытается перейти на MSDTC, который у меня не включен, и я предпочел бы не включать.

Мои сомнения:

  • Понимание того, что метод, который запускает транзакцию, находится на бизнес-уровне, и я не хочу там никакого SqlConnection,, могу ли я использовать другой дизайн для доступа к данным, чтобы я мог повторно использовать то же соединение?
  • Должен ли я включить MSDTC и забыть об этом?

Спасибо.

РЕДАКТИРОВАТЬ: решение

Я создал новый класс в DAL для хранения транзакций, таких как:

namespace GOA.DAL
{
    public class DAL_Management
    {
        public SqlConnection ConexionTransaccional { get; set; }

        public bool TransaccionAbierta { get; set; }

        public DAL_Management(bool pIniciarTransaccion)
        {
            if (pIniciarTransaccion)
            {
                this.IniciarTransaccion();
            }
            else
            {
                TransaccionAbierta = false;
            }
        }

        private void IniciarTransaccion()
        {
            this.TransaccionAbierta = true;
            this.ConexionTransaccional = new SqlConnection();
            ConnectionStringSettings conString = ConfigurationManager.ConnectionStrings["CSMARTDB"];
            this.ConexionTransaccional.ConnectionString = conString.ConnectionString;
            this.ConexionTransaccional.Open();
        }

        public void FinalizarTransaccion()
        {
            this.ConexionTransaccional.Close();
            this.ConexionTransaccional = null;
            this.TransaccionAbierta = false;
        }
    }
}

Я изменил методы выполнения DAL, чтобы получить параметр этого нового класса, и использую его следующим образом:

public int InsertarItemDeUnaOrden(int pOrdenDeCompraId, string pCodigoProductoAudifarma, string pCodigoProductoJanssen, string pCodigoEAN13, string pDescripcion, int pCantidadOriginal, decimal pValorUnitario, DAL_Management pManejadorDAL)
        {
            try
            {
                DataTable dataTable = new DataTable();
                using (SqlConnection conexion = new SqlConnection())
                {
                    using (SqlCommand comando = new SqlCommand())
                    {
                        if (pManejadorDAL.TransaccionAbierta == true)
                        {
                            comando.Connection = pManejadorDAL.ConexionTransaccional;
                        }
                        else
                        {
                            ConnectionStringSettings conString = ConfigurationManager.ConnectionStrings["CSMARTDB"];
                            conexion.ConnectionString = conString.ConnectionString;
                            conexion.Open();
                            comando.Connection = conexion;
                        }                        
                        comando.CommandType = CommandType.StoredProcedure;
                        comando.CommandText = "GOA_InsertarItemDeUnaOrden";

И, наконец, изменил вызывающий класс:

        DAL.DAL_OrdenDeCompra dalOrdenDeCompra = new GOA.DAL.DAL_OrdenDeCompra();
        DAL.DAL_ItemDeUnaOrden dalItemDeUnaOrden = new GOA.DAL.DAL_ItemDeUnaOrden();            
            using (TransactionScope transaccion = new TransactionScope())
            {
                DAL.DAL_Management dalManagement = new GOA.DAL.DAL_Management(true);
                orden.Id = dalOrdenDeCompra.InsertarOrdenDeCompra(orden.NumeroOrden, orden.PuntoDeEntregaParaLaOrden.Id, (int)orden.TipoDeCompra, orden.FechaOrden, orden.Observaciones, dalManagement);
                foreach (ItemDeUnaOrden item in orden.Items)
                {                        
                    dalItemDeUnaOrden.InsertarItemDeUnaOrden(orden.Id, item.CodigoProductoAudifarma, item.CodigoProductoJanssen, item.CodigoEAN13, item.Descripcion, item.CantidadOriginal, item.ValorUnitario, dalManagement);
                }
                transaccion.Complete();                    
            }
            dalManagement.FinalizarTransaccion();

С этими изменениями я вставляю заказы и позиции без включения MSDTC.

Ответы [ 3 ]

5 голосов
/ 31 августа 2011

При использовании TransactionScope с несколькими подключениями к SQL Server 2005 транзакция всегда будет преобразовываться в распределенную (то есть будет использоваться MSDTC).

Это известная проблема , исправленная в SQL Server 2008.

Один из возможных вариантов - написать одну хранимую процедуру, которая выполняет все необходимые операции (сворачивание GOA_InsertarOrdenDeCompra и все вызовы GOA_InsertarItemDeUnaOrden). В SQL Server 2005 это может быть выполнено с помощью параметра XML, хотя SQL Server 2008 (за исключением отсутствия этой проблемы) имеет табличные параметры .

3 голосов
/ 31 августа 2011

Разве вы не можете создать соединение вне методов и передать одно и то же соединение обоим методам через параметры?

Таким образом, вы используете одно и то же соединение, избегая продвижения.

Мое доброРешением было бы переосмыслить архитектуру DAL.Что-то вроде наличия центрального DAL, который хранит объект соединения и имеет ссылку на ваши объекты DAL_OrdenDeCompra и DAL_ItemDeUnaOrden, и передает ссылку на DAL этим объектам, чтобы они могли взаимодействовать с соединением, хранящимся в DAL.И тогда DAL может иметь метод Open и Close с подсчетом ссылок, приращениями открытия, приращениями закрытия, и он должен удалять соединение только тогда, когда оно достигает нуля, и создавать новое при увеличении до единицы.Также DAL должен реализовывать IDisposable для очистки ресурсов соединения.Затем на вашем бизнес-уровне вы делаете что-то вроде этого:

using(DAL dal = new DAL())
{
    DAL.DAL_OrdenDeCompra dalOrdenDeCompra = dal.OrdenDeCompra;
    DAL.DAL_ItemDeUnaOrden dalItemDeUnaOrden = dal.ItemDeUnaOrden;
    using (TransactionScope transaccion = new TransactionScope())
    {
        dal.Open();
        //Insertion of the order
        orden.Id = dalOrdenDeCompra.InsertarOrdenDeCompra(orden.NumeroOrden, orden.PuntoDeEntregaParaLaOrden.Id, (int)orden.TipoDeCompra, orden.FechaOrden, orden.Observaciones);
        foreach (ItemDeUnaOrden item in orden.Items)
        {                       
            //Insertion of each one of its items. 
            dalItemDeUnaOrden.InsertarItemDeUnaOrden(orden.Id, item.CodigoProductoAudifarma, item.CodigoProductoJanssen, item.CodigoEAN13, item.Descripcion, item.CantidadOriginal, item.ValorUnitario);
        }
        transaccion.Complete();
    }
    return true;
}
1 голос
/ 31 августа 2011

Вы можете иметь метод в DAL.DAL_ItemDeUnaOrden, который получает коллекцию ItemDeUnaOrden вместо одного элемента, таким образом вы можете использовать SqlTransaction (или TransactionScope) и выполнять итерации по элементам в методе DA.

orden.Id = dalOrdenDeCompra.InsertarOrdenDeCompra(...);
dalItemDeUnaOrden.InsertarVariosItemsDeUnaOrden(orden.Items);

В зависимости от вашего кода, у вас может не быть доступа к объектам вашего бизнеса (ItemDeUnaOrden) в вашем DAL, поэтому вам может потребоваться передать значения другим способом, например, DTO или DataTable.

...