gpt4 book ai didi

c# - 如何使多个 SOAP Web 服务调用通用以减少冗余?

转载 作者:行者123 更新时间:2023-11-30 16:37:30 25 4
gpt4 key购买 nike

我在下面有这些调用一些 SOAP Web 服务的方法,它们都来自同一个提供商,因此它们都具有相同的方法/调用/等等。我正在寻找一种更面向对象/抽象的方式来调用这些方法而无需编写那么多方法?理想情况下,我希望每个方法都有一个 -> GetClaim()、AddClaim()、SearchClaim()、RemoveClaim() 等。

问题 - 我是否应该传递特定于服务的参数以使方法更通用,通过消除 15 个其他方法都是这样,还是有更好的更 oop/抽象方法?有人可以给我举个例子吗?

    // ex. how can I make these two methods 1?
public async void ClaimSearchForWRG(string url, string userName, string password) {
var client = new WebServiceWRGClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<WebServiceWRG>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new User();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var response = await client.ClaimSearchAsync(user, "", "", 12345, statuscode.NotSet, "");
}

// another call (same provider) with the same call -> ClaimSearchAsync()
public async void ClaimSearchForAWI(string url, string userName, string password) {
var client = new WebServiceAWIClient();
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<WebServiceAWI>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = new ArmUser();
user.UserName = await webService.EncryptValueAsync(userName);
user.Password = await webService.EncryptValueAsync(password);
var response = await client.ClaimSearchAsync(user, "", "", 12345, ArmStatuscode.NotSet, "");
}
// then we have 15 other web service calls from the same provider for ClaimSearchAsync()
// then we have 15 more calls for ClaimGetAsync()
// then we have 15 more calls for AddClaimAsync()
// then we have 15 more calls for RemoveClaimAsync()
// etc, etc, etc

已更新 在尝试使用下面的代码使事情变得更通用(以消除冗余)之后,我在代码中遇到了一些错误。特别是与编译器找不到与我传递给方法的通用实体关联的属性有关。前任。找不到 user.Username -> 错误消息说“‘TTwo’不包含‘UserName’的定义”

    public class Test {
public void TestWebService() {
var ws = new WebService<WebServiceWRG>();
ws.SearchClaim(new WebServiceWRGClient(), new GraceUser(),
"https://trustonline.delawarecpf.com/tows/webservicewrg.svc", "userName", "password");
}
}

public class WebService<T> {
public void SearchClaim<TOne, TTwo>(TOne entity1, TTwo entity2, string url, string userName, string password)
where TOne : class
where TTwo : class
{
var client = entity1;
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
var webService = channelFactory.CreateChannel();
var user = entity2;
user.UserName = webService.EncryptValue(userName);
user.Password = webService.EncryptValue(password);
var response = client.ClaimSearch(user, "", "", 12345, GraceStatuscode.NotSet, "");
}
}

已更新 我被要求展示“ClaimSearchAsync”的作用或它是什么。我从 dotnet 生成的 Web 服务引用文件中复制了这个

System.Threading.Tasks.Task<GRACE_GRACES.WebServiceResult> ClaimSearchAsync(GRACE_GRACES.User user, string ssn, string lastname, int claimnumber, GRACE_GRACES.statuscode statuscode, string assignedto);

因为这是一个网络服务,所以没有任何方法或代码可以说明它的作用。

最佳答案

所提供的示例方法都违反了单一职责原则 (SRP) 和关注点分离 (SoC),因此我开始尝试使它们更通用。

服务和服务客户​​端的创建应该抽象出来成为他们自己的关注点

例如,可以通过通用工厂抽象创建 Web 服务

public interface IWebServiceFactory {
TWebService Create<TWebService>(string uri);
}

以及使用提供的 URL 封装 channel 工厂创建的简单实现。

public class ServiceFactory : IWebServiceFactory {
public TWebService Create<TWebService>(string url) {
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport) {
MaxReceivedMessageSize = Int32.MaxValue,
MaxBufferSize = Int32.MaxValue
};
var endpoint = new EndpointAddress(url);
var channelFactory = new ChannelFactory<TWebService>(binding, endpoint);
TWebService webService = channelFactory.CreateChannel();
return webService;
}
}

