Serialize delivered class's instance using DataContract - c#

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>.

Related

In C#, is it possible to enforce applying Attributes onto Classes implementing a specified Interface?

Let's say we have a custom attribute, interface and a couple of classes defined in C#, like so:
[AttributeUsage(AttributeTargets.Class)]
class TestAttribute : Attribute {}
interface ITestInterface {
void PrintInternals();
}
[Test]
class TestClass : ITestInterface {
private string Internals { get; set; }
public TestClass(string initialString) {
Internals = initialString;
}
public void PrintInternals() {
Console.WriteLine(Internals);
}
}
Is it possible to enforce that TestAttribute can be applied only to classes implementing ITestInterface? (The intent of this is that the attribute can provide extra info needed for automated class registration, for example to map enum values onto types.)

Silverlight WCF: consuming a collection of derived objects as a collection of base class results in a NetDispatcherFaultException

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.

Protobuf-net not serializing base class members

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

subclassing and data contracts

I'm playing with the following code:
[ServiceContract]
public interface IUserAccountService
{
[OperationContract]
UserAccountResponse CreateNewUserAccount(UserAccountRequest userAccountRequest);
}
public abstract class BaseResponse
{
public bool Success { get; set; }
public string Message { get; set; }
}
public class UserAccountResponse : BaseResponse
{
public int NewUserId { get; set; }
}
My questions are:
Do I need to add the DataContract attribute to both the abstract class and the subclass?
If the abstract class does not need the DataContract attribute, can I add the DataMember attribure to its properties?
If you want items in the base class to be serialized, then you must apply DataContract to the base class and apply DataMember to those items that are to be serialized in the base class. However, if you do not want anything in the base class to be serialized, then you shouldn't need to specify DataContract on the base class.
From the MSDN documentation:
"When you apply the DataContractAttribute to a base class, the derived types do not inherit the attribute or the behavior. However, if a derived type has a data contract, the data members of the base class are serialized. However, you must apply the DataMemberAttribute to new members in a derived class to make them serializable."
Yes you will have to use [DataContract] on both BaseResponse and UserAccountResponse. You will also have to use the [KnownType] attribute for every subclass as shown below.
[KnownType(typeof(UserAccountResponse))]
[DataContract]
public abstract class BaseResponse
{
...
}

Honouring of AttributeUsage on derived attribute types

Given the following, I would not expect the compiler to allow multiple attributes that are derived from the base attribute, given that is set to AllowMultiple=false. In fact it compiles without a problem - what am I missing here?
using System;
[AttributeUsage(AttributeTargets.Property,AllowMultiple=false,Inherited=true)]
abstract class BaseAttribute : Attribute { }
sealed class DerivedAttributeA : BaseAttribute { }
sealed class DerivedAttributeB : BaseAttribute { }
class Sample1
{
[DerivedAttributeA()]
[DerivedAttributeB()]
public string PropertyA{ get; set; } // allowed, concrete classes differ
[DerivedAttributeA()]
[DerivedAttributeA()]
public string PropertyB { get; set; } // not allowed, concrete classes the same, honours AllowMultiple=false on BaseAttribute
}
The problem is simply that the AllowMultiple check only compares attributes of the same actual type (i.e. the concrete type instantiated) - and is perhaps best used with sealed attributes for this reason.
It will, for example, enforce the following (as an illegal duplicate), inheriting this from BaseAttribute:
[DerivedAttributeB()]
[DerivedAttributeB()]
public string Name { get; set; }
In short, I don't think you can do what you want here... (enforce no more than one instance including subclasses of BaseAttribute per property).
A similar example of this problem would be:
[Description("abc")]
[I18NDescriptionAttribute("abc")]
public string Name { get; set; }
class I18NDescriptionAttribute : DescriptionAttribute {
public I18NDescriptionAttribute(string resxKey) : base(resxKey) { }
}
The intent above is to provide a [Description] from resx at runtime (fully supported by ComponentModel etc) - but it can't stop you also adding a [Description].

Categories

Resources