C#双Web引用切换难题:基于Autofac实现生产/测试SOAP服务动态切换
解决方案:基于Autofac实现不同命名空间SOAP服务的动态切换
遇到这种不同命名空间、无统一接口的SOAP服务切换问题,最稳妥的方案是适配器模式+Autofac条件注册,既保留所有服务方法的可用性,又能轻松基于配置切换实现。下面是具体步骤:
步骤1:定义统一接口
先把两个服务的公共方法抽成一个统一接口,上层代码只依赖这个接口,完全隔离具体服务的差异。比如假设两个服务都有SubmitOrder和GetOrderStatus方法:
public interface ITradePortalService { string SubmitOrder(OrderRequest request); OrderStatus GetOrderStatus(string orderId); // 这里添加所有你需要调用的公共方法 }
步骤2:为每个SOAP服务创建适配器
分别给两个命名空间的webServices类写适配器,实现上面的统一接口,内部直接调用对应服务的方法:
生产服务适配器
假设生产服务的命名空间是YourNamespace.ProductionService:
public class ProductionTradePortalAdapter : ITradePortalService { private readonly YourNamespace.ProductionService.webServices _innerService; public ProductionTradePortalAdapter() { _innerService = new YourNamespace.ProductionService.webServices(); // 这里可以根据需要设置服务地址、超时等参数 } public string SubmitOrder(OrderRequest request) { // 若两个服务参数类不同,可在此做类型转换适配 return _innerService.SubmitOrder(request); } public OrderStatus GetOrderStatus(string orderId) { return _innerService.GetOrderStatus(orderId); } }
测试服务适配器
测试服务的命名空间是YourNamespace.TestService:
public class TestTradePortalAdapter : ITradePortalService { private readonly YourNamespace.TestService.webServices _innerService; public TestTradePortalAdapter() { _innerService = new YourNamespace.TestService.webServices(); } public string SubmitOrder(OrderRequest request) { return _innerService.SubmitOrder(request); } public OrderStatus GetOrderStatus(string orderId) { return _innerService.GetOrderStatus(orderId); } }
如果两个服务的参数/返回值类型不完全一致,适配器里也可以做简单的类型转换,确保上层接口的一致性。
步骤3:基于配置的Autofac注册
接下来在Autofac容器里,根据配置文件的标识(比如AppSettings里的TradePortalEnvironment)来注册对应的适配器:
方式1:直接根据配置选择注册
var builder = new ContainerBuilder(); var environment = ConfigurationManager.AppSettings["TradePortalEnvironment"]; // 比如"Production"或"Test" if (environment.Equals("Production", StringComparison.OrdinalIgnoreCase)) { builder.RegisterType<ProductionTradePortalAdapter>().As<ITradePortalService>().InstancePerDependency(); } else { builder.RegisterType<TestTradePortalAdapter>().As<ITradePortalService>().InstancePerDependency(); } // 其他注册代码... var container = builder.Build();
方式2:使用Autofac的条件注册(更灵活)
如果需要更复杂的条件判断,也可以用Autofac的条件注册:
builder.RegisterType<ProductionTradePortalAdapter>() .As<ITradePortalService>() .InstancePerDependency() .OnlyIf(_ => ConfigurationManager.AppSettings["TradePortalEnvironment"].Equals("Production", StringComparison.OrdinalIgnoreCase)); builder.RegisterType<TestTradePortalAdapter>() .As<ITradePortalService>() .InstancePerDependency() .OnlyIf(_ => ConfigurationManager.AppSettings["TradePortalEnvironment"].Equals("Test", StringComparison.OrdinalIgnoreCase));
步骤4:在业务代码中使用
现在你只需要在需要调用服务的地方注入ITradePortalService,完全不用关心底层是生产还是测试服务:
public class OrderService { private readonly ITradePortalService _tradePortalService; public OrderService(ITradePortalService tradePortalService) { _tradePortalService = tradePortalService; } public void ProcessOrder(OrderRequest request) { var orderId = _tradePortalService.SubmitOrder(request); var status = _tradePortalService.GetOrderStatus(orderId); // 业务逻辑... } }
额外优化:工厂类方案(可选)
如果你更倾向于用工厂类来控制实例创建,也可以注册一个工厂:
public class TradePortalServiceFactory { public ITradePortalService CreateService() { var environment = ConfigurationManager.AppSettings["TradePortalEnvironment"]; return environment.Equals("Production", StringComparison.OrdinalIgnoreCase) ? new ProductionTradePortalAdapter() : new TestTradePortalAdapter(); } } // Autofac注册工厂 builder.RegisterType<TradePortalServiceFactory>().InstancePerDependency(); // 使用时注入工厂 public class OrderService { private readonly ITradePortalService _tradePortalService; public OrderService(TradePortalServiceFactory factory) { _tradePortalService = factory.CreateService(); } }
这种方案的好处是完全解耦了服务实现和上层代码,后续如果新增第三个服务,只需要加一个适配器和修改配置判断即可,非常容易扩展。
内容的提问来源于stack exchange,提问作者r3plica
相关产品推荐
相关产品推荐

