【问题标题】:Implementing LINQ-to-SQL transactions through WCF通过 WCF 实现 LINQ-to-SQL 事务
【发布时间】:2009-04-15 20:36:32
【问题描述】:

我有一个 WCF 服务,用于向数据库添加投标,即 MS SQL Server 2005。WCF 使用 LINQ-to-SQL。

每个投标可以有很多文件和很多项目。客户可以在每次服务调用中添加一个对象。也就是说,做这样的事情:

TendersServiceClient service = new TenderServiceClient();
service.BeginTransaction();

// Adding a new tender
service.AddTender(TenderDTO tenderInfo);

// Adding tender's documents
foreach (DocumentDTO documentInfo in documents)
   service.AddTenderDocument(tenderInfo.TenderID, documentInfo);

// Adding tender's items
foreach (ItemDTO itemInfo in items)
   service.AddTenderItem(tenderInfo.TenderID, itemInfo);

service.CommitTransaction();

注意 BeginTransaction() 和 CommitTransaction()。也就是说,上述所有过程要么完全成功,要么完全回滚。例如,如果其中一项无法插入,则整个标书不应该存在...

所以问题是我如何实现这种交易。当然,问题在于 WCF 是无状态的。因此为每个服务调用创建新的 DataContext。如果我使用静态 DataContext 代替,那么我将能够使用其内置的事务功能,但是我如何处理其他可以尝试同时添加另一个投标的客户(当然,他们必须是,在此交易之外)?

所以请帮助我使用某种设计模式来实现这一点。我可以随意更改服务和客户端的代码,所以请随意提出您的建议 =)

【问题讨论】:

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


    【解决方案1】:

    你控制服务的接口吗?

    如果是这样,那么优雅的解决方案肯定是让服务在单个方法中接受聚合的 Tender 对象,而不是使用您现在拥有的繁琐的方法。然后,Tender 会将 Items 和 Documents 作为子集合,并且数据访问代码可以更轻松地处理单个事务中的所有更新。

    除非我有误解,否则它似乎与 Order/OrderDetails 场景非常相似,同样的逻辑也适用。

    【讨论】:

    • 我同意。建议的代码非常类似于 RPC。最好将事情分块并让服务处理事务。
    【解决方案2】:

    首先,您必须在此处使用事务性服务调用 - 因为您有一个“初始化”调用、一堆中间调用,然后可能有一个结束所有调用,我建议您看看方法的 OperationContract 上的“IsInitiating”和“IsTerminating”属性 - 这将允许您指定一种方法来启动会话和结束会话。

    接下来,通过将“TransactionFlow”属性放在服务或所有操作上(无论您喜欢哪个),确保将您的服务配置为事务性服务。

    在您的客户端代码中,您必须使用 System.Transactions 创建一个 TransactionScope,它将包装您的服务调用。这是一个轻量级或完全两阶段的分布式事务协调器 - 取决于您的调用具体执行的操作。

    类似的东西:

    1) 将您的绑定标记为事务性:

    <bindings>
      <wsHttpBinding>
        <binding name="TransactionalWsHttp" transactionFlow="true" />
      </wsHttpBinding>
    </bindings>
    

    2) 服务合同:

    [ServiceContract]
    public interface ITenderService
    {
      // method to start your submission process
      [OperationContract(IsInitiating=true, IsTerminating=false)]
      [TransactionFlow(TransactionFlowOption.Mandatory]
      public void StartTenderProcess();
    
      // all your other methods "in between"
      [OperationContract(IsInitiating=false, IsTerminating=false)]
      [TransactionFlow(TransactionFlowOption.Mandatory]
      public void AddTender()
    
      [OperationContract(IsInitiating=false, IsTerminating=false)]
      [TransactionFlow(TransactionFlowOption.Mandatory]
      public void AddTenderDocument()
    
      [OperationContract(IsInitiating=false, IsTerminating=false)]
      [TransactionFlow(TransactionFlowOption.Mandatory]
      public void AddTenderItem()
    
      ...
    
      // method to end your submission process
      [OperationContract(IsInitiating=false, IsTerminating=true)]
      [TransactionFlow(TransactionFlowOption.Mandatory]
      public void FinishTenderProcess();
    }
    

    3) 在您的客户端代码中:

    using (TransactionScope ts = new TransactionScope())
    {
       serviceClient.StartTenderProcess();
    
       ..... 
    
       serviceClient.FinishTenderProcess();
    
       ts.Complete();   // Transaction Commit 
    }
    

    这对您暂时开始有帮助吗??

    马克

    【讨论】:

    • 嗯...IsInitiating 和 IsTerminating 属性似乎是我真正需要的。但是客户端的 TransactionScope 存在问题:我的一些客户端是 C++ 非托管的。我应该在服务器端实现整个事情吗?
    • 另外你能指出这个东西是如何连接到 LINQ-to-SQL 的吗?我应该为每个 WCF 事务创建一个单独的 DataContext,还是有办法在 WCF 事务和 LINQ-to-SQL 事务之间建立链接?
    • 好吧,如果您使用会话模式将整个会话(从开始......到完成)与服务器端的单个服务实例相关联,您可以尝试“缓存”调用之间的 DataContext。不过,我自己从来没有这样做过……
    • 我正在使用 basicHttpBinding 作为 legacy,现在我明白这个绑定几乎不支持 =) 人们说我可以以某种方式使用 ASP.NET 会话(无论如何我已经在使用 asp.net 兼容模式) ,但到目前为止我还不能让它工作——每个调用都有新的会话 ID(嗯,在 basicHttpBinding 中,唯一支持的会话模式是 PerCall,所以这是有道理的)。
    • 我个人会设置两个单独的端点,一个用于托管客户端,一个用于 C++ 客户端。这样,当 C++ 客户端更新时,它们将自动获得事务支持。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多