I have this ServiceContract
[OperationContract(IsOneWay=true)]
void ProcessMessage(Message message);
and these objects
[DataContract]
public class Message
{
[DataMember]
public long Id { get; set; }
[DataMember]
public string Body { get; set; }
}
[DataContract]
public class ExtendedMessage : Message
{
[DataMember]
public NameValueCollection AdditionalData { get; set; }
}
Will WCF be able to handle if I pass in the subclass to the service method? Or will it drop all extra properties that aren't on the base class?
ExtendedMessage msg = new ExtendedMessage();
...
ProcessMessage(msg);
I think if you didn't specify ExtendedMessage via the KnownType attribute, you would get an error. Once you've told WCF about ExtendedMessage via KnownType, it will work without losing data.
By the way, you don't need to know the set of possible types at compile time because the KnownType attribute can reference a method that will return the set of possible types at runtime.
You could also use the ServiceKnownType attribute to narrow down the scope of the KnownType attribute. You can apply the ServiceKnownType attribute on a specific operation or on your service contract.
Related
I am trying to create a WCF service with some enumerators exposed for clients to set certain properties on the class object. All enumerators associated with different Operations are put in a separate class.
What I understand after reading through some articles is that the Enumerator is accessible on the client side of a WCF service if,
the enumerator is declared in a separate DataContract. For my case, the problem is I am unable to have the client call the Enumerator like ClassName.Enum.Value per how the class has been designed. For some reason, wsdl generated combines the class name and enum name in to a single name like ClassNameEnumName for the enumerator and that needs to be accessed like Namespace.ClassNameEnumName.
a property of the enum type is declared inside the Class containing the Enumerators. The problem with this approach is that, I do not need this property but it is the only way I could have the enums recognized and included in the proxy class by the proxy generator on the client side as expected so that it could used like ClassName.Enum.Value. Another issue is that it exposes those properties to the client since it needs to be decorated with [DataMember]. I wish the client never sees those because the client never needs to use it. I am not sure how could I expose only the enumerator without the backing property showing up such that it could be used like ClassName.Enum.Value.
What I understand is, without an explicit instance of the DataMember, the object declared never gets defined in the proxy class generated. In my case, I am making it happen by creating a property like in the mock-up code below by creating properties but my concern is that this introduces unwanted DataMembers (like EnumErrType, ErrType) being exposed to the client.
So the question is, how can I have an enumerator declared inside a class to be used by the client which can be used like ClassName.Enum.Value without having to expose backing properties or even not creating one in the first place.
[DataContract]
public class ErrorTransaction
{
[DataMember]
public ICollection<Error> Errors { get; set; }
}
[DataContract]
public class Error
{
[DataMember]
public EnumErrorType EnumErrType { get; set; }
[DataMember]
public int id { get; set; }
[DataMember]
public ErrXYZ.EnumErrorType Type { get; set; }
[DataMember]
public DateTime Date { get; set; }
}
public class ErrXYZ
{
[DataMember]
public EnumErrorType ErrType { get; set; }
public enum EnumErrorType : int
{
[EnumMember]
errType1 = 1,
[EnumMember]
errType2 = 2,
[EnumMember]
errType3 = 3
}
}
is the following a known bug in Silverlight?
if so, are there any good workarounds?
the class hierarchy is simple:
on the first PCL:
namespace ClassLibrary1
{
[DataContract]
public class BaseClass
{
[DataMember]
public string BaseString { get; set; }
}
}
on the second PCL (of course, referencing the first...)
namespace ClassLibrary2
{
[DataContract]
public class Derived : BaseClass
{
[DataMember]
public string DerivedString { get; set; }
}
}
the service (on the WebApp):
namespace SilverlightApplication1.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[KnownType(typeof(Derived))]
public class Service1
{
[OperationContract]
public List<BaseClass> GetSomething()
{
var data = new List<BaseClass>();
data.Add(new Derived());
return data;
}
}
}
Now,
the service reference does not add the ServiceKnownType attribute to the reference.cs file. and the resulting error:
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter :GetQueueItemsResult. The InnerException message was 'Element 'http://schemas.datacontract.org/2004/07/XXX_BASE_CLASS' contains data of the 'http://schemas.datacontract.org/2004/07/XXX_DERIVED_CLASS' data contract. The deserializer has no knowledge of any type that maps to this contract. Add the type corresponding to 'DERIVED_CLASS' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
[UPDATED]
the error is thrown on the client, of course. the server side returns the correct values, bu the client cannot parse them properly.
Fiddler says the server returned 0 bytes.
but in reality it's the client who fails to deserialize the data.
I need some way to tell the runtime deserializer how to deserialize the BaseClass into the actual type transferred.
[/UPDATED]
You need to tell the DataContractSerializer to serialize the derived as the base class. To do this you need to clarify the DataContract with a Name attribute on the derived class:
[DataContract(Name="BaseClass")]
public class Derived : BaseClass
[DataMember]
public string DerivedString { get; set; }
}
Im 100% not sure if you need to declare the Derived as a KnownType - I suspect strongly that you DO.
In addition you are using KnownType on the Service class - its my understanding you should use ServiceKnownType here. Generally you have a choice either:
a. Use KnownType on the object classes.
b. Use ServiceKnownType on the Service contract (usually on the Interface delcaration for the Service).
I prefer the later b. because it groups all KnownTypes in one place - where as a. has them scatered all over the code on each object - but that is just personal prefernce.
How about setting the KnownType attributes at the DataContract type definitions, enabling the deserializer to find the correct type during runtime, does it solve the issue?
namespace ClassLibrary1
{
[DataContract]
[KnownType(typeof(BaseClass))]
[KnownType(typeof(Derived))]
public class BaseClass
{
[DataMember]
public string BaseString { get; set; }
}
}
namespace ClassLibrary2
{
[DataContract]
[KnownType(typeof(BaseClass))]
[KnownType(typeof(Derived))]
public class Derived : BaseClass
{
[DataMember]
public string DerivedString { get; set; }
}
}
To be honest, I'm not sure whether you have to set the attributes at both DataContracts or only at the derived type or only at the basetype.
This seems to be a bug in Silverlight/client code generator.
if you have a service returning a collection of "base class" the silverlight client code generator (add service reference) will not be able to add the derived types as ServiceKnownType / KnowType or whatever on the generated client code.
the solution we are currently using (until we abandon SL altogether) is to manually copy-paste the ServiceKnownType declarations into the generated code for all derived type - every time we generate the code.
disgusting! but works.
I have a ServiceStack 3-based client-server architecture. I'm trying to create a service whose request DTO contains a property with an abstract type, with two different concrete classes implementing it. The abstract type could be either an abstract class or an interface; however, in either case, the server receives a null object in the property.
There's three assemblies and corresponding namespaces: TestClient, Server, and CommonLib referenced by both client and server.
That is, spread across the three assemblies:
namespace CommonLib.Services
{
public class GetThing : IReturn<GetThingResponse> // request DTO
{
public IThisOrThat Context { get; set; }
}
public class GetThingResponse
{
public Dictionary<int, string> Result { get; private set; }
public GetThingResponse(Dictionary<int, string> result) // response DTO
{
Result = result;
}
}
}
namespace CommonLib
{
public interface IThisOrThat { }
public class This : IThisOrThat { } // and so forth
}
namespace Server.Services
{
public class GetThing Service : IService
{
public object Get(GetThing request)
{
var foo = request.Context; // this is null
}
}
}
namespace TestClient
{
class Program
{
public const string WSURL = "http://localhost:61435/";
static void Main(string[] args)
{
using (var client = new JsonServiceClient(WSURL))
{
var result = client.Get(new GetThing
{
Context = new CommonLib.This("context info")
});
}
}
If I change the Context property in GetThing to be of type This instead of IThisOrThat, this works. Leaving it as the interface, or changing IThisOrThat to be an abstract class, results in the data being transmitted as null.
I'm assuming this is a serialization problem. I've tried changing the interface to an abstract class and decorating that with appropriate KnownType attributes, but ServiceStack's serializer doesn't appear to benefit from this. Is there any trick to get this done?
You would need to enable JsConfig.IncludeTypeInfo = true; on the client side, so the serializer includes the type information with the request. This will add an extra property (__type) with the type definition so the service knows what to type it as.
It fails currently because requests by default don't provide type information to deserialize the object into the class that implements the interface. This was an issue that was previously raised.
The problem is the when the JSON client makes the request, it will serialize up the a class that implements IThisOrThat such as your This class. But when it gets to the other end ServiceStack.Text doesn't know what to deserialize the object into. The type information is lost so it doesn't know what kind of IThisOrThat it is. So without the additional __type information property in the request this is happening:
Scenario:
interface ISomething
{
string Name;
}
class MySomething : ISomething
{
public string Name { get; set; }
public int Age { get; set; }
}
class MySomethingElse : ISomething
{
public string Name { get; set; }
public int Size { get; set; }
}
Then you make the call from your JsonServiceClient using a typed object
client.Get(new MySomething { Name: "Duck", Age: 20 });
The JSON that is sent would be { "Name":"Duck", "Age":20 } what type does the deserialiser choose now? It could be an MySomething or a MySomethingElse, or even another ISomething that it just doesn't know about yet. So because it can't decide the result is simply null.
Generally interfaces and DTOs don't mix, see here.
I had a similar problem, and realized i didn't have { get; set; } applied to the response DTO, so the result of my object was always null...
Thought this information could also help anyone searching for this ...
I have library with class in it looks like next:
[DataContract]
class MyBaseClass
{
public string Name { get; set; }
}
Also, I have another project with ref of this library and class in it looks like:
[DataContract]
class MyDeliveredClass : MyBaseClass
{
public string SecondName { get; set; }
}
I wonder how could I serialize list<MyBaseClass> which could contain objects of MyBaseClassand MyDeliveredClass ?
May be I should use [KnownType(MyDeliveredClass)]... but I have no reference to MyDeliveredClass in library...
If you declare both MyBaseClass and MyDeliveredClass as Service known types on the service contract interface then that should do the trick:
[ServiceContract(SessionMode = SessionMode.Required, ...)]
[ServiceKnownType(typeof(MyBaseClass ))]
[ServiceKnownType(typeof(MyDeliveredClass ))]
public interface IMySerivceContract {
...
}
Generally you have to choice between delclaring as KnownTypes on the class declaration or as ServerKnownTypes on the service interface. However sometimes the KnownType route is not an option for various reasons - one of which is your situation where you dont have access to a class declaration. Another case where you are forced to use ServiceKnownTypes is if your contract uses interfaces instead of the base class i.e if your contract had List<IMyBaseInterface>.
We have the following classes and WCF service (using protobuf-net for serialization):
[DataContract]
[KnownType(typeof(NamedViewModel))]
public class NamedViewModel<TKey> : IViewModel
{
[DataMember]
public virtual TKey Id { get; set; }
[DataMember]
public virtual string Name { get; set; }
}
[DataContract]
[KnownType(typeof(ScheduleTemplateViewModel))]
public class NamedViewModel : NamedViewModel<int>
{
}
[DataContract]
public class ScheduleTemplateViewModel : NamedViewModel
{
[DataMember]
public string Comment { get; set; }
}
[DataContract]
public class Container
{
[DataMember]
public IEnumerable<ScheduleTemplateViewModel> Templates { get; set; }
}
[ServiceContract]
public interface IService
{
[OperationContract]
Container Get();
}
public class Service : IService
{
public IEnumerable<Container> Get()
{
return new Container { Templates = Enumerable.Range(1, 10)
.Select(i => CreateTemplate()).ToArray() };
}
private void ScheduleTemplateViewModel CreateTemplate()
{
var instance = WindsorContainer.Resolve<ScheduleTemplateViewModel>();
// populate instance
return instance;
}
}
We have two problems:
We get an exception during serialization that the Castle DynamicProxy type for ScheduleTemplateViewModel is unexpected. We noticed that there is custom code in protobuf-net to handle NHibernate and EntityFramework proxies...but not Castle DynamicProxies. We worked around this by adding an additional case statement in the protobuf-net source code to check for Castle's IProxyTargetAccessor type...but it would be nice if there were a way of handling this without modifying the protobuf-net source code...
Members on ScheduleTemplateViewModel (namely Comment) are serialized correctly...but base class Members are not. We already have the InferTagFromNameDefault set to true on RuntimeTypeModel.Default.
I can add that; can you tell me the full name (including namespace) of that interface?
From the example you give, none of those values should serialize, as none of them include the necessary numeric field-number information. Since you say some do serialize, I'm going to assume that this is an omission in the copy/paste. Protobuf-net will try to use the Order=n information from [DataMember(...)] if nothing better is available. However, if must be emphasized that protobuf-net cannot use [KnownType(...)], and inheritance again needs some explicit numeric field-number information. This is most easily added via [ProtoInclude(...)], but can also be provided at runtime