【问题标题】:Func delegate for multiple method calls用于多个方法调用的 Func 委托
【发布时间】:2012-02-17 10:14:21
【问题描述】:

我之前问过 Converting using SqlConnection to Func delegate

如何使用以下委托使用同一事务进行多次调用? 更复杂的是,其中一个调用我希望返回一个值。

我有以下委托函数定义

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

【问题讨论】:

  • 到底是什么问题?
  • 如何更改委托以使用同一个 sql 事务进行多次调用?

标签: c# delegates


【解决方案1】:

您可以使用“块”lambda。而不是:

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;
    });
}

回复:您的更新。我会这样写(请注意,您可以存储 token 并在 lambda 末尾返回它,就像普通方法一样):

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

【讨论】:

  • 看起来很不错。我现在遇到受保护的 TResult UsingSqlTransaction 函数的语法错误。这是说The type arg for method TResult UsingSqlTransaction&lt;TResult&gt;(List&lt;FuncSqlTrasaction, TResult&gt;&gt;) cannot be inferred from the usage. Try specifying the type arguments explicitly.
  • 编译器很难确定TResult 应该是什么。如果没有看到您的代码,很难说出问题所在,但请尝试像消息中所述手动指定 TResult。如果您仍然收到错误,那么您在某处遇到了不匹配的类型。
  • 谢谢。如果您觉得值得,我已在原始问题中更新了我的代码。委托上的 List 与调用函数不一致......正在尝试一些事情。
  • @Valamas:我添加了另一部分,展示了如何以更简洁的方式进行更新,而无需引用 lambda 之外的变量。
【解决方案2】:

我的程序中有相同的方法,但它是这样的:

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

这个方法只负责管理一个事务,没有别的。它不关心我们是在查询数据库还是在插入一个值。这是调用者的责任来处理这些事情,并与闭包之美,这很容易做到:)

这就是我的想法:不要试图对所有事物都进行抽象,因为很快你的程序员就会变得过于复杂而无法扩展和/或维护。只要有一些基本的抽象供你使用,然后你就可以在这些简单的基础上构建你的应用程序.

【讨论】:

    【解决方案3】:

    这是我成功使用过几次的模式:

    拥有一个内部跟踪当前连接和事务的“事务管理器”对象。它可以是一个简单的静态,用于单线程应用程序,或者使用线程本地存储甚至 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;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-10
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-13
      相关资源
      最近更新 更多