KnownType for MessageContract in WCF - wcf

I am using Stream object inside my wcf Contracts so forced to use MessageContract instead of DataContract.
[MessageContract]
public class Document
{
[MessageBodyMember(Order = 1)]
public System.IO.Stream FileData;
}
[MessageContract]
public class A : Document
{
[MessageBodyMember]
public string input;
}
[MessageContract]
public class B : Document
{
[MessageBodyMember]
public string someProp;
}
[ServiceContract]
public interface ISomeService
{
[OperationContract]
Document SomeMethod(Document file);
}
I want the consumer of this service to create object of either A or B and call the service with it. On the service side, I can type cast it to proper object then perform some action.
Problem is I cannot specified KnownType with MessageContract and inherited contracts cannot be exposed to client until they are used in service or declared with KnownType.
I tried google it but couldn't find anything related to KnownType with MessageContract.
As suggested in comment... i updated my message contract with KnownType but they are still not exposed to client through service reference...
[MessageContract]
[KnownType(typeof(FileSystemStoredDocument))]
[KnownType(typeof(FileBoundStoredDocument))]
[KnownType(typeof(SharepointStoredDocument))]
public class Document : DocumentInfo, IDisposable
{
}
Can any one help me what's wrong here?
Note: ALL KnownType are inherited from Document

Message contracts describe exactly how the message should look like. They do support inheritance, but you must specify the exact message contract you're using in a specific operation.
If you check the body parts of the message:
ContractDescription.GetContract(typeof(ISomeService)).Operations[0].Messages[0].Body.Parts
You'll see exactly one part - a Stream object. That's in contrast to data contracts, where the body contains a part of the type Object. So you see why KnownType wouldn't work here.
(The ContractDescription class is used, among other things, to generate WSDL. See the WsdlExporter class.)
What you can do is create a hierarchy of data contracts that would be contained in the message contract, e.g.
[MessageContract]
public class Document
{
[MessageHeader]
public DocumentProperties Properties;
[MessageBodyMember(Order = 1)]
public System.IO.Stream FileData;
}
[DataContract]
[KnownType(typeof(A))]
[KnownType(typeof(B))]
public abstract class DocumentProperties { }
[DataContract]
public class A : DocumentProperties
{
[DataMember]
public string input;
}
[DataContract]
public class B : DocumentProperties
{
[DataMember]
public string someProp;
}
Note that you you cannot have more than one body member if you want to pass a Stream, so the rest of the properties must be in headers.

Related

Common WCF Response Handler?

I have a WCF Client, and the Endpoint has just been upgraded with a new method (OperationContract). I want to write a common method to handle the response from this new method as well as from the existing method at the endpoint.
I am trying to create a "Base" response class and adding common properties to it provided by the WCF endpoint, but I notice in my handler method, the properties are not being retained.
The code for the class I want all responses to inherit from looks like this :
public class ResponseBase
{
public string[] ItemsReturned;
public bool ItemsWereAvailable;
}
So I add partial declarations to get this onto the objects in the endpoint.
public partial class RetrieveResponse :ResponseBase
{
}
public partial class RetrieveResponse2 :ResponseBase
{
}
This way I have a handler method that just accepts "ResponseBase" as its input.
Am I doing this all wrong?
Any class whose instances will be return values and/or parameters of an operation contract should be decorated with the DataContract attribute, and the properties, as DataMembers:
[DataContract]
public class ResponseBase
{
[DataMember]
public string[] ItemsReturned { get; set; }
[DataMember]
public bool ItemsWereAvailable { get; set; }
}
http://msdn.microsoft.com/en-us/library/ms733127.aspx
If they are not, the DataContractSerializer doesn't serialize them.

Abstract classes in client lib, and concrete class in shared lib

How do I solve the serialization problem with abstract class defined in a shared client library, and concrete implementation in a server side library.
Interface in shared client library :
[ServiceContract(SessionMode=SessionMode.Required)]
[ServiceKnownType(typeof(SharedClient.Shape))]
public interface IMyInterface
{
void UploadDrawing(Drawing dr);
}
Concreate Drawing class in shared client library :
[DataContract]
[KnownType(typeof(SharedClient.Shape))]
public class Drawing
{
public Shape s;
}
Abstract class in shared client library :
[DataContract]
abstract public class Shape
{
[DataMember]
public abstract string Name;
}
Concrete class implementation in separate library which references the client library :
[DataContract]
public class Cirle : ClientLibrary.Shape
{
public override string Name { get; set; }
}
I keep getting the exception message:
There was an error while trying to serialize parameter
http://tempuri.org/:Drawing. The InnerException message was 'Type
'Circle' with data contract name
'Circle:http://schemas.datacontract.org/2004/07/' is not expected.
Consider using a DataContractResolver or add any types not known
statically to the list of known types - for example, by using the
KnownTypeAttribute attribute or by adding them to the list of known
types passed to DataContractSerializer.'. Please see InnerException
for more details.
KnownType works in other way. If you want to use KnownType attribute you must use it on the base class to define its child:
[DataContract]
[KnownType(typeof(Circle))]
abstract public class Shape
{
[DataMember]
public abstract string Name;
}
That will not be too much helpful in your case. Try to put ServiceKnownType with concrete class on your interface:
[ServiceContract(SessionMode=SessionMode.Required)]
[ServiceKnownType(typeof(Circle))]
public interface IMyInterface
{
void UploadDrawing(Drawing dr);
}
You doesn't have to define Shape as ServiceKnownType - it is already known because it is used in Drawing but WCF yet doesn't know the Circle type.

Error while running WCF service library