服务客户的创建也可以抽象出来成为它自己的关注点。

public interface IClientFactory {
TClient Create<TClient>() where TClient : class, new();
}

将根据您的客户的共同定义实现。

现在,为了创建一个通用客户端,您需要从要调用的成员所涉及的类型中获取预期的通用功能。

这可以允许将约定用于预期的类型。动态表达式用于构造所应用的约定。

SearchClaimAsync 生成以下帮助程序

static class ExpressionHelpers {

public static Func<string, string, TUserResult> CreateUserDelegate<TUserResult>() {
var type = typeof(TUserResult);
var username = type.GetProperty("username", BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
var password = type.GetProperty("password", BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
//string username =>
var usernameSource = Expression.Parameter(typeof(string), "username");
//string password =>
var passwordSource = Expression.Parameter(typeof(string), "password");
// new TUser();
var user = Expression.New(type);
// new TUser() { UserName = username, Password = password }
var body = Expression.MemberInit(user, bindings: new[] {
Expression.Bind(username, usernameSource),
Expression.Bind(password, passwordSource)
});
// (string username, string password) => new TUser() { UserName = username, Password = password }
var expression = Expression.Lambda<Func<string, string, TUserResult>>(body, usernameSource, passwordSource);
return expression.Compile();
}

public static Func<TService, string, Task<string>> CreateEncryptValueDelegate<TService>() {
// (TService service, string name) => service.EncryptValueAsync(name);
var type = typeof(TService);
// TService service =>
var service = Expression.Parameter(type, "service");
// string name =>
var name = Expression.Parameter(typeof(string), "name");
// service.EncryptValueAsync(name)
var body = Expression.Call(service, type.GetMethod("EncryptValueAsync"), name);
// (TService service, string name) => service.EncryptValueAsync(name);
var expression = Expression.Lambda<Func<TService, string, Task<string>>>(body, service, name);
return expression.Compile();
}

public static Func<TClient, TUser, Task<TResponse>> CreateClaimSearchDelegate<TClient, TUser, TResponse>() {
var type = typeof(TClient);
// TClient client =>
var client = Expression.Parameter(type, "client");
// TUser user =>
var user = Expression.Parameter(typeof(TUser), "user");
var method = type.GetMethod("ClaimSearchAsync");
var enumtype = method.GetParameters()[4].ParameterType; //statuscode
var enumDefault = Activator.CreateInstance(enumtype);
var arguments = new Expression[] {
user,
Expression.Constant(string.Empty), //ssn
Expression.Constant(string.Empty), //lastname
Expression.Constant(12345), //claimnumber
Expression.Constant(enumDefault), //statuscode
Expression.Constant(string.Empty)//assignto
};
// client.ClaimSearchAsync(user, ssn: "", lastname: "", claimnumber: 12345, statuscode: default(enum), assignedto: "");
var body = Expression.Call(client, method, arguments);
// (TClient client, TUser user) => client.ClaimSearchAsync(user,....);
var expression = Expression.Lambda<Func<TClient, TUser, Task<TResponse>>>(body, client, user);
return expression.Compile();
}
}

花一些时间查看评论,以更好地了解正在做的事情。

通用网络服务可以定义如下

public class WebService<TWebServiceClient, TWebService, TUser>
where TWebService : class
where TWebServiceClient : class, new()
where TUser : class, new() {

/// <summary>
/// Create user object model
/// </summary>
private static readonly Func<string, string, TUser> createUser =
ExpressionHelpers.CreateUserDelegate<TUser>();
/// <summary>
/// Encrypt provided value using <see cref="TWebService"/>
/// </summary>
private static readonly Func<TWebService, string, Task<string>> encryptValueAsync =
ExpressionHelpers.CreateEncryptValueDelegate<TWebService>();

private readonly IWebServiceFactory serviceFactory;
private readonly IClientFactory clientFactory;
Lazy<TWebServiceClient> client;

public WebService(IWebServiceFactory serviceFactory, IClientFactory clientFactory) {
this.serviceFactory = serviceFactory ?? throw new ArgumentNullException(nameof(serviceFactory));
this.clientFactory = clientFactory ?? throw new ArgumentNullException(nameof(clientFactory));
client = new Lazy<TWebServiceClient>(() => clientFactory.Create<TWebServiceClient>());
}

public async Task<TResponse> SearchClaimAsync<TResponse>(WebServiceOptions options) {
TWebService webService = serviceFactory.Create<TWebService>(options.URL);
TUser user = createUser(
await encryptValueAsync(webService, options.UserName),
await encryptValueAsync(webService, options.Password)
);
Func<TWebServiceClient, TUser, Task<TResponse>> claimSearchAsync =
ExpressionHelpers.CreateClaimSearchDelegate<TWebServiceClient, TUser, TResponse>();
TResponse response = await claimSearchAsync.Invoke(client.Value, user);
return response;
}

//...other generic members to be done
}

public class WebServiceOptions {
public string URL { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}

它自身的代码与实现问题充分分离,允许对其进行隔离测试,以确保其行为符合预期。

如以下单元测试所示

[TestClass]
public class GenericWebServiceTests {
[TestMethod]
public void Should_Create_New_WebService() {
//Arrange
var serviceFactory = Mock.Of<IWebServiceFactory>();
var clientFactory = Mock.Of<IClientFactory>();

//Act
var actual = new WebService<WebServiceWRGClient, IWebService, User1>(serviceFactory, clientFactory);

//Assert
actual.Should().NotBeNull();
}

[TestMethod]
public async Task Should_ClaimSearchAsync() {
//Arrange

var service = Mock.Of<IWebService>();
Mock.Get(service)
.Setup(_ => _.EncryptValueAsync(It.IsAny<string>()))
.ReturnsAsync((string s) => s);

var serviceFactory = Mock.Of<IWebServiceFactory>();
Mock.Get(serviceFactory)
.Setup(_ => _.Create<IWebService>(It.IsAny<string>()))
.Returns(service);

var clientFactory = Mock.Of<IClientFactory>();
Mock.Get(clientFactory)
.Setup(_ => _.Create<WebServiceWRGClient>())
.Returns(() => new WebServiceWRGClient());

string url = "url";
string username = "username";
string password = "password";

var options = new WebServiceOptions {
URL = url,
UserName = username,
Password = password
};

var webService = new WebService<WebServiceWRGClient, IWebService, User1>(serviceFactory, clientFactory);

//Act
var actual = await webService.SearchClaimAsync<WebServiceResult>(options);

//Assert
//Mock.Get(serviceFactory).Verify(_ => _.Create<IService1>(url));
//Mock.Get(service).Verify(_ => _.EncryptValue(username));
//Mock.Get(service).Verify(_ => _.EncryptValue(password));
//Mock.Get(clientFactory).Verify(_ => _.Create<Client1>());
actual.Should().NotBeNull();
}

#region Support

public class User1 {
public string UserName { get; set; }
public string Password { get; set; }
}

public class User2 {
public string UserName { get; set; }
public string Password { get; set; }
}

public class WebServiceWRGClient {
public Task<WebServiceResult> ClaimSearchAsync(User1 user, string ssn, string lastname, int claimnumber, statuscode statuscode, string assignedto) {
return Task.FromResult(new WebServiceResult());
}
}

public enum statuscode {
NotSet = 0,
}

public class Client2 { }

public interface IWebService {
Task<string> EncryptValueAsync(string value);
}

public interface IService2 {
Task<string> EncryptValueAsync(string value);
}

public class Service1 : IWebService {
public Task<string> EncryptValueAsync(string value) {
return Task.FromResult(value);
}
}

public class WebServiceResult {

}
#endregion
}

这应该足以让您开始审查其他成员以使其成为通用的。上面提供的代码已经过测试,并根据原始问题中提供的内容按预期工作。

请注意,根据要重构的成员数量,这似乎是一项艰巨的任务。您应该花一些时间来确保这些努力是值得的。

关于c# - 如何使多个 SOAP Web 服务调用通用以减少冗余?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58088668/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com