Suppose this simple scenario:
My client has an already working .net application and he/she wants to expose some functionality through WCF. So he gives me an assembly, containg a public class that exposes the followig method.
OrderDetail GetOrderDetail (int orderId) // Suppose OrderDetail has {ProductId, Quantity, Amount)
Now, I want some members of OrderDetail (Amount) not to be serialized.
According to http://msdn.microsoft.com/en-us/library/aa738737.aspx, the way to do this is by means of the [DataContract] and [DataMember]/[IgnoreDataMember] attributes. However, that's not an option for me because I can not modify client's source code. So I'm looking for a way to specify which members I want to serialize out, outside the type's definition. Something that should look like this:
[OperationContract]
[IgnoreMember(typeof(OrderDetail), "Amount" )]
OrderDetail QueryOrder(int orderId){
return OrderDetail.GetOrderDetail(orderId)
}
Is there any way to to this?
Thanks,
Bernabé
Don't send the clients objects across the wire, create a DTO from the clients object containing only the information that you want to send and send that instead.
This allows you to control exactly what information gets sent, and is in keeping with the WCF intentions of passing messages and not objects
So create an OrderDetailDto class and populate this with the data from the OrderDetail returned by the call to the method in the clients code. Decorate The OrderDetailDto with the DataContract and DataMember attributes (you can rename the class in here so that when it is returned by WCF it is returned with the name OrderDetail)
Repeat this for all objects in the client code, so that at the service boundary you basically convert from DTO->Client objects and Client Objects->DTO
EDIT
Whilst there might be an option which allows what you have asked for (I am not aware of one, but hopefully someone else might be) consider that when you send use your client objects as DTOs you are using them for two purposes (the client object and the message contract), which is against the Single Responsibility Principle and when you get them on the client side they will not be the same client side objects, just DTOs with the same properties, you will not be able to get behaviour in the client side objects (at least not without sharing libraries on the server side and client side).
By binding the data contract to the objects you also end up having to manage the changes to client objects and data contracts as one thing. When they are separate you can manage the changes to client side objects without neccessarily changing the DTOs, you can just populate the differently.
Whilst it seems like it is a lot of work to create the DTOs, in the end I think it will be worth it.
You will have to write a wrapper class that only exposes the desired properties and simply calls the class your client provided to gets its values.
The only other option would be to emit a new dynamic class using reflection and serialize that (see http://msdn.microsoft.com/en-us/library/system.reflection.emit.typebuilder.aspx), but its probably not worth the effort unless you need to build a lot of wrapper classes.
Related
I have a WCF method which takes an argument that is a custom class, say,
void MyWCFMethod(MyCustomClass MethodArgument)
In the above, MyCustomClass has a number of constructor overloads. The service has a reference to the class but not the client. I want to allow the client to use the other overloads but the default constructor is the only one that seems to be allowed. Is there a way to do this?
You can certainly do this, but I think it is important to know why the Data Transfer Objects (DTOs) do not expose logic over the service reference.
The WSDL\XSD metadata that is used in order to generate the client proxy to access the WCF Service only describes the web service by the operations exposed and the datatypes exchanged.
Specifically, XSD only describes the structure of your DTOs and not the logic - that is why there is only the default constructor and public properties/fields available on the client proxy.
So the solution is to put all of your custom classes exchanged between the client and service in a separate shared library. This way both sides of the wire have access to the additional logic (like your parameterized constructors) that you could not obtain via WSDL\XSD.
I guess - no!
As I understand MyCustomClass is data contract and marked by [DataContract] attribute.
So WCF runtime will use DataContractSerializer (by default) to deserialize data from received message to the instance of object.
So where can DataContractSerializer get additional parameters for your specific constructors?
Instance of data contract must have public parameter-less constructor to be instantiated.
But maybe you can write own serializer (but keep in mind that DataContractSerializer cannot be inherited)... and provide additional data to constructor. But if you can get that information somewhere just do it in public parameter-less constructor of your data contract.
So I guess you are doing something wrong. Try to specify what is the goal to pass data in constructor in your case. Maybe your app can use some another solution.
Sorry for the long question in the first place. I would rather prefer to come up with a shorter question but this is the most stripped version I could provide that I can clearly explain my point.
I have been trying to deliver a wrapper service to our client which should provide multiple services in it. Idea behind it is to reduce multiple calls to a one call and return a single object which has other associated objects in it. To illustrate my point, let me give following example:
Let's say we have following services:
MyCompany.Services.Donation
MyCompany.Services.Payment
MyCompany.Services.PartialPayment
Normally client should query Donation service (with a donationID) to get donation information, and then using the retrieved donation information, they should query Payment service to get payment related details, and if the payment is done in multiple small payments, using retrieved payment information, they should query PartialPayment service to get all donation information for a particular Donor.
Instead of client doing this, I am going to provide a wrapper service to accept donationID as a single parameter and return a class similar to this:
[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;
}
So an instance of DonationDetail record should return all relevant information with that donation.
My problem arises when I use these individual services DLL's* in my wrapper service since any class I pass to client using wrapper service becomes part of the wrapper service and client can't use them right away with the corresponding types they retrieved using service references without writing a custom construction method to convert one type to another - although they are same objects. Instead of referring classes in original namespace, service uses following classes something like that now for the classes mentioned above:
DonationDetail.Record (Donation Record - I would expect MyCompany.Services.Donation.Record)
DonationDetail.Record1 (Payment Record - I would expect MyCompany.Services.Payment .Record)
DonationDetail.Record2 (PartialPayment Record - I would expect MyCompany.Services.PartialPayment.Record)
Is there a way to provide such an interface without a custom constructor? So, if they use "PartialPayment" namespace for the MyCompany.Services.PartialPayment WCF service, can they do something below after DonationDetail is retrieved via wrapper service?
PartialPayment.Record partialPayment = dDetailObj.paymentDetail.partialPayments[0];
*: Don't ask me why I don't use service references unless that is the cause of the problem, since that option gives me other problems to me at this point)
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.
I have a server side service called ConstructionManager, one of its operation is GetAll() which return a list of constructions. Construction is a data contract, and there are several types which inherit from Construction (Buildings, Apartments etc..)
When I send the list of apartments all is good, all properties is on their place, but when i receive that list at client side, and see what it is in the received object at run time, in Non Public Members i saw all the properties that are specific to type that inherits from Construction, like Rooms, Floor, but in Result View it shows all properties have the value "0", and not the value with which they were sent.
On data contract Construction, at the top of class, are KnownType attributes to inherited classes.
It maybe helpful to know, I use Web Service Software Factory.
Sorry for my bad English.
If you see that properties are populated in an object in your service just before it is sent over the wire to the client (i.e. just before serialization), and then see that the received object is missing the values in those properties just after it is received by the client, it means that they were lost in the serialization process.
There are 2 things you need to remember about serialization:
You need to make sure the classes you send over the wire are marked with the [DataContract] attribute, and that all properties within that are marked with the [DataMember] attribute. If a property is not a .NET type, then the class that defines (and the properties within it) it also needs to be marked up with these attributes.
Class Inheritance is lost in serialization. If you create an object of type "Building", and your WCF service method returns a type of "Construction", then the message sent to the client might not serialize correctly (eg. could it only be serializing the properties defined in the base type "Construction"?). I suggest you test this out by making your service return the inherited type rather than the base type, and see if it fixes the problem.
I think that the 2nd point is the most likely cause of your issues. If you provide your code I can help you in more detail
I have inherited an application that is logically split into 4 tiers, but physically resides across two. The 4 logical tiers are:
asp.net website
business logic in a C# .Net assembly (referenced from website)
data access c# assembly - classes generated by codesmith tool (referenced from business logic)
sql server database
An example of the way that the website interacts with the business layer is:
Booking b = new Booking();
b.property1 = x;
b.property2 = y;
result = b.method();
ie. it sets the data on public properties of the biz class then executes a method that in-turn reads from the properties.
Unfortunately, there are lots of properties and some of these are not base types, they are other objects eg the Booking object contains collection of Vouchers objects
I need to make the tiers 2-4 available to a new user interface (a very different website that will serve in-store kiosks).
I would like to expose the business layer through WCF. I have created an IBooking interface, defined the method signatures and decorted with [OperationContract] etc. Where I'm stuck is how to manage the data. I realise that I could define a data contract to match the various public properties of the Booking object but then I would need to make significant changes to the existing website - rather than it setting the properties and calling a method withouth parameters it would need to populate an instance of the data contract and pass this as a parameter to every method call.
Could anyone advise on the best way to approach this please. I am able to make changes to the exisiting website but I'd like to keep these to a minimum.
Many thanks,
Rob.
I'd suggest the simplest means of implementing this would be to create a WCF wrapper around your existing business logic without altering your current website. This can be done without any (significant) code changes to what you already have. The 'downside', if you consider it such, is that your existing website won't use your WCF services.
You've already created an contract for the service. If you haven't already, create message contracts for the operation parameters. Then you can create your 'new' website by working with the service contract & message contracts.
Services are different to OO, in that you don't normally set properties & then call parameterless methods - instead you invoke an operation and include any relevant, required data at the same time. Your service implementation - the class that implements the IBooking contract - will do the work of
instantiating your existing classes
populating those objects
calling the parameterless methods, and
returning results.
e.g.
// contract
[OperationContract]
MyResponseMessage DoMethod(MyResultRequest requestData);
// and the implementing class (the 'service')
public MyResponseMessage DoMethod(MyResultRequest requestData)
{
MyResponseMessage responseData = new MyResponseMessage();
Booking b = new Booking();
b.property1 = requestData.X;
b.property2 = requestData.y;
responseData = b.method();
}
I'm working on a simple plug-in framework. WCF client need to create an instance of 'ISubject' and then send back to service side. The 'ISubject' can be extended by the user. The only thing client knows at runtime is ID of a subclass of 'ISubject'.
Firstly, client need to get type information of a specific subclass of 'ISubject'. Secondly, client using reflection to enumerate all members to create a custom property editor so that each member can be asigned with proper value. Lastly, client create an instance of that subclass and send back to service.
The problem is how does client get the type information through WCF communication?
I don't want client to load that assembly where the subclass (of 'ISubject') exists.
Thanks
First, you need to be aware that there is no magic way that WCF will provide any type information to your client in the scenario you have descibed. If you are going to do it, you will have to provide a mechanism yourself.
Next, understand that WCF does not really pass objects from server to client or vice versa. All it passes are XML infosets. Often, the XML infoset passed includes a serialized representation of some object which existed on the sender's side; in this case, if the client knows about that type (i.e. can load the type's metadata from its assembly), it can deserialize the XML to instantiate an identical object on the client side. If the client doesn't have the type metadata, it can't: this is the normal case with WCF unless data contract types are in assemblies shared by both server and client implementations (generally not a good idea).
The way WCF is normally used (for example if the client is implemented using a "Service Reference" in Visual Studio), what happens is that the service publishes WSDL metadata describing its operations and the XML schemas for the operation parameters and return values, and from these a set of types is generated for use in the client implementation. These are NOT the same .NET types as the data contract types used by the service implementation, but they are "equivalent" in the sense that they can be serialized to the same XML data passed over the network. Normally this type generation is done at design time in Visual Studio.
In order to do what you are trying to do, which is essentially to do this type generation at runtime, you will need some mechanism by which the client can get sufficient knowledge of the structure of the XML representing the various types of object implementing ISubject so that it can understand the XML received from the service and generate the appropriate XML the service is expecting back (either working with the XML directly, or deserializing/serializing it in some fashion). If you really, really want to do this, possible ways might be:
some out-of-band mechanism whereby the client is preconfigured with the relevant type information corresponding to each subclass of ISubject that it might see. The link provided in blindmeis's answer is one way to do that.
provide a separate service operation by which the client can translate the ID of the subclass to type metadata for the subclass (perhaps as an XSD schema from which the client could generate a suitable serializable .NET type to round trip the XML).
it would also be feasible in principle for the service to pass type metadata in some format within the headers of the response containing the serialized object. The client would need to read, interpret and act on the type infomation in an appropriate fashion.
Whichever way, it would be a lot of effort and is not the standard way of using WCF. You will have to decide if it's worth it.
I think you might be missing something :)
A major concept with web services and WCF is that we can pass our objects across the network, and the client can work with the same objects as the server. Additionally, when a client adds a service reference in Visual Studio, the server will send the client all the details it needs to know about any types which will be passed across the network.
There should be no need for reflection.
There's a lot to cover, but I suggest you start with this tutorial which covers WCF DataContracts - http://www.codeproject.com/KB/WCF/WCFHostingAndConsuming.aspx
To deserialize an object the receiving side will need to have the assembly the type is defined in.
Perhaps you should consider some type of remoting or proxying setup where the instance of ISubject lives on one side and the other side calls back to it. This may be problematic if you need to marshal large amounts of data across the wire.
wcf needs to know the real object(not an interface!) which should be sent across the wire. so you have to satisfy the server AND the clientproxy side from the WCF service that they know the types. if you dont know the object type while creating the WCF service, you have to find a way to do it in a dynamic way. i use the solution from here to get the knownTypes to my WCF service.
[ServiceContract(SessionMode = SessionMode.Required]
[ServiceKnownType("GetServiceKnownTypes", typeof(KnownTypeHelper))]//<--!!!
public interface IWCFService
{
[OperationContract(IsOneWay = false)]
object DoSomething(object obj);
}
if you have something "universal" like the code above, you have to be sure that whatever your object at runtime will be, your WCF service have to know this object.
you wrote your client create a subclass and sent it back to the service. if you want to do that, WCF(clientproxy and server!) needs to know the real type of your subclass.