Делегат Func для нескольких вызовов методов - PullRequest
3 голосов
/ 17 февраля 2012

Я ранее спрашивал о Преобразование с использованием SqlConnection в делегат Func

Как я могу использовать следующий делегат для выполнения нескольких вызовов с использованием одной и той же транзакции? Чтобы усложнить дело, один из звонков я хочу вернуть значение.

У меня есть следующее определение функции делегата

protected TResult UsingSqlTransaction<TResult>(Func<SqlTransaction, TResult> myFunction)
{
    using (SqlConnection sqlConn = new SqlConnection(ConnectionString))
    {
        sqlConn.Open();
        using (SqlTransaction sqlTrans = sqlConn.BeginTransaction())
        {
            var result = myFunction(sqlTrans);
            sqlTrans.Commit();

            return result;
        }

    }
}

Текущее использование

public Guid? InsertUpdate(News entity)
{
    return UsingSqlTransaction(sqlTrans => data.InsertUpdate(sqlTrans, entity));
}

Решение - см. Принятый ответ

public Guid? InsertUpdate(News entity)
{
    return UsingSqlTransaction(sqlTrans =>
    {
        var token = data.InsertUpdate(sqlTrans, entity);
        data.DoSomethingElse(sqlTrans, entity);
        return token;
    });
}

//-- The above UsingSqlTransaction remains unchanged

Ответы [ 3 ]

2 голосов
/ 17 февраля 2012

Вы можете использовать «блочную» лямбду.Вместо:

public Guid? InsertUpdate(News entity)
{
    return UsingSqlTransaction(sqlTrans => data.InsertUpdate(sqlTrans, entity));
}

Вы можете использовать (обратите внимание на фигурные скобки и точки с запятой):

public List<Guid?> InsertUpdate(News entity)
{
    return UsingSqlTransaction(sqlTrans =>
    {
        var result = new List<Guid?>();
        result.Add(data.InsertUpdate(sqlTrans, entity));
        result.Add(data.DoSomethingElse(sqlTrans, entity));
        return result;
    });
}

Re: ваше обновление.Я бы написал это так (обратите внимание, что вы можете сохранить token и вернуть его в конце своей лямбды, как обычный метод):

public Guid? InsertUpdate(News entity)
{
    return UsingSqlTransaction(sqlTrans =>
    {
        var token = data.InsertUpdate(sqlTrans, entity);
        data.DoSomethingElse(sqlTrans, entity);
        return token;
    });
}
0 голосов
/ 17 февраля 2012

Это шаблон, который я успешно использовал несколько раз:

Имеет объект «Менеджер транзакций», который внутренне отслеживает текущее соединение и транзакцию.Это может быть простая статика, для однопоточного приложения, или делать забавные вещи с локальным хранилищем потоков или даже с контекстом операций WCF.Идея состоит в том, что он дает вам единственное место для создания соединений и транзакций, которое отделено от кода, вызывающего базу данных.

Менеджер транзакций предоставляет открытый метод BeginTransaction, который возвращает объект транзакции, реализующий IDisposable.Каждый раз, когда вы вызываете BeginTransaction, вы получаете новый экземпляр области транзакции.

Объект области транзакции также предоставляет методы для получения соединения с базой данных и метод Commit, который должен быть вызван перед удалением.Если Commit не вызывается, транзакция будет откатываться на Dispose.

void M1() 
{
    using( var scope = TransactionManager.BeginTransaction() )
    {
      // Do stuff with the database.
      M2(); // M2 and M3 each create their own scopes that share the transaction
      M3(); // created by M1.

      // An exception before the commit will cause the transaction to roll back.
      scope.Commit();
    }
}

Важно то, что TransactionManager позволит вам создавать вложенные области действия - строка «Выполнить вещи с базой данных» может вызватькуча других методов, каждый из которых создает свои собственные области, которые совместно используют транзакцию.Все области должны быть зафиксированы, или все будет откатано.

Все это очень похоже на то, как работает материал в пространстве имен System.Transactions , и я бы порекомендовал вам посмотретьна это тоже.

А затем ваша служебная программа, упрощающая код, если вы все еще хотите их, выглядит следующим образом:

public static void Execute(Action<ITransactionScope> action)
{
  using( var scope = TransactionManager.BeginTransaction() )
  {
    action(scope);
    scope.Commit();
  }
}

public static TResult Execute<TResult>(Func<ITransactionScope, TResult> func)
{
  using( var scope = TransactionManager.BeginTransaction() )
  {
    var result = func(scope);
    scope.Commit();
    return result;
  }
}
0 голосов
/ 17 февраля 2012

У меня такой же метод в моей программе, но он выглядит примерно так:

void RunTransaction(Action<IDbCommand> action)
{
using(var cnn=GetConnection)
cnn.Open();
using(var trans=cnn.BeginTransaction())
{
var command=cnn.CreateCommand();
action(command);
trans.Commit();
}
}

этот метод отвечает только за управление транзакцией и ничего больше. Его не волнует, запрашиваем ли мы базу данныхили мы вставляем значение. Ответственный за вызов должен позаботиться об этих вещах, а с красотой замыканий это легко сделать:)

Вот что я думаю: не пытайтесьиметь абстракцию для всего, потому что скоро ваш программист станет слишком сложным, чтобы расширять или поддерживать. Просто предоставьте некоторую базовую абстракцию, тогда вы можете построить свое приложение поверх этих простых.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...