You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#重构:实现通用Wrapper的Invoke方法,替代多WCF包装类

Reduce Redundancy with Generic Wrapper + Strategy Pattern for Your C# Services

Hey there! Let's tackle your problem of cutting down on those repetitive WCF wrapper classes while implementing that clean wrapper.Invoke(x => x.GetOrderById(5)) pattern you're aiming for. I've worked through similar scenarios with WCF and dual-mode services, so here's a practical implementation plus some optimizations to make this even better.

First: Implement the Generic Wrapper's Invoke Method

Your existing ServiceFactory<T> is already the foundation here—it handles serving up either the local offline implementation or the WCF proxy based on your mode. The generic wrapper will use this factory to get the service instance, handle the method call via lambda, and clean up resources properly (critical for WCF!).

Step 1: Build the Generic ServiceWrapper Class

This class will work with any service interface (like IOrderService) and handle both return-value and void methods:

public class ServiceWrapper<T> where T : class
{
    private readonly ServiceFactory<T> _serviceFactory;

    public ServiceWrapper(ServiceFactory<T> serviceFactory)
    {
        _serviceFactory = serviceFactory;
    }

    // For methods that return a value
    public TResult Invoke<TResult>(Func<T, TResult> serviceCall)
    {
        var service = _serviceFactory.GetService();
        try
        {
            // Execute the lambda directly against the service instance
            return serviceCall(service);
        }
        finally
        {
            // Clean up resources based on service type
            CleanUpServiceInstance(service);
        }
    }

    // Overload for void methods
    public void Invoke(Action<T> serviceCall)
    {
        var service = _serviceFactory.GetService();
        try
        {
            serviceCall(service);
        }
        finally
        {
            CleanUpServiceInstance(service);
        }
    }

    // Reusable cleanup logic
    private void CleanUpServiceInstance(T service)
    {
        // Handle WCF proxies specifically (they implement ICommunicationObject)
        if (service is ICommunicationObject wcfChannel)
        {
            if (wcfChannel.State == CommunicationState.Faulted)
            {
                wcfChannel.Abort(); // Abort if channel is in a bad state
            }
            else
            {
                wcfChannel.Close(); // Properly close healthy channels
            }
        }
        // Dispose local implementations if they support it
        else if (service is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}

Step 2: How This Works

  • The Invoke methods take a lambda (like x => x.GetOrderById(5)) which directly calls the target method on the service instance from your factory.
  • The finally block ensures we always clean up resources: WCF channels need special handling (abort on fault, close otherwise), while local services just get disposed if they implement IDisposable.
  • You don't need any service-specific wrapper classes anymore—this single ServiceWrapper<T> works for all 12 of your service interfaces.

Step 3: Usage Example

Here's how you'd call your IOrderService with this wrapper:

// Set up your factory and wrapper (you can inject these if using DI)
var orderServiceFactory = new ServiceFactory<IOrderService>();
var orderWrapper = new ServiceWrapper<IOrderService>(orderServiceFactory);

// Call a method with a return value
var order = orderWrapper.Invoke(x => x.GetOrderById(5));

// Call a void method (like updating status)
orderWrapper.Invoke(x => x.UpdateOrderStatus(5, OrderStatus.Shipped));

Next: Optimizations to Polish This Implementation

Now that the core Invoke method is working, let's make this more robust and efficient.

1. Extract WCF Channel Management to a Helper

WCF channel handling has some boilerplate—move that to a dedicated helper to keep your wrapper clean:

public static class WcfChannelHelper<T> where T : class
{
    // Cache ChannelFactory to avoid expensive re-creation
    private static readonly Lazy<ChannelFactory<T>> _channelFactory = 
        new Lazy<ChannelFactory<T>>(() => new ChannelFactory<T>("*"));

    public static TResult Execute<TResult>(Func<T, TResult> operation)
    {
        var channel = _channelFactory.Value.CreateChannel();
        try
        {
            var result = operation(channel);
            ((ICommunicationObject)channel).Close();
            return result;
        }
        catch
        {
            ((ICommunicationObject)channel).Abort();
            throw;
        }
    }

    public static void Execute(Action<T> operation)
    {
        var channel = _channelFactory.Value.CreateChannel();
        try
        {
            operation(channel);
            ((ICommunicationObject)channel).Close();
        }
        catch
        {
            ((ICommunicationObject)channel).Abort();
            throw;
        }
    }
}

Then update your ServiceFactory<T> to use this helper for WCF instances—now your factory handles mode switching, and the helper takes care of WCF-specific logic.

2. Use Dependency Injection (DI) for Wrapper/Factory

If you're using a DI container (like Autofac, Microsoft.Extensions.DependencyInjection), register your generic types so you don't have to manually instantiate them everywhere:

For Microsoft DI:

services.AddSingleton(typeof(ServiceFactory<>));
services.AddTransient(typeof(ServiceWrapper<>));

Then inject ServiceWrapper<IOrderService> directly into your controllers/services—no more manual factory setup!

3. Unify Exception Handling

Wrap WCF-specific exceptions (like FaultException) into your own business exceptions so upper layers don't have to deal with WCF details:

public TResult Invoke<TResult>(Func<T, TResult> serviceCall)
{
    var service = _serviceFactory.GetService();
    try
    {
        return serviceCall(service);
    }
    catch (FaultException ex)
    {
        // Convert WCF fault to your custom business exception
        throw new ServiceInvocationException("Service call failed", ex);
    }
    finally
    {
        CleanUpServiceInstance(service);
    }
}

4. Cache WCF Channel Factories

As you saw in the WcfChannelHelper, using Lazy<T> to cache the ChannelFactory<T> avoids the cost of creating a new factory every time—WCF factories are expensive to spin up, so this gives a nice performance boost.

Final Thoughts

This approach eliminates all those redundant service-specific WCF wrappers while keeping your code clean and maintainable:

  • The generic ServiceWrapper<T> handles all service invocation and resource cleanup
  • ServiceFactory<T> manages switching between offline/online modes
  • The lambda-based Invoke syntax keeps your service calls readable and close to direct interface calls

内容的提问来源于stack exchange,提问作者Glauco Cucchiar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:22:27