SvcUtil not generating abstract classes in the client proxy (WCF) - wcf

I have classes in my service defined as
[DataContract]
[KnownType(typeof(MyConcrete1)) ]
[KnownType(typeof(MyConcrete2)) ]
public abstract class MyAbstract
{
[DataMember]
public int AbsInt { get; set; }
}
[DataContract]
public class MyConcrete1 : MyAbstract
{
[DataMember]
public int Concrete1Int { get; set; }
}
[DataContract]
public class MyConcrete2 : MyAbstract
{
[DataMember]
public int Concrete2Int { get; set; }
}
and in my Service, I use it as
[ServiceContract]
public interface IService1
{
[OperationContract]
MyAbstract TestAbstract(MyAbstract value);
}
As you can see, the method TestAbstract takes and returns the abstract parameter MyAbstract, however in the client proxy generated by SvcUtil, the type "MyAbstract" is not abstract! It generated a concrete class.
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name = "MyAbstract", Namespace = "http://schemas.datacontract.org/2004/07/WcfService")]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(WcfService.MyConcrete1))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(WcfService.MyConcrete2))]
public partial class MyAbstract : object, System.Runtime.Serialization.IExtensibleDataObject
{
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
private int AbsIntField;
public System.Runtime.Serialization.ExtensionDataObject ExtensionData
{
get
{
return this.extensionDataField;
}
set
{
this.extensionDataField = value;
}
}
[System.Runtime.Serialization.DataMemberAttribute()]
public int AbsInt
{
get
{
return this.AbsIntField;
}
set
{
this.AbsIntField = value;
}
}
}
How can I force svcUtil to generate MyAbstract as an abstract class? I'm stuck, please help...

You cannot - svcutil cannot know that MyAbstract class is abstract. The metadata that's being exchanged between server and client just doesn't support such a concept. The SOA world doesn't always support everything the OO world has in store.
The service metadata only knows about things like services, method calls, and data contracts - anything else is not part of the service metadata.
You will need to add some extra logic and tweaking to the client code, once it's been creating, if that's a real requirement on your side.
Marc

Related

Different SOAP Headers in request/response using Message Contracts

