事实证明,这个话题已经在 SO 上讨论过几次。我将发布我所做的事情,希望有人能从中受益(或者甚至有人给了我更好的方法来实现这一点)。
我实现的目的是将请求对象的集合传递给服务管理器对象;每个请求对象指定一个操作和该操作所需的任何其他参数。
然后,我的服务实现将为收到的每个请求对象获取响应 - 响应数据的类型会有所不同 - 决定因素是请求中指定的操作。也就是说,如果我有一个“GetCatalog”操作,则该请求的结果将是List<Items>。相反,“GetAddressbooks”将产生List<AddressbookRecords>。
这是我需要一个类的通用属性的地方。我的消息响应对象将有一个通用列表作为属性。
最后我结合了@Mihai Caracostea 建议使用对象和发布here 的解决方案。
首先,为了清晰和高效,我修改了 MessageRequest 和 MessageResponse 对象:
public class MessageRequest
{
private readonly string _messageId;
private readonly Operation _operation;
public MessageRequest(string id, Operation operation)
{
_messageId = id;
_operation = operation;
}
public string MessageId { get { return _messageId; } }
public Operation Operation { get { return _operation; } }
}
public class MessageResponse
{
private object _data;
public MessageRequest Request { get; set; }
public T Data<T>()
{
return (T)Convert.ChangeType(_data, typeof(T));
}
public void SetData(object data)
{
_data = data;
}
}
MessageResponse 定义确实实现了这一点。对属性使用 getter / setter 方法 - 我使用 Object _data 字段设置从支持服务接收的数据,并使用 T Data 将数据基本上转换为接收 MessageResponse 对象的客户端读取数据时应有的数据。
所以服务管理器实现看起来像这样:
public List<MessageResponse> GetData(List<MessageRequest> messageRequests)
{
List<MessageResponse> responses = new List<MessageResponse>();
try
{
foreach (MessageRequest request in messageRequests)
{
//Set up the proxy for the right endpoint
SetEndpoint(request);
//instantiate a new Integration Request with the right proxy and program settings
_ir = new IntegrationRequest(_proxy, ConfigureSettings(request));
MessageResponse mr = new MessageResponse { Request = request };
using (IntegrationManager im = new IntegrationManager(_ir))
{
mr.SetData(GetData(im, request));
}
responses.Add(mr);
}
return responses;
}//
catch (Exception)
{
throw;
}
使用 GetData 方法结果的客户端实现如下所示:
List<MessageRequest> requests = new List<MessageRequest>();
requests.Add(new MessageRequest(Guid.NewGuid().ToString(), Operation.GetBudgets));
requests.Add(new MessageRequest(Guid.NewGuid().ToString(), Operation.GetCatalogItems));
List<MessageResponse> responses;
using (ServiceManager sm = new ServiceManager())
{
responses = sm.GetData(requests);
}
if (responses != null)
{
foreach (var response in responses)
{
switch (response.Request.Operation)
{
case Operation.GetBudgets:
List<Budget> budgets = response.Data<List<Budget>>();
break;
case Operation.GetCatalogItems:
List<Item> items = response.Data<List<Item>>();
break;
}
}
}
这只是一个测试 - 但基本上我构建了两个 MessageRequest 对象(获取预算和获取目录项) - 发布到服务并返回 MessageResponse 对象的集合。
这适用于我需要它做的事情。
我想在这个主题上提到的另外两点是我使用反射来确定运行时的响应类型。我能够做到这一点的方法是在操作枚举上指定自定义属性类型:
public enum Operation
{
[DA.Services.ResponseType (Type = ResponseType.CreateOrder)]
CreateOrder,
[DA.Services.ResponseType(Type = ResponseType.GetAddressbooks)]
GetAddressbooks,
[DA.Services.ResponseType(Type = ResponseType.GetCatalogItems)]
GetCatalogItems,
[DA.Services.ResponseType(Type = ResponseType.GetAddressbookAssociations)]
GetAddressbookAssociations,
[DA.Services.ResponseType(Type = ResponseType.GetBudgets)]
GetBudgets,
[DA.Services.ResponseType(Type = ResponseType.GetUDCTable)]
GetUDCTable
}
class ResponseType : System.Attribute
{
public string Type { get; set; }
public const string CreateOrder = "Models.Order";
public const string GetAddressbooks = "Models.AddressbookRecord";
public const string GetCatalogItems = "Models.Item";
public const string GetAddressbookAssociations = "Models.AddressbookAssociation";
public const string GetBudgets = "Models.Budget";
public const string GetUDCTable = "Models.UdcTable";
}
我基本上研究了使用 Activator.CreateType() 通过评估请求中指定的操作上的 ResponseType.Type 来为客户端动态创建响应对象。
虽然这很优雅,但我发现花费时间来处理它是不值得的。这个实现有相当明确的对象,多年来没有改变。我愿意编写一个 switch 语句来涵盖所有场景,而不是使用反射来获得灵活性。现实情况是我只是不需要在这个特定情况下的灵活性。
我要提到的第二点(仅针对阅读此内容的任何人)的启发是“为什么”泛型不能用作类属性。事实证明,这也引起了争论。有一些争论从“它没有意义”到“微软觉得在发行版中做起来太难并放弃了它”。这些讨论可以在here 和here 找到。
最后,其中一个线程提供了指向技术原因的链接。这个原因是编译器无法确定为具有通用属性的对象分配多少内存。这篇文章的作者是 Julian Bucknail,可以找到here。
感谢所有在寻找我的解决方案时提出建议的人。