I'm desesperated, I'm trying to update an existing service reference to a WCF service (sharing types) and I can't. I've tryied all what I've found on Google (social.msdn, stackoverflow, ...) but I haven't found the solution to my problem.
I've have a ServiceContract and I add a new Operation like the code below:
[ServiceContract]
public partial interface IServiceDTO : IGenericServiceDTO<EntityDTO>
{
// Some OperationContracts working like
[OperationContract]
EntityDTO[] Method(int field);
// NewMethod
[OperationContract]
OtherEntityDTO[] NewMethod(int field);
}
[DataContract]
public class EntityDTO {
// Some properties working
}
[DataContract]
public class OtherEntityDTO {
// Some properties working
[DataMember]
YetAnotherEntity NewProperty {get;set;}
}
When I try to update the service reference it throws me the follwing error:
Attempting to download metadata from 'http://localhost:65499/Services/Acciones/ProcesoServiceDTO.svc' using WS-Metadata Exchange or DISCO.
Error: Cannot import wsdl:portType
Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter
Error: Referenced type 'mpm.seg.ServiceModel.DTO.DataContracts.Acciones.ProcesoDTO, mpm.seg.ServiceModel.DTO.DataContracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' with data contract name 'ProcesoDTO' in namespace 'http://schemas.datacontract.org/2004/07/mpm.seg.ServiceModel.DTO.DataContracts.Acciones' cannot be used since it does not match imported DataContract.
Need to exclude this type from referenced types.XPath to Error Source: //wsdl:definitions[#targetNamespace='http://tempuri.org/']/wsdl:portType[#name='IProcesoServiceDTO']
First of all, I don't understand exactly the sentence "...cannot be used since it does not match imported DataContract." How the svcutil is trying to match referenced type to imported DataContract? I've referenced the project that have the referenced types on the client project, cause server and client are in the same solution, but I've tried to separate them and reference exactly the same dll too.
Also, when I try, for example, the following situation it works (write "NewProperty" of the "OtherEntityDTO to EntityDTO"), and I don't understand the difference:
[ServiceContract]
public partial interface IServiceDTO : IGenericServiceDTO<EntityDTO>
{
// Some OperationContracts working like
[OperationContract]
EntityDTO[] Method(int field);
// NewMethod
[OperationContract]
OtherEntityDTO[] NewMethod(int field);
}
[DataContract]
public class EntityDTO {
// Some properties working
[DataMember]
YetAnotherEntity NewProperty {get;set;}
}
[DataContract]
public class OtherEntityDTO {
// Some properties working
}
Please, help me and thanks a lot in advance.
Sorry, but after I've posted the question I've found the problem and it was a reported bug (http://blogs.msdn.com/b/distributedservices/archive/2010/02/04/wcf-client-issue-with-reuse-types-from-referenced-assemblies.aspx?wa=wsignin1.0). Another developer had added this attribute (IsReference=true) on a parent class and I didn't know. Now I must to workaround this bug, but that's another battle.
Anyway, I don't understand why sometimes work and sometimes not...
Thanks.
I had a similar error, but my issue seemed to be different.
I had a readonly property and I kept getting that error. When I changed it to a normal property and added a set (that did nothing), the contract worked fine.
Related
I have the following classes...
public abstract class Fallible<T> {
}
public class Success<T> : Fallible<T> {
public Success(T value) {
Value = value;
}
public T Value { get; private set; }
}
The background to this can be found in a previous question of mine, but you don't need to read that post as the classes above are all that's needed to see the problem.
If I have a simplified WCF service call like this...
[OperationContract]
public Fallible<Patient> GetPatient(int id) {
return new Success<Patient>(new Patient {ID = 1,FirstName = "Jim",Surname = "Spriggs"});
}
...then when I try to call the service from the WPF app that consumes it (or the WCF test client), I get a CommunicationException exception...
There was an error while trying to serialize parameter :GetPatientResult. The
InnerException message was 'Type 'PhysioDiary.Entities.FallibleClasses.Success`1[[PhysioDiary.Entities.Patient,
PhysioDiary.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
with data contract name > 'SuccessOfPatient0yGilFAm:http://schemas.datacontract.org/2004/07/PhysioDiary.Entities.FallibleClasses'
is not expected. Consider using a DataContractResolver if you are using
DataContractSerializer 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 the serializer.'. Please
see InnerException for more details.
...with an inner SerializationException exception of...
Type 'PhysioDiary.Entities.FallibleClasses.Success`1[[PhysioDiary.Entities.Patient,
PhysioDiary.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'
with data contract name > 'SuccessOfPatient0yGilFAm:http://schemas.datacontract.org/2004/07/PhysioDiary.Entities.FallibleClasses'
is not expected. Consider using a DataContractResolver if you are using
DataContractSerializer 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 the serializer.
I've tried adding [DataContract] to the class and [DataMember] to each property, as well as adding a [KnownType] attribute for all four classes involved, and adding a [ServiceKnownType] for each of them on the service contract, but nothing helps.
I've read countless answers to the same question, but have not found anything that works. My services return other custom classes, and they all get serialised without a problem.
Anyone able to explain what the problem is here? Please let me know if I've not supplied enough information.
Turns out all I needed to do was decorate the service method with [ServiceKnownType] attributes for the base type, and each derived type...
[OperationContract]
[ServiceKnownType(typeof(Fallible<Patient>)]
[ServiceKnownType(typeof(Success<Patient>)]
[ServiceKnownType(typeof(BadIdea<Patient>)]
[ServiceKnownType(typeof(Failure<Patient>)]
public Fallible<Patient> GetPatient(int id) {
return new Success<Patient>(new Patient {ID = 1,FirstName = "Jim",Surname = "Spriggs"});
}
Although it's a pain to have to add four attributes to every call, it works. I'd like to know if there is a way to combine them into one attribute, but at least I have a working service now.
Hope this helps someone.
I have several WCF services that use the same dataContracts to interchange data between them.
Can I have a sort of Metadata Endpoint for centralizing the management of all this schemas?
When creating a new WCF service, how can i make it to load the schemas from the Metadata Endpoint (instead of redefining them) to use inside its operationContracts?
Thanks!
As I said in the comment, I'm not sure I understand your question (at least with regards to wanting a metadata endpoint), but it sounds like you want to have the ability to reuse existing data contracts in multiple different services.
The easiest way to do that is to put the data contracts in a separate assembly (class library - DLL), and then have the services reference that assembly - they don't have to be defined inside the service itself.
Here's a trivial example (code is for illustration only, not intended to actually do anything or be a complete sample):
MyDataContracts assembly
namespace MyDataContracts
{
[DataContract]
public class DataContract1
{
}
[DataContract]
public class DataContract2
{
}
}
MyService1 assembly
using MyDataContracts;
namespace MyService1
{
[ServiceContract]
public interface IMyService1
{
[OperationContract]
DataContract1 GetSomeData(string someParm);
}
}
MyService2 assembly
using MyDataContracts;
namespace MyService2
{
[ServiceContract]
public interface IMyService2
{
[OperationContract]
void SendSomeData(DataContract1 dc);
[OperationContract]
DataContract2 GetSomeOtherData(int parm);
}
}
Of course, each of the WCF service projects will need to include a reference to the MyDataContracts assembly as well.
Now whenever you create a new service, you simply add a reference to the MyDataContracts.dll and you can reuse your data contracts in it.
I need to have 2 families of classes (one on server and one on client side) which are identical in data structure but differs in behavior. Also I suppose that these fmailies will be enough big, thus I don't want to implement intermediate level of DTO and transformations into and from it.
I decided to move in following manner: declare shared assembly with declaration of data and services interfaces like these ones:
public interface ITest
{
string Title { get; set; }
int Value { get; set; }
}
public interface IService
{
ITest GetData();
}
Having these declarations I can implement these interfaces on server side for example basing on Entity Framework (data) and WCF (services). On the client side I can use for example Dependency Properties (data) and WCF (service).
When I started trying to implement this, I met several troubes.
First one was about server side of WCF - it simply do not want to work with interfaces as return parameters. Thanks to StackOverflow this issue was resolved like here
.
Next problem is that XML rendered by server side includes qulified assembly name of serialized on the server class.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetDataResponse xmlns="http://tempuri.org/">
<Test z:Id="1" z:Type="Dist.Server.Model.Test" z:Assembly="Dist.Server, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/Dist.Server.Model" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<Title z:Id="2">Test</Title>
<Value>123</Value>
</Test>
</GetDataResponse>
</s:Body>
</s:Envelope>
Thus during deserialization on client side there was an attempt to load this type. As this type is inaccessible on client side, I had to implement some kind of type mapping. I found that this is quite easy as NetDataContractSerializer used for serialization supports Binder property. Thus I override this property on client side and return correct value (hardcode in meantime, but it's OK for tests).
public class NetBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName) {
var type = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
return type;
}
}
Now I have following picture:
- Server uses NetDataContractSerializer to serialize response. It uses actual value (calss) during serialization instead of type used in declaration of service (interface).
- Client side receives XML and starts deserialization. To resolve type, NetDataContractSerializer calls my Binder that returns correct type.
- NetDataContractSerializer creates instance of correct type and starts loading of its properties.
And here I got a trouble that I don't know how to resolve. Values of properties are not deserialized. It means that instance of class is created correctly (uninitialized instance created through reflection services), but all properties are in their default values (0 or null).
I tried to play with declaration of class on client side: mark it as [Serializable], implement ISerializable, etc., but nohing is helpful. NetDataContractSerializer requires class to be marked as [DataContract] or [Serializable]. First option leaves properties empty, second one causes exceptions like "</Test> is unexpected, expected is bla-bla-bla_Value_Ending_bla-bla-bla".
Does anybody have any suggestions on how to resolve this last step?
I can provide full sources for better understanding, but I don't know ifI can attach them here...
Thanks in advance.
You could have a look at frameworks like AutoMapper that would take care the transformation to and from DTO. This would make your life much easier.
Instead of using an interface why not create a base class containing only the data and inherit it on both sides by sharing the assembly containing this class. Playing around with the ServiceKnownType should help you fix the last issues.
You may also share the same base classes on both sides and implement the specific logic as extension methods.
Seems that problem was solved enough easily. I created own serializer and used it instead of NetDataContractSerializer. Code is quite simple:
public class MySerializer: XmlObjectSerializer
{
public override void WriteStartObject(XmlDictionaryWriter writer, object graph) {
}
public override void WriteObjectContent(XmlDictionaryWriter writer, object graph) {
var formatter = new XmlSerializer(graph.GetType());
formatter.Serialize(writer, graph);
}
public override void WriteEndObject(XmlDictionaryWriter writer) {
}
public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName) {
var realType = Type.GetType("Client.Test, Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"); //temporary solution
var formatter = new XmlSerializer(realType);
return formatter.Deserialize(reader);
}
public override bool IsStartObject(XmlDictionaryReader reader) {
return true;//temporary solution
}
}
I checked SOAP that goes from server to client and it's almost the same as NetDataSerializer renders. The only difference is in attribute xmlns="".
Kai, Johann thanksfor your tries.
I have a WCF service and I'm sharing types with a client in a shared assembly.
If the client create a derived class will it be possible to pass back the derived type to the service so that I can read the added properties through reflection ?
I tried but having issues with KnownTypes since the service don't know how to deserialize the derived type.
[Serializable]
public abstract class Car : ICar
{........
//on the client :
[Serializable]
public class MyCar : Car
{......
when passing myCar to Service I get the exception complaining about knownType but I cant add this on the server since I wont know what the client will be sending through and I want to handle extra properties through reflection.
Possible to register client types as knowntypes at runtime ?
Is this maybe the solution ?
http://blogs.msdn.com/b/sowmy/archive/2006/03/26/561188.aspx
This is not possible. Both service and client has to know what types will be sent in messages. If you want to use known type you have to define that relation to parent type on the service.
Why do you need to know added properties on the server?
I think there is a way.
I vaguely remember that when I studied WCF, I met ExtensionData which should be a mechanism to get everything that does not match the serialization of the class. for example, if you enable ExtensionData and you are in this situation
//Server
public class GenericRQ
{
public string GenericProperty {get;set;}
}
public Service GenericService
{
Public void GenericMethod(GenericRQ RQ)
{
}
}
// client
Public class MoreSpecificRQ : GenericRQ
{
public string SpecificProperty {get;set;}
}
At
Public void GenericMethod(GenericRQ RQ)
{
// the serializer adds automatically in RQ.ExtensionData everything that has come and that does not match the class GenericRQ.
}
On how to enable ExtensionData you to easily search on the web
I am having problems creating WCF client proxy for service code like in this example:
// data classes
[KnownType(typeof(ClassA))]
[KnownType(typeof(ClassB))]
public abstract class BaseClass : Dictionary<string, ITest>
{
}
public class ClassA : BaseClass
{
}
public class ClassB : BaseClass
{
}
public interface ITest
{
}
// service
[ServiceContract]
public interface IService1
{
[OperationContract]
BaseClass Method();
}
public class Service1 : IService1
{
public BaseClass Method()
{
...
}
}
Whenever I try to create a WCF proxy using "Add Service Reference" in VS it fails and trace log says
Type 'WcfProxyTest.ClassA' cannot be added to list of known types since another type 'WcfProxyTest.ClassB' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfKeyValueOfstringanyType' is already present. If there are different collections of a particular type - for example, List<Test> and Test[], they cannot both be added as known types. Consider specifying only one of these types for addition to the known types list.
I can see what the error message is saying, but is there any other way around this (other than refactoring the classes). I am dealing with a legacy system which has classes written in the same manner as in my example and rewriting them is not an option as this stuff sits in the very core of the system :S
Any ideas? Thanks!
I decided to refactor the code in such a way that I don't have to provide two KnownTypes which gets me around the problem. About 300 syntax errors later that worked. I would be interested in any other ways of doing it though...
Try adding:
[KnownType(typeof(Dictionary<string, ITest>))]