通过WCF实现Linq-to-Sql事务 - 第二部分

时间:2009-05-01 09:16:54

标签: wcf web-services linq-to-sql

我从这里开始:Implementing LINQ-to-SQL transactions through WCF

从那以后我来到了以下。我使用basicHttpBinding用于遗留,我的WCF客户端是非托管C ++(gSOAP)。所以我使用ASP.NET Session并在WCF上启用aspNetCompatibilityMode。这有效,所以现在我可以正确处理会话。

现在我的想法是为每个会话分配单独的LINQ-to-SQL DataContext,并以此方式实现事务行为。我想要做的是将DataContext实例存储在Session中,在不调用SubmitChanges()的情况下执行所有插入操作,然后只调用SubmitChanges()一次以提交事务。

不幸的是,LINQ-to-SQL中存在一个错误,它阻止您在调用SubmitChanges()之前从DataContext中获取以前插入的数据(请参阅here)我需要这样做,因为在插入招标文件和标题之前,我需要检索招标本身。所以这不起作用(至少没有完全重构代码并且失去了大部分LINQ-to-SQL的美感......)

现在的问题是:如何通过WCF正确实现per-DataContext事务?我不能从客户端传播TransactionScope,因为客户端是不受管理的。由于上面的错误,我无法使用内部LINQ-to-SQL事务。我唯一拥有的是ASP.NET Session(可以工作)。也许我可以以某种方式将TransactionScope存储在Session中?

2 个答案:

答案 0 :(得分:1)

一旦人们开始在同一句话中提及“交易”和“会话”,我就会自动担心。根据我的观点和经验,它只是不能扩展到考虑单个事务工作单元的多个WCF调用:

  • 表示您依赖外部客户端进行阻止
  • 很多可能无法解决的死锁 - 即只能超时,而不是可以检测到的以数据库为中心的死锁
  • 您被迫使用粘性负载平衡

就个人而言,我会尝试在单个工作单元上工作。这可能意味着客户端打包完整的请求并将其作为原子操作提交,或者它可能意味着您通过会话单独执行此操作 - 即您缓冲多个调用的“内容”,但只有在你拥有所需的一切时才开始做某事。

无论哪种方式,最终结果是您最终在单个实现上运行您的工作单元,这应该使db-transactions或环境(TransactionScope)事务正常工作。环境事务(在您的场景中)的优势在于它可以跨多个数据上下文工作,但我希望能够修复Single调用(根据相关问题),而不必太过头痛

或者等到.NET 4.0,我确信这个有问题的bug已经解决了。好的,最后可能在短期内没用...

答案 1 :(得分:0)

在我看来,重要的是你重复使用相同的连接和事务。我将使用DataContext上的构造函数来获取现有连接。会话启动后,通过分配连接和事务来开始事务。将连接和事务存储在会话中。然后在每次调用时,使用相同的连接和事务重新创建DataContext。完成后,调用提交事务的方法。在会话超时时,您需要确保将事务回滚。

 public bool CreateTransaction()
 {
     var connection = new SqlConnection( connectionString );
     var transaction = connection.BeginTransaction();
     Session["DBConnection"] = connection;
     Session["DBTransaction"] = transaction;

     return true;
 }

 private DBDataContext CreateContextFromSession()
 {
      if (Session["DBConnection"] == null)
          throw new NullReferenceException( "No connection available for transaction." );
      if (Session["DBTransaction"] == null)
          throw new NullReferenceException( "No transaction available." );

      var context = new DBDataContext( (IDbConnection)Session["DBConnection"] );
      context.Transaction = (DbTransaction)Session["DBTransaction"];

      return context;
 }

 public Tender CreateTender( ... )
 {
     var context = CreateContextFromSession();

     var tender = new Tender { ... };
     context.Tenders.InsertOnSubmit( tender );
     context.SubmitChanges();

     return tender;
 }

 public void CommitTransaction()
 {
     var transaction = (DbTransaction)Session["DBTransaction"];
     transaction.Commit();
     Session.Remove( "DBTransaction" );

     var connection = (IDbConnection)Session["DBConnection"];
     connection.Close();
     Session.Remove( "DBConnection" );
 }