I'm building some services which have a common header. This header has a certain layout in the request, and a different one in the response (i.e., there are two classes).
However, when I add a reference or use svcutil, the proxy is generated with the same header in both request and response types.
For instance:
[MessageContract]
class Contract<THeader, TBody>
{
[MessageHeader] public THeader Header { get; set; }
[MessageBodyMember] public TBody Body { get; set; }
}
class MyRequestHeader
{
public string RequestorId { get; set; }
}
class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
The OperationContract is something like:
[OperationContract]
public Contract<MyResponseHeader, ResponseBody> Process(Contract<MyRequestHeader, RequestBody> data);
The proxy becomes something like:
var client = new ...;
var header = new MyRequestHeader();
var body = new RequestBody();
**ResponseBody**
response = client.Process(ref header, body);
As you can see, the header (Request) is passed as ref; That probably means WCF is having this header as the same in request and response. And the MyResponseHeader disappears.
Anyone can shed some light on the subject?
There is something strange going on here.
I tried to reproduce your problem, and got the following results (i had to mark some types public, and added [DataContract] to your header classes).
Here is a view of the WSDL:
The generated code (svcutil 4.0.30319.18046) uses MyRequestHeader in the Response message as well:
This is caused by the following XSD:
As you can see there is only one instance generated for the "Header" class.
I tried creating types for the generic classes, as follows:
[MessageContract]
public abstract class Contract<THeader, TBody>
{
[MessageHeader]
public THeader Header { get; set; }
[MessageBodyMember]
public TBody Body { get; set; }
}
[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
public string RequestorId { get; set; }
}
[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
[MessageContract]
public class RequestContract : Contract<MyRequestHeader, string>
{ }
[MessageContract]
public class ResponseContract : Contract<MyResponseHeader, string>
{ }
[ServiceContract]
public interface IService1
{
[OperationContract]
ResponseContract Process(RequestContract data);
}
But that did not fix the problem, the generated client ResponseContract still is generated using a Header of type RequestHeader.
Even changing the service code to use two diffrent message contracts:
[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
public string RequestorId { get; set; }
}
[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
[MessageContract]
public class RequestContract<TBody>
{
[MessageHeader]
public MyRequestHeader Header { get; set; }
[MessageBodyMember]
public TBody Body { get; set; }
}
[MessageContract]
public class ResponseContract<TBody>
{
[MessageHeader]
public MyResponseHeader Header { get; set; }
[MessageBodyMember]
public TBody Body { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ResponseContract<string> Process(RequestContract<string> data);
}
does not solve the problem:
Even removing all shared inheritance and generics as follows:
[DataContract(Name="RequestHeader")]
public class MyRequestHeader
{
public string RequestorId { get; set; }
}
[DataContract(Name = "ResponseHeader")]
public class MyResponseHeader
{
public string ErrorMessage { get; set; }
}
[MessageContract(WrapperName="RequestMessage")]
public class RequestContract
{
[MessageHeader]
public MyRequestHeader Header { get; set; }
[MessageBodyMember]
public string Body { get; set; }
}
[MessageContract(WrapperName = "ResponseMessage")]
public class ResponseContract
{
[MessageHeader]
public MyResponseHeader Header { get; set; }
[MessageBodyMember]
public string Body { get; set; }
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ResponseContract Process(RequestContract data);
}
still results in the RequestHeader being used in the ResponseMessage.
I think the answer lies somewhere in this documentation:
WSDL Considerations
When generating a Web Services Description
Language (WSDL) contract from a service that uses message contracts,
it is important to remember that not all message contract features are
reflected in the resulting WSDL [sic]. Consider the following points: WSDL
cannot express the concept of an array of headers. When creating
messages with an array of headers using the
MessageHeaderArrayAttribute, the resulting WSDL reflects only one
header instead of the array.
The resulting WSDL document may not reflect some protection-level
information.
The message type generated in the WSDL has the same name as the class
name of the message contract type.
When using the same message contract in multiple operations, multiple
message types are generated in the WSDL document. The names are made
unique by adding the numbers "2", "3", and so on, for subsequent uses.
When importing back the WSDL, multiple message contract types are
created and are identical except for their names.

WCF Interface as parameter

I am using interface as input parameter in OperationContract. But when i generate proxy class at client side. I am not able to access the members of interface or class implemeting the ITransaction interface. I am only geeting is object
Service Interface
[ServiceContract]
public interface IServiceInterface
{
[OperationContract]
string SyncDatabase(ITransaction TransactionObject);
}
Service class
class SyncService:IServiceInterface
{
public string SyncDatabase(ITransaction TransactionObject)
{
return "Hello There!!";
}
}
Interface
public interface ITransaction
{
ExpenseData ExpData { get; set; }
void Add(ITransaction transactionObject);
}
Data Contract
[DataContract]
public class Transaction:ITransaction
{
[DataMember]
public ExpenseData ExpData
{
get;
set;
}
public void Add(ITransaction transactionObject)
{
}
}
In above case should i also copy the iTransaction class and interface on client
You actually need to make your ServiceContract aware of the implementation of the interface you pass as a parameter, so WCF will include it in the WSDL.
This should work:
[ServiceContract]
[ServiceKnownType(typeof(Transaction))]
public interface IServiceInterface
{
[OperationContract]
string SyncDatabase(ITransaction TransactionObject);
}
Use [KnownType(typeof(testClass))].
Refer these links:
msdn.microsoft.com/en-us/library/ms730167.aspx
www.codeproject.com/Tips/108807/Implementing-KnownType-Attribute
Try making your interface the [DataContract] and use the [KnownType] attribute to tell WCF what the known implementations of that interface are.
[DataContract]
[KnownType(typeof(Transaction))]
public interface ITransaction
{
[DataMember]
ExpenseData ExpData { get; set; }
void Add(ITransaction transactionObject);
}

VS-generated clients of my service throw Deserialization exceptions

Our WCF service has just one method:
[ServiceContract(Name = "Service", Namespace = "http://myservice/")]
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
public interface IService {
Response Execute(Request request);
}
public class Service : IService {
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider) {
return KnownTypesResolver.GetKnownTypes();
}
public Response Execute(Request request) {
return new MyResponse { Result = MyEnumHere.FirstValue };
}
}
Both the Request and Response class includes a ParameterCollection member.
[Serializable]
[CollectionDataContract(Name = "ParameterCollection", Namespace = "http://myservice/")]
[KnownType("GetKnownTypes")]
public class ParameterCollection : Dictionary<string, object> {
private static IEnumerable<Type> GetKnownTypes()
{
return KnownTypesResolver.GetKnownTypes();
}
}
Subclasses of Request and Response store their values into the ParameterCollection value bag.
I am using the KnownTypesResolver class to provide type information across all Service objects.
public static class KnownTypesResolver {
public static IEnumerable<Type> GetKnownTypes()
{
var asm = typeof(IService).Assembly;
return asm
.GetAllDerivedTypesOf<Response>() // an extension method
.Concat(new Type[] {
typeof(MyEnumHere),
typeof(MyEnumHere?),
typeof(MyClassHere),
typeof(MyClassListHere),
});
}
}
If I'm not mistaken, everything should have proper type information for proxy class generation tools to produce well-defined classes client-side.
However, whenever one of the Response subclasses (i.e. MyResponse) contains an enum value such as MyEnumHere, WCF starts complaining that the deserializer has no knowledge of the MyEnumHere value. It should have. I provided a KnownTypeAttribute for this very reason.
The client-side proxy class does have a MyEnumHere enum in the Reference.cs file; the problem is that the ParameterCollection class has no KnownTypeAttributes generated for it.
I resorted to hand-editing and including the following lines in the generated Reference.cs file:
//>
[KnownTypeAttribute(typeof(MyEnumHere))]
[KnownTypeAttribute(typeof(MyEnumHere?))]
[KnownTypeAttribute(typeof(MyClassHere))]
[KnownTypeAttribute(typeof(MyClassListHere))]
//<
public class ParameterCollection : Dictionary<string, object> { /* ... */ }
Hand-editing generated files is horrible. But this makes the clients work. What am I doing wrong? How can I define my Service objects so that the VS-proxy classes that are generated are correct from the get-go?
Thanks for your time.
WCF does not work well with Dictionary because it is not interoperable. You may use Array, List or custom collection to make sure that your data is properly serialized.
Code below uses List<ParamCollectionElement> instead of Dictionary. I also removed some redundant attributes.
[DataContract]
public class Request
{
[DataMember]
public ParameterCollection ParameterCollection { get; set; }
}
[DataContract]
public class Response
{
[DataMember]
public ParameterCollection ParameterCollection { get; set; }
}
[DataContract]
public class MyResponse : Response
{
[DataMember]
public MyEnumHere Result { get; set; }
}
public class ParamCollectionElement
{
public string Key { get; set; }
public object Value { get; set; }
}
[CollectionDataContract(Name = "ParameterCollection")]
public class ParameterCollection : List<ParamCollectionElement>
{
}
public static class KnownTypesResolver
{
public static IEnumerable<Type> GetKnownTypes()
{
return
new Type[] {
typeof(MyEnumHere),
typeof(MyEnumHere?),
typeof(Request),
typeof(Response),
typeof(MyResponse)
};
}
}
[DataContract]
public enum MyEnumHere
{
[EnumMember]
FirstValue,
[EnumMember]
SecondValue
}
[ServiceKnownType("GetServiceKnownTypes", typeof(Service))]
[ServiceContract(Name = "Service")]
public interface IService
{
[OperationContract]
Response Execute(Request request);
}
public class Service : IService
{
public static IEnumerable<Type> GetServiceKnownTypes(ICustomAttributeProvider provider)
{
return KnownTypesResolver.GetKnownTypes();
}
public Response Execute(Request request)
{
var result = new MyResponse
{
Result = MyEnumHere.FirstValue,
ParameterCollection = new ParameterCollection()
};
result.ParameterCollection.Add(new ParamCollectionElement {Key = "one", Value = MyEnumHere.FirstValue});
result.ParameterCollection.Add(new ParamCollectionElement { Key = "two", Value = new Response() });
return result;
}
}
Make sure you have [DataContract] on your enum and [EnumMember] on each of the enum members:
[DataContract]
public enum MyEnumHere
{
[EnumMember]
SomeValue,
[EnumMember]
OtherValue,
[EnumMember]
OneMoreValue
}
That should cause the proxy-enum to be built out (with its member values) in your client without having to manually change the Reference.cs file.

Send a list with appointments through WCF

I would like to send a list of Appointments through WCF. My Interface looks like this:
[ServiceContract]
public interface IServices
{
[OperationContract]
string addAppointments(List<Appointment> appointmentList);
}
If I call my WCF Service I'm always getting the following error:
Type 'Microsoft.Exchange.WebServices.Data.Appointment' cannot be
serialized. Consider marking it with the DataContractAttribute
attribute, and marking all of its members you want serialized with the
DataMemberAttribute attribute. See the Microsoft .NET Framework
documentation for other supported types.
My Service currently looks like this:
class Service : IServices
{
public string addAppointments(List<Appointment> appointmentList)
{
foreach (Appointment app in appointmentList)
{
Console.WriteLine(app.Organizer.Name);
}
return "true";
}
}
It's not your service that's at fault, it's the class your passing, Appointment.
Start by adding [DataContract] to your class. then [DataMember] to each of the properties you'd like to pass.
For example, if you started with:
public class Appointment{
public DateTime Date { get; set; }
public string Name { get; set; }
}
You can make it serializable by WCF's DataContractSerializer by adding those attributes:
[DataContract]
public class Appointment{
[DataMember]
public DateTime Date { get; set; }
[DataMember]
public string Name { get; set; }
}

WCF not exposing fields but uses ExtensionData

Im trying to create a WCF which consumes a List
public class myClass {
public int ID { get;set;}
}
In my service i write
[DataContract]
public class myClass
{
public int ID { get; set; }
}
And my client nows the entity
ServiceReference2.myClass[] sendData = new ServiceReference2.myClass[2];
but when i would add a new myClass to the array like
ServiceReference2.myClass add1= new ServiceReference2.myClass();
the entity only exposes the field 'ExtensionData' and not the field ID
What am i doing wrong
You forgot to decorate the ID property with the DataMember attribute:
[DataContract]
public class myClass
{
[DataMember]
public int ID { get; set; }
}