How to handle Dtos for objects which implement multiple interfaces?

こ雲淡風輕ζ 提交于 2020-01-01 09:14:49

问题


We are using Dtos in our WCF service interface, but have started to come across issues when the Business Object that the Dto represents implements more than a single interface and we want to return the Dtos in those different contexts and to also be able to treat the Dtos polymorphically on the client.

For example lets say we have an interface for an IBusinessObject with several properties containing details of the relationships of the object, attributes of the object etc etc. I have several implementations of this one being a LinearBusinessObject which implement IBusinessObject and ILinear. There are other implementations of ILinear which are not also business objects, just simple linear things.

Our service has a method to get a business object. This returns a base Dto class (BusinessObjectDto) which declares the common parts of a IBusinessObject (relationships attributes etc) and the LinearBusinessObjectDto which extends BusinessObjectDto and adds the extra information about the linear side of things. This is fine and enables the client to treat the returned BusinessObjects with some degree of polymorphism.

We also want a method which gets a Linear thing. This returns a base class LinearDto which contains the common linear details. The simple linear object implementation extend LinearDto and all is good. But now I have a problem, as I can't have my LinearBusinessObjectDtoextend from both LinearDto and and BusinessObjectDto as only single inheritance is supported, and I can't use interfaces as WCF doesn't know what types to then put in the service contract definitions in the WDSL.

So I've started having 2 dtos for my LinearBusinessObject, one which derives from BusinessObjectDto (LinearBusinessObjectAsBusinessObjectDto) and one which derives from LinearDto (LinearBusinessObjectAsLinearDto) and then converting each one based on the interface I'm interested in.

This seems like its going to result in many extra Dto classes (of which I already have many) and so I'm wondering if there is a better solution than this? Or is this just something we have to live with?


回答1:


A wise man once told me that Object Orientation is the enemy of services.

It seems to me that this is a general OO/SOA problem rather than a specific WCF problem: the old advice of "Favor Composition over Inheritance" comes to mind. When it comes to services especially, Polymorphic designs should not be what you are after in your DTO layer. You should avoid using DTO's that use inheritance or Interfaces (and interfaces are not even possible unless you are serializing/deserialising dynamically...you can't generate concrete proxies using SVCUtil as the concrete types are not known at generation time, but from my memory this is possible when using ChannelFactories in your .NET client...I can't remember the details though).

In general when you create DTO/DataContracts only define members/properties in them that are concrete. Your DTO model should be designed to be flat and cross platform, not Object Orientated.




回答2:


I think I have understood what you are trying to say so please excuse me if I have miss interpreted. Basically you have the following objects which you want to pass via a WCF contract:

class SpecificImplementationOfA : IInterfaceA

class SpecificImplementationOfB : IInterfaceB

class CombinationOfAAndB : IInterfaceA, IInterfaceB

You mentioned that you don't want to use interfaces on the WCF contract because you may return different implementations of the interface.

My question to you is do you actually need same implementation of the object returned to the client, do you actually need the client to receive an instance of CombinationOfAAndB or can it be something else that has the same signature. Secondly does the client ever return any of these objects and if so how does the question above apply?

If the answer to this is that client and service doesn't care can you introduce a client side side specific object which implements the interfaces in question and is registered in the clients knowntypes (KnownTypes are for deserialization not serialization) and let WCF do the mapping.

interface IInterfaceAAndB : IInterfaceA, IInterfaceB

interface ISomeWcfContract
{
    IInterfaceA GetA();
    IInterfaceB GetB();
    IInterfaceAAndB GetAAndB();
}

class ClientImplementationOfA : IInterfaceA
class ClientImplementationOfB : IInterfaceB
class ClientImplementationOfAAndB : IInterfaceAAndB

private static IEnumerable<Type> GetKnownType()
{
    yield return typeof(ClientImplementationOfA);
    yield return typeof(ClientImplementationOfB);
    yield return typeof(ClientImplementationOfAAndB);
}

Yes this does introduce another DTO object but it only needs to exist on the client and it means that your whole contract is implemented to an interface, you can change the implementation / swap in different objects on the client or server without any impact on the other as long as the contract is not broken. You can even introduce new properties and methods without breaking existing clients if done correctly and within reason.




回答3:


WCF provides a hook for defining "known types" on interfaces and inheritance chains. I'm not sure that using either the ServiceKnownType attribute or the KnownType attribute is directly applicable to what you describe but its definitely worth a look.




回答4:


Is this an internal web service or will you be hosting it externally?

If this is internal, have a look at the "Reuse types in referenced assemblies" option in the "Configure service reference" dialogue

  1. Reference your DTO assembly (ensuring your DTOs are in their own assembly) in the client project

  2. Bring up the "Configure Service Reference" dialogue for the targeted WCF service

  3. Select the "Reuse types in referenced assemblies" checkbox

  4. Select the "Reuse types in specified referenced assemblies" radio button

  5. Select the DTO assembly from the list.

The result you receive from your ServiceClient() will be the actual type from the referenced DTO assembly, not a generated proxy. you should now be able to act on your objects polymorphically.




回答5:


Maybe you could encapsulate the base objects instead. So, you have your BusinessObjectDto and LinearDto objects. You then have your LinearBusinessObjectDto that contains two properties, one of type BusinessObjectDto and another of type LinearObjectDto. When you service method returns a LinearBusinessObjectDto it initializes each of these properties. You could then, possible, implement the interfaces on the LinearBusinessObjectDto and deleage the calls to each of the properties. Unfortunately, on the client you could cast to the required interface, but you should be able to choose which property you want to use.

EDIT: Not sure about the interface on DTO part...don't know if a DataContract attributed class that implements interfaces will be created on the client with the interfaces??!?




回答6:


Please read the following Post

http://social.msdn.microsoft.com/Forums/eu/wcf/thread/31102bd8-0a1a-44f8-b183-62926390b3c3

This might be useful

I am Copying the Code for your Reference

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IServer
{
  [OperationContract]
  [ServiceKnownType(typeof(EntityInfo))]
  IEntityInfo GetEntityInfo(string className);
}



回答7:


Why don't you just use composition instead of inheritance? Failing that, would PostSharp multiple inheritance help?



来源:https://stackoverflow.com/questions/9599079/how-to-handle-dtos-for-objects-which-implement-multiple-interfaces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!