跨事务WCF调用服务器及跨服务器Web服务交互实现咨询
Alright, let's walk through how to implement this cross-server WCF interaction properly, especially considering the database lock dependency you mentioned. Here's a step-by-step breakdown tailored to your scenario:
deliveryRetrieved.svc on Server2 for Server1 to Call First, you need to set up the WCF service on Server2's Windows Service that listens for the "delivery retrieved" notification.
Define the Service Contract & Implementation
Create a service contract that Server1 will invoke, and implement the logic to release the database lock for the delivery ID:
[ServiceContract] public interface IDeliveryNotificationService { [OperationContract] void DeliveryRetrieved(int deliveryId); } public class DeliveryNotificationService : IDeliveryNotificationService { public void DeliveryRetrieved(int deliveryId) { // Logic to unlock the delivery ID in Server2's database using (var dbContext = new Server2DbContext()) { var delivery = dbContext.Deliveries.FirstOrDefault(d => d.Id == deliveryId); if (delivery != null) { delivery.IsLocked = false; // Or release a pessimistic lock here dbContext.SaveChanges(); } } } }
Configure WCF Endpoints
Set up a WCF endpoint in Server2's Windows Service configuration (either via app.config or code) so Server1 can reach it. Choose a binding that fits your network environment—netTcpBinding for fast internal network communication, or basicHttpBinding if you need broader compatibility:
<system.serviceModel> <services> <service name="Server2.DeliveryNotificationService"> <endpoint address="net.tcp://localhost:8080/DeliveryNotificationService" binding="netTcpBinding" contract="Server2.IDeliveryNotificationService" /> </service> </services> </system.serviceModel>
Next, set up Server1's processingComplete.svc to invoke Server2's deliveryRetrieved.svc once it finishes processing the delivery ID.
Generate WCF Client Proxy
In Server1's Web Application project, add a Service Reference pointing to Server2's WCF endpoint (e.g., net.tcp://server2-machine:8080/DeliveryNotificationService). This will auto-generate a client proxy class.
Invoke the Notification Service in processingComplete.svc
Update your processingComplete.svc implementation to call Server2's service after handling its own business logic:
public class ProcessingCompleteService : IProcessingCompleteService { public void ProcessingComplete(int deliveryId) { // Step 1: Execute your core business logic for the delivery ID HandleDeliveryProcessing(deliveryId); // Step 2: Notify Server2 that the delivery ID has been retrieved try { using (var notificationClient = new DeliveryNotificationServiceClient()) { notificationClient.DeliveryRetrieved(deliveryId); notificationClient.Close(); } } catch (Exception ex) { // Critical: Handle call failures (network issues, Server2 downtime) // Log the error and implement retry logic to avoid stale locks Logger.Error($"Failed to notify Server2 for delivery {deliveryId}: {ex.Message}"); // Add to a retry queue (e.g., SQL table, Hangfire) for later processing EnqueueRetryNotification(deliveryId); } } private void HandleDeliveryProcessing(int deliveryId) { // Your Server1 business logic here } private void EnqueueRetryNotification(int deliveryId) { // Logic to store failed notifications for retry } }
If you need the processing on Server1 and the lock release on Server2 to be atomic (i.e., either both succeed or both fail), you'll need to use distributed transactions:
Enable Transaction Flow in WCF
Update both Server2's service contract and binding to allow transaction flow:
[OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void DeliveryRetrieved(int deliveryId);
<bindings> <netTcpBinding> <binding name="TransactionalTcpBinding" transactionFlow="true" /> </netTcpBinding> </bindings>
Wrap Calls in a Transaction Scope on Server1
Use TransactionScope to wrap both Server1's processing and the WCF call in a distributed transaction:
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required)) { HandleDeliveryProcessing(deliveryId); using (var notificationClient = new DeliveryNotificationServiceClient()) { notificationClient.DeliveryRetrieved(deliveryId); notificationClient.Close(); } transactionScope.Complete(); }
Note: Distributed transactions require the MSDTC (Microsoft Distributed Transaction Coordinator) service to be configured and running on both Server1 and Server2.
Cross-server calls are prone to failures, so don't skip these:
- Retry Logic: Implement exponential backoff for failed calls to avoid overwhelming Server2
- Dead-Letter Queue: For notifications that keep failing, store them in a persistent queue and process them periodically
- Logging: Track every notification attempt (success/failure) for debugging
- Lock Timeouts: On Server2, set a timeout on the database lock so it doesn't stay locked indefinitely if the notification never arrives
Instead of using long-lived pessimistic locks on Server2's database, consider:
- Optimistic Locking: Use a
Versioncolumn orIsLockedflag to track lock state, which avoids holding database locks for extended periods - Short Locks: If you must use pessimistic locks, keep them as brief as possible and release them immediately when the notification is received
内容的提问来源于stack exchange,提问作者Abhishek