I have built a WCF service library, I have added reference to few 3rd party dll's. This is my interface
[ServiceContract]
public interface IService1
{
[OperationContract]
3rdpartyreturntype GetObj(System.Windows.Controls.Control txtcontrol);
}
// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract]
public class nclass
{
[DataMember]
public System.Windows.Controls.Control txtcontrol
{
get { return txtcontrol; }
set { txtcontrol = value; }
}
[DataMember]
public 3rdpartyreturntype nobj
{
get { return vuiobj; }
set { vuiobj = value; }
}
[DataMember]
public System.Windows.Input.TouchDevice tchdev
{
get { return tchdev; }
set { tchdev = value; }
}
}
and I have implemented the above interface. When I run the service I get the below error, can anyone help me out?
type 'System.Windows.Input.TouchDevice' cannot be serialized. Consider
marking it with the DataContractAttribute attribute, and marking all
of its members you want serialized with the DataMemberAttribute
attribute.
if you want a complex type like a class to be serialized you have to declare each of its members as data member and class as datacontract.
In this case touch device is the complex type(class) so you need to mark all of its members as datamembers and class as datacontract.
but as i can see from type that it belongs to system.windows.input the class may not be serialized. hence you cannot use this as a data member.
If you are using only a set of members belonging to touchDevice class, you can create your own class which will map only the set of touchdevice members which can be serialized. and decorate this new class with datacontract and datamember attributes.
It would appear that the type "System.Windows.Input.TouchDevice" is itself not marked as serializable or a DataContract.
Instead of trying to pass the TouchDevice and the Control directly like that, maybe you can just pass the properties you care about. I'm guessing in the case of the TextControl you just care about the Text value. And for the TouchDevice, maybe the source and target control name?

Wcf inhereted models

[DataContract]
Base
{
[DataMember]
public int Id {get;set;}
}
[DataContract]
A : Base
{
[DataMember]
public string Value {get;set;}
}
[ServiceContract]
interface IService
{
[OperationContract]
void SetValue (Base base);
}
is there a way to use the service like the following style:
new Service ().SetValue (new A ());
You tagged this WCF so I assume you want to use it.
You need to connect to the endpoint using the ChannelFactory and then open the channel.
This will not work:
new Service ().SetValue (new A ());
You need to do smth. like this:
using (var scf = new ChannelFactory< IService >(<Binding>,<EndpointAddress>)
{
IService proxy = scf.CreateChannel();
proxy.SetValue(new (A));
}
This will return you a proxy object that implements the IService interface. You can call the SetValue on this object.
As well as changing the way you're calling the service as indicated by #Flo, you'll also need to make a small change to prepare the Data Contract Serializer to deal with the inheritance hierarchy.
The easiest way of doing this is decorating Base with the KnownTypeAttribute. Like this,
[DataContract]
[KnownType(typeof(A))]
Base
{
[DataMember]
public int Id {get;set;}
}
[DataContract]
A : Base
{
[DataMember]
public string Value {get;set;}
}

WCF - Sending data to server outside of contract

I have a WCF service with a client application. I have complete control over both the client and server implementation. I have hundreds of methods in the WCF contract which need a piece of information supplied by the client. Instead of modifying hundreds of methods, is there a way I can send specific data from the client with every call to the service, possibly somewhere in the channel?
Maybe when the client is setting up the proxy before making the call, it can store this data somewhere in an internal property of the proxy... the data would then get sent to the server and from within the service method I could inspect the OperationContext or some other piece of memory to get this data back and use it?
Any ideas?
It sounds like you are wanting something like headers like with SOAP webservices. I'm not a WCF expert, but this looks like the WCF equivalent.
It shouldn't actually be that hard. The best way I can think of is to write an IClientMessageInspector that adds a SOAP header into the Message.Headers in its BeforeSendRequest method.
See e.g. http://weblogs.asp.net/paolopia/archive/2007/08/23/writing-a-wcf-message-inspector.aspx
You can't do this trivially. It will take some work.
It's true that SOAP Headers are the perfect way to pass out-of-band data to and/or from a service. But you already have your contract defined, and adding headers will change the contract.
I believe you'll have to start using message contracts.
Original:
[DataContract]
public class ComplexObject
{
[DataMember(Name = "Id")]
public int Id;
[DataMember]
public string Name;
}
[ServiceContract()]
public interface IMyContract
{
void MyOperation(ComplexObject co);
}
public class MyService : IMyContract
{
#region Implementation of IMyContract
public void MyOperation(ComplexObject co)
{
// use co.*
}
#endregion
}
Using Message Contracts:
[DataContract]
public class ComplexObject
{
[DataMember(Name = "Id")]
public int Id;
[DataMember]
public string Name;
}
[DataContract]
public class MyHeader
{
[DataMember]
public string UserName;
[DataMember]
public string Password;
}
[DataContract]
public class OutputHeader
{
[DataMember]
public string Token;
}
[MessageContract]
public class MyOperationRequest
{
[MessageHeader]
public MyHeader Authentication;
[MessageBodyMember]
public ComplexObject TheObject;
}
[MessageContract]
public class MyOperationResponse
{
[MessageHeader]
public OutputHeader OutputHeader;
}
[ServiceContract()]
public interface IMyContract
{
MyOperationResponse MyOperation(MyOperationRequest request);
}
public class MyService : IMyContract
{
public MyOperationResponse MyOperation(MyOperationRequest request)
{
// use request.TheObject.*
// Can also read request.Authentication.*
return new MyOperationResponse
{ OutputHeader = new OutputHeader { Token = "someToken" } };
}
}