2009-04-15 6 views
1

Ich habe einen WCF-Dienst, der zum Hinzufügen von Angeboten in die Datenbank verwendet wird. Dies ist MS SQL Server 2005. WCF verwendet LINQ-to-SQL.Implementieren von LINQ-zu-SQL-Transaktionen über WCF

Jedes Angebot kann viele Dokumente und viele Artikel enthalten. Die Kunden können ein Objekt pro Serviceaufruf hinzufügen. Das heißt, das tun etwas wie folgt:

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

Beachten Sie die BeginTransaction() und CommitTransaction(). Das heißt, alle oben genannten Verfahren müssen entweder vollständig erfolgreich sein oder vollständig rückgängig gemacht werden. Wenn zum Beispiel eines der Elemente nicht eingefügt werden konnte, sollte das gesamte Angebot nicht existieren ...

Die Frage ist also, wie ich diese Art von Transaktion implementieren. Das Problem ist, dass WCF natürlich staatenlos ist. Daher wird für jeden Serviceaufruf ein neuer DataContext erstellt. Wenn ich stattdessen einen statischen DataContext verwende, kann ich die integrierten Transaktionsfunktionen verwenden, aber wie kann ich dann mit anderen Kunden umgehen, die versuchen, in derselben Zeit ein anderes Zahlungsmittel hinzuzufügen (das muss natürlich sein). außerhalb dieser Transaktion)?

Also bitte helfen Sie mir mit einer Art von Design-Muster, um dies zu erreichen. Ich bin frei, den Code sowohl des Service und des Kunden zu ändern, also fühlen Sie sich frei mit Ihren Vorschlägen =)

Antwort

1

Zunächst einmal müssten Sie transaktionale Service-Anrufe hier - und da Sie einen "Initialisierungs" Anruf haben , eine Reihe von Zwischenaufrufen und dann möglicherweise eine, um alle Aufrufe zu beenden, würde ich empfehlen, dass Sie sich die "IsInitiating" - und "IsTerminating" -Attribute im OperationContract für die Methoden ansehen - damit können Sie eine Methode angeben Starten Sie Ihre Sitzung, und eine, die es beenden wird.

Als nächstes stellen Sie sicher, dass Ihr Dienst als Transaktionsdienst konfigurieren, indem Sie auf den „TransactionFlow“ -Attribut setzen entweder den Dienst oder alle Operationen - je nachdem, was Sie bevorzugen.

In Ihrem Client-Code, müssen Sie System.Transactions verwenden, um eine Transaction zu schaffen, die Ihre Service-Anrufe wickeln wird. Dies ist ein leichtgewichtiger oder vollständig zweiphasiger Distributed Transaction Coordinator - abhängig davon, was Ihre Anrufe im Detail tun.

etwas in diese Richtung:

1) Markieren Sie Ihre als Transaktionsbindung:

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

2) Servicevertrag:

[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) In Ihrem Client-Code:

using (TransactionScope ts = new TransactionScope()) 
{ 
    serviceClient.StartTenderProcess(); 

    ..... 

    serviceClient.FinishTenderProcess(); 

    ts.Complete(); // Transaction Commit 
} 

Hilft dir das vorerst, dich auf den Weg zu bringen?

Marc

+0

Hmm ... IsInitiating und IsTerminating Attribute scheinen wirklich zu sein, was ich brauche. Aber es gibt ein Problem mit TransactionScope auf der Client-Seite: einige meiner Clients sind C++ - nicht verwaltet. Soll ich das Ganze auf der Serverseite implementieren? –

+0

Und können Sie bitte auch darauf hinweisen, wie diese Sache mit LINQ-to-SQL verbindet? Soll ich für jede WCF-Transaktion einen separaten DataContext erstellen oder gibt es eine Möglichkeit, zwischen WCF-Transaktion und LINQ-zu-SQL-Transaktion zu verknüpfen? –

+0

Hallo Dmitry - ok, C++ unmanaged .... das wird hart sein ... –

2

Sie die Schnittstelle des Dienstes steuern Sie?

Wenn ja, sicherlich die eleganteste Lösung ist für den Dienst ein Aggregat Ausschreibungs Objekt in einem einzigen Verfahren zu akzeptieren, anstatt die gesprächig Methoden mit, die Sie jetzt haben. Der Tender würde dann Elemente und Dokumente als Untersammlungen haben, und der Datenzugriffscode könnte viel einfacher alle Aktualisierungen in einer einzigen Transaktion handhaben.

Wenn ich nicht falsch verstanden habe, scheint es einem Order/OrderDetails-Szenario sehr ähnlich zu sein, in dem die gleiche Logik gilt.

+0

Ich stimme zu. Der vorgeschlagene Code ist sehr RPC-ähnlich. Es ist viel besser, Dinge aufzuteilen und den Service mit der Transaktion abwickeln zu lassen. – Brook

Verwandte Themen