0

抱歉,首先问了这么长的问题。我宁愿提出一个更简短的问题,但这是我能提供的最精简的版本,我可以清楚地解释我的观点。

我一直在尝试向我们的客户提供包装服务,该服务应该在其中提供多种服务。它背后的想法是将多次调用减少到一次调用并返回一个包含其他关联对象的对象。为了说明我的观点,让我举一个例子:

假设我们有以下服务:

  • 我的公司.服务.捐赠
  • MyCompany.Services.Payment
  • MyCompany.Services.PartialPayment

通常客户应该查询捐赠服务(带有捐赠ID)以获取捐赠信息,然后使用检索到的捐赠信息,他们应该查询支付服务以获取支付相关详细信息,如果支付是在多个小额支付中完成的,则使用检索到的支付信息,他们应该查询 PartialPayment 服务以获取特定捐赠者的所有捐赠信息。

我将提供一个包装服务来接受donationID 作为单个参数并返回一个类似于此的类,而不是客户端这样做:

[DataContract(Namespace = "http://MyCompany.Services.DonationDetail")]
public class DonationDetail
{
     [DataMember]
     public MyCompany.Services.Donation.Record donationRecord;
     [DataMember]
     public PaymentDetail paymentDetail;    
}

[DataContract(Namespace = "http://MyCompany.Services.DonationDetail")]
public class PaymentDetail
{
     [DataMember]
     public MyCompany.Services.Payment.Record paymentRecord;
     [DataMember]
     public List<MyCompany.Services.PartialPayment.Record> partialPayments;
}

因此,DonationDetail 记录的实例应返回与该捐赠相关的所有相关信息。

当我在包装服务中使用这些单独的服务 DLL* 时出现了我的问题,因为我使用包装服务传递给客户端的任何类都成为包装服务的一部分,并且客户端无法立即使用它们使用服务引用检索到的相应类型而不使用它们编写自定义构造方法以将一种类型转换为另一种类型 - 尽管它们是相同的对象。服务不是在原始命名空间中引用类,而是使用以下类,就像现在对上面提到的类一样:

  • DonationDetail.Record(捐赠记录 - 我希望 MyCompany.Services.Donation.Record)
  • DonationDetail.Record1(付款记录 - 我希望 MyCompany.Services.Payment .Record)
  • DonationDetail.Record2(PartialPayment Record - 我希望 MyCompany.Services.PartialPayment.Record)

有没有办法在没有自定义构造函数的情况下提供这样的接口?因此,如果他们为 MyCompany.Services.PartialPayment WCF 服务使用“PartialPayment”命名空间,他们可以在通过包装服务检索 DonationDetail 后执行以下操作吗?

PartialPayment.Record partialPayment = dDetailObj.paymentDetail.partialPayments[0];

*:不要问我为什么不使用服务引用,除非那是问题的原因,因为此时该选项给我带来了其他问题)

4

1 回答 1

2

So I think what you are saying, effectively, is that if you have two different services that return the same object and when you add this as two different service references to the client, even though ultimately they are the same object as far as the services are concerned (since they reference the same DLL), the client sees them as two different types so you can't take the object returned from one and send it as the input to the other service.

Assuming I have understood your question (and I apologise if I have not)...

You could map one type to the other by constructing it and setting the properties but that is really kind of a pain and not very friendly to the consumer etc, hence I am going to suggest something kind of radical...

Ditch the service references on the client.

Yup, I said it, why would I suggest such a thing!?! Here's why...

First of all I would make sure my project was structured something like this:

Donation Detail Client Library

  • IDonationService (this is the service contract - notice no implementation in the client library)
  • DonationRecord

Payment Detail Client Library

  • IPaymentService (this is the service contract - notice no implementation in the client library)
  • PaymentRecord

Partial Payment Client Library

  • IPartialPaymentService (this is the service contract - notice no implementation in the client library)
  • PartialPaymentRecord

Wrapper Service Client Library (which references the three other client libraries)

  • IWrapperService (this is the service contract - notice no implementation in the client library)

Incidentally, I gave your records different class names but you could use namespaces if you like and call them all Record (I think calling them different names is less confusing, but that is probably just me).

On the service end you reference the client library that you need to implement the service and do whatever you have to do just as you always have.

On the client you reference the client libary (or libraries depending on what service you want to call) too, in the same way (so you effectively have a shared library between server and client - yeah old skool, but hey, you will see why).

The client then has the interface for the service contract and all the data contracts so it does not need the whole service reference, generated code thing. Instead what you can do on your client is something like this:

DonationRecord donation;

using (var cf = new ChannelFactory<IDonationService>("EndpointNameInConfigurationFile"))
{
    IDonationService donationservice = cf.CreateChannel();
    donation = donationservice.GetDonation("Donation1234");
}

using (var cf = new ChannelFactory<IWrapperService>("EndpointNameInConfigurationFile"))
{
    IWrapperService wrapperService = cf.CreateChannel();
    wrapperService.DoSomethingWithDonation(donation);
}

There, you see I took the data contract from one service and sent it to a completely unrelated service and it looks natural (I have an object that is returned from a method on class X and I took it and passed it as an agrument on class Y, job done, just like programming).

NOTE: Using this technique will not stop service references from working just as they always have so any existing client code would not have to change, just if you use your new wrapper service, you could use it like this to save having to map types.

于 2012-04-06T08:23:08.637 回答