WCF
async call
fire and forget
sample code
web services

Need sample fire and forget async call to WCF service

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In WCF, a fire-and-forget call is about returning control to the caller quickly, not guaranteeing that downstream processing completed. This is useful for non-critical side effects such as telemetry and best-effort audit logs. For critical workflows, you should use durable messaging rather than pure in-memory background dispatch.

What Fire-and-Forget Means in Practice

A caller can continue immediately while background code attempts to deliver a WCF message. The request appears fast, but failures can still occur after the caller has already moved on.

That means you need to answer two design questions early:

  • Can this event be lost occasionally.
  • What should happen when outbound queue is full.

If event loss is unacceptable, fire-and-forget is the wrong model.

One-Way WCF Contract Example

WCF supports one-way operations using IsOneWay = true. This avoids waiting for response payloads.

csharp
1[ServiceContract]
2public interface IAuditService
3{
4    [OperationContract(IsOneWay = true)]
5    void WriteAudit(AuditEvent evt);
6}
7
8[DataContract]
9public sealed class AuditEvent
10{
11    [DataMember] public string CorrelationId { get; set; } = "";
12    [DataMember] public string Action { get; set; } = "";
13    [DataMember] public DateTime CreatedAtUtc { get; set; }
14}

One-way improves caller latency, but it still does not imply durable delivery.

Bounded Background Dispatcher Pattern

A practical implementation is to queue events in memory and process them in a single background worker.

csharp
1using System.Collections.Concurrent;
2using System.ServiceModel;
3
4public sealed class AuditDispatcher : IDisposable
5{
6    private readonly BlockingCollection<AuditEvent> _queue = new(capacity: 5000);
7    private readonly CancellationTokenSource _cts = new();
8    private readonly Task _worker;
9
10    public AuditDispatcher()
11    {
12        _worker = Task.Run(ProcessLoopAsync);
13    }
14
15    public bool TryEnqueue(AuditEvent evt) => _queue.TryAdd(evt);
16
17    private async Task ProcessLoopAsync()
18    {
19        foreach (var evt in _queue.GetConsumingEnumerable(_cts.Token))
20        {
21            try
22            {
23                var factory = new ChannelFactory<IAuditService>("AuditEndpoint");
24                var client = factory.CreateChannel();
25                client.WriteAudit(evt);
26                ((IClientChannel)client).Close();
27                factory.Close();
28            }
29            catch (Exception ex)
30            {
31                Console.Error.WriteLine($"Audit send failed: {ex.Message}");
32            }
33
34            await Task.Yield();
35        }
36    }
37
38    public void Dispose()
39    {
40        _queue.CompleteAdding();
41        _cts.Cancel();
42        try { _worker.Wait(TimeSpan.FromSeconds(5)); } catch { }
43        _cts.Dispose();
44        _queue.Dispose();
45    }
46}

This is best-effort by design and should be documented that way.

Usage from Application Flow

Request handlers enqueue and return quickly.

csharp
1public sealed class CheckoutService
2{
3    private readonly AuditDispatcher _dispatcher;
4
5    public CheckoutService(AuditDispatcher dispatcher)
6    {
7        _dispatcher = dispatcher;
8    }
9
10    public void CompleteOrder(string orderId)
11    {
12        var accepted = _dispatcher.TryEnqueue(new AuditEvent
13        {
14            CorrelationId = Guid.NewGuid().ToString("N"),
15            Action = $"order_completed:{orderId}",
16            CreatedAtUtc = DateTime.UtcNow
17        });
18
19        if (!accepted)
20        {
21            Console.Error.WriteLine("Audit queue full");
22        }
23    }
24}

A clear queue-full policy avoids surprise behavior during traffic spikes.

When to Use Durable Messaging Instead

If compliance, billing, or legal auditing depends on the event, move to durable queue infrastructure.

Reliability features you need in that case:

  • Persist before acknowledging caller.
  • Retry with backoff.
  • Dead-letter capture for poison events.
  • Monitoring for backlog age and send failures.

In WCF ecosystems this can involve MSMQ-backed services or migration to a dedicated broker.

Operational Checks Before Production

Run failure drills, not just happy-path tests.

Test at least:

  • Endpoint unavailable for several minutes.
  • Queue reaches capacity.
  • Process restarts with pending items.
  • Credentials or certificates expire.

Track dropped events and recovery time. If loss is too high, redesign to durable delivery.

Common Pitfalls

  • Assuming one-way WCF operations guarantee eventual processing.
  • Using unbounded in-memory queues and risking memory exhaustion.
  • Hiding failures by skipping logs and metrics for background sends.
  • Using fire-and-forget for business-critical or compliance-critical events.
  • Not defining queue overflow behavior until after incidents occur.

Summary

  • Fire-and-forget optimizes caller latency, not delivery guarantees.
  • One-way WCF contracts are useful for non-critical side effects.
  • Use bounded queues and explicit failure visibility for best-effort designs.
  • Choose durable messaging when event loss is unacceptable.
  • Define overflow, retry, and shutdown policy before release.

Course illustration
Course illustration

All Rights Reserved.