Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在C语言中以功能性的方式处理数据库事务#_C#_Design Patterns_Functional Programming - Fatal编程技术网

C# 在C语言中以功能性的方式处理数据库事务#

C# 在C语言中以功能性的方式处理数据库事务#,c#,design-patterns,functional-programming,C#,Design Patterns,Functional Programming,我在看这类代码时想知道,如果从函数式编程的角度来看,有什么可以改进的地方吗 您不必严格地重新实现我的示例来回答这个问题,如果您有涉及事务的不同示例,那就太好了 using (var unitOfWork = _uowManager.Begin()) { _paymentRepository .InsertOrUpdate(payment); // Returns payment instance // Being executed to get Payment.I

我在看这类代码时想知道,如果从函数式编程的角度来看,有什么可以改进的地方吗

您不必严格地重新实现我的示例来回答这个问题,如果您有涉及事务的不同示例,那就太好了

using (var unitOfWork = _uowManager.Begin())
{
    _paymentRepository
       .InsertOrUpdate(payment); // Returns payment instance

    // Being executed to get Payment.Id
    _uowManager
       .SaveChanges();

    _otherRepository
       .OtherMethod(payment.Id); // Could be changed as necessary

    unitOfWork
       .Complete()
}

如果有帮助的话,上面的代码是基于和实体框架的。

我认为创建事务性monad是处理这个问题的一个很好的方法。无论C#中的FP值是多少,这都会带来很多好处:

  • 单一、全局、统一的交易处理方式
  • 不要重复自己的操作,只编写一次此类,不要为每个控制器函数重新创建n次事务逻辑
  • 您可以创建这个类作为接口,并在集成测试中模拟它,例如,回滚事务,这样您的测试就不会影响数据库状态
我用这样的方式:

public class Tx
{     
        public DbConnectionExtra _connection { get; set; } // class which has DbTransaction and DbConnection combined 

        public T UseTx<T>(Func<T> fnToCall)
        {          
            try
            {
                _connection.BeginTransaction();

                var result = fnToCall();

               _connection._transaction.Commit();
               _connection._transaction.Dispose();

                return result;
            }
            catch
            {
               _connection._transaction.Rollback();
                _connection._transaction.Dispose();

                throw;
            }
        }
}
公共类Tx
{     
公共DbConnectionExtra_连接{get;set;}//组合了DbTransaction和DbConnection的类
公用电话(功能呼叫)
{          
尝试
{
_connection.BeginTransaction();
var result=fnToCall();
_连接。_transaction.Commit();
_连接。_transaction.Dispose();
返回结果;
}
抓住
{
_连接。_事务.回滚();
_连接。_transaction.Dispose();
投掷;
}
}
}

在函数式编程语言中,这类事情通常使用monad处理。然而,C#主要不是一种FP语言(尽管它在一定程度上支持它),并且把单子塞进里面,这样你就可以说你是功能性的,这似乎是不自然的。@Jeroen Mostert请你提供一些来自功能性世界的例子。:)好吧,如果你要写那么多代码,我明白你为什么需要包装,但这不是正确的方法<代码>只需使用(var t=_connection.BeginTransaction()){var result=fnToCall();t.Commit();return result;}即可。没有明确的
捕获
,没有
。处置
,没有
。回滚
,所有这些都是由
使用
事务处理来处理的。处置();推荐的方法是使用(var ts=newtransactionscope()){…ts.Commit();}
将代码包装在
中,而不显式引用单个事务。登记在事务作用域中的代码(默认情况下SQL命令将执行该操作)将成为事务的一部分,而无需将该代码更改为事务感知的代码。我不确定使用TransactionScope(用于分布式事务)是否正确。你对此有任何参考资料吗?据我所知,如果你正在使用EF,这是绝对不推荐的。关于包装-如果您在catch子句中有更多的逻辑(例如,日志记录),它会有所帮助。
TransactionScope
与explicit
SqlTransaction
相比,它本身就有很多问题(事实上,在这方面有很多问题),因此我将不再深入讨论。诚然,
TransactionScope
对于分布式事务特别有用,但这不是它的唯一用例。
catch
中的额外逻辑是一回事,但您当前编写的代码根本不正确——如果事务遇到数据库错误,它已经回滚,并调用
.Rollback()
同样会产生一个无效的错误。(我假设您的事务的工作方式很像
SqlTransaction
,当然——我无法查看
DbConnectionExtra
)如果
使用
是一个选项,并且如果您这样做,那么最后应该在
块中完成,那么您肯定不希望显式调用
.Dispose()