Serializing a List of Interfaces using the DataContractSerializer - c#

I have a class and there are some nested classes within it. I serialize it and send it to the wcf service using a method with no problems. Here's the class;
public class ComputerDTO
{
[DataMember]
public ComputerTypeDTO Type { get; set; }
[DataMember]
public string ComputerName { get; set; }
[DataMember]
public MonitorDTO Monitor { get; set; }
}
Here's the method;
public void Check()
{
Computer c = new Computer();
ISystemInfoOperations cli = GetServiceClient();
Mapper.CreateMap<Monitor, MonitorDTO>();
Mapper.CreateMap<IHardwarePart, IHardwarePartDTO>();
Mapper.CreateMap<Computer, ComputerDTO>()
.ForMember(s => s.Hardware, m => m.MapFrom(q => Mapper.Map<List<IHardwarePart>, List<IHardwarePartDTO>>(q.Hardware)));
Mapper.AssertConfigurationIsValid();
ComputerDTO dto = Mapper.Map<Computer, ComputerDTO>(c);
string sendComputerInfo = cli.SendComputerInfo(dto);
}
But I have also a list of interface to be sent. So I change the code like below and get an error.
public class ComputerDTO
{
[DataMember]
public ComputerTypeDTO Type { get; set; }
[DataMember]
public string ComputerName { get; set; }
[DataMember]
public MonitorDTO Monitor { get; set; }
[DataMember]
public List<IHardwarePartDTO> Hardware { get; set; }
}
public interface IHardwarePartDTO
{
[DataMember]
string Name { get; set; }
[DataMember]
HardwarePartTypeDTO PartType { get; set; }
}
Inside of hardware is getting filled in the project. But if I try to send it, I get this famous error :
Type
'Proxy'
with data contract name
'_x0030__Culture_x003D_neutral_PublicKeyToken_x003D_null_x003E_:http://schemas.datacontract.org/2004/07/Proxy%3CSystemInfo.DTO.IHardwarePartDTO_SystemInfo.DTO_Version=1.0.0'
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.

The DataContractSerializer needs to know about the concrete types that is might return. An interface cannot be serialized, as it cannot be deserialized (how can you create an instance of an interface without a concrete implementation).
The simple resolution is to add KnownTypes attribute like below:
[KnownType(typeof(your hardware dto concrete type here))]
public class ComputerDTO
{
[DataMember]
public ComputerTypeDTO Type { get; set; }
[DataMember]
public string ComputerName { get; set; }
[DataMember]
public MonitorDTO Monitor { get; set; }
[DataMember]
public List<IHardwarePartDTO> Hardware { get; set; }
}
You can add as many known type attributes as you like.
Slightly more complex is the ServiceKnownTypes attribute. This is very similar but you would add it to your service class.
Other than that you can use a data contract resolver - but this is very complicated and would take a while to explain.
EDIT: 18/02/2013 15:11
You may also need to look at you Automapper as its currently going to create proxies in your Hardware list - and proxies cannot be serialized. You need to tell automapper what to serialize them to - for example:
Mapper.CreateMap<Monitor, MonitorDTO>();
Mapper.CreateMap<Monitor, IHardwarePartDTO>().As<MonitorDTO>();
Mapper.CreateMap<Audio, AudioDTO>();
Mapper.CreateMap<Audio, IHardwarePartDTO>().As<AudioDTO>();
Mapper.CreateMap<CDROMDrive, CDROMDriveDTO>();
Mapper.CreateMap<CDROMDrive, IHardwarePartDTO>().As<CDROMDriveDTO>();
//you need entries like these for everythin that implements IHardwarePartDTO
This way automapper knows what it needs to create.

Related

object collision with wcf

Could someone explain why this is happening, I have a C# backend that I'm connecting to via WCF. In the back end, i have two classes in the same namespace that have two properties that have the same name. These classes are used in a separate object. The types of the properties are different, one is a string and one is an object but there seems to be some sort of collision when deserializing the object?
It's returning this random error when I call to return the object.
This could be due to the service endpoint binding not using the HTTP
protocol. This could also be due to an HTTP request context being aborted by
the server (possibly due to the service shutting down). See server logs for
more details.
Here are the classes, the property causing the problem is BCIssued
public class Activities
{
public string ApplicationReceived { get; set; }
public string PIMGranted { get; set; }
public Bcgranted[] BCGranted { get; set; }
public object CCCGranted { get; set; }
// public object BCIssued { get; set; }
public object CCCIssued { get; set; }
}
public class CCC
{
public string BCIssued { get; set; }
public string FinalIns { get; set; }
public string LapsedMonths { get; set; }
public object WorkStarted { get; set; }
public object Notified { get; set; }
public object Lapsed { get; set; }
public object Extension { get; set; }
}
Thanks to Rene's post about WCF logging, i was able to turn on logging and found the error on the server side
Type 'Newtonsoft.Json.Linq.JToken' is a recursive collection data contract
which is not supported. Consider modifying the definition of collection
'Newtonsoft.Json.Linq.JToken' to remove references to itself.

SignalR Client - IHubProxy.On<> and Invoke<> accepting an interface

I am building a windows service that will act as a client to an existing site that's employing SignalR.
I have IHubProxy.On<> and IHubProxy.Invoke methods working when passing around concrete classes.
For example, this works:
hubProxy.On<MigrationRequest>("iSWindowsServiceRequest", request =>
MigrateData.Invoke(request));
And the MigrationRequest looks like this:
public class MigrationRequest : IISWindowsServiceRequest
{
public MigrateWhat What { get; set; }
public MigrationFor For { get; set; }
public Guid EntityFor_Id { get; set; }
}
Now, If i try this:
hubProxy.On<IISWindowsServiceRequest>("iSWindowsServiceRequest", request =>
Handshake.HandleRequest(request));
my request is never picked up.
what I was hoping to achieve was creating single pub-sub methods, rather than one for each concrete class that this service would accept.
Is it possible to pass in an interface into the On<>() methods?
The same goes for the Invoke<>() - if the object I am passing contains any properties that are of an interface, the call never makes it.
so this will not work:
public class ISWindowsServiceResponse
{
public IISWindowsServiceRequest OriginalRequest { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
but this will
public class ISWindowsServiceResponse
{
public MigrationRequest OriginalRequest { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
Where I work, we spent a lot of time trying to figure out a way to get SignalR hubs to serialize interfaces. In the end, we wound up having to extract real objects (real classes and structs, not behind an interface) to send over the wire. There is no way to tell SignalR how to serialize an interface.

AutoMapper does not unproxy NHibernate entity

Consider this entity:
public class CondRule
{
public virtual decimal Id { get; set; }
public virtual string Name { get; set; }
public virtual CondRuleType RuleType { get; set; }
public virtual string Statement { get; set; }
}
and CondRuleType is:
public class CondRuleType
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
It is obvious that there is a one to one relation between CondRule and CondRuleType entities.
Also I have CondRuleDto:
public class CondRuleDto
{
public decimal Id { get; set; }
public string Name { get; set; }
public CondRuleType RuleType { get; set; }
}
I have mapped CondRule to CondRuleDto using AutoMapper:
Mapper.CreateMap<CondRule, CondRuleDto>();
When I call Session.Get to get CondRule by id and the map the result to CondRuleDto, AutoMapper does not resolve proxies (here RuleType).
Here is my code:
var condRule = Session.Get<CondRule>(id);
var condRuleDto = Mapper.Map<CondRuleDto>(condRule);
When I watch condRuleDto, RuleType property is a NHibernate proxy. I want AutoMapper to map RuleType proxy to a POCO. How to make this work?
PS: I have to mention that when I use query and use automapper's Project, it will result a list with no proxies (I know that Project make this happen. May be I need something like Project to use after Session.Get):
Session.Query<CondRule>().Project().To<CondRuleDto>().ToList()
Casts won't change the underlying object (i.e. your CondRuleType will be still a proxy even if you map its instance to another property of type CondRuleType).
It seems like you need to create a custom mapping where CondRule.RuleType is mapped creating a new instance of CondRuleType.

Using Datacontract: WCF

How should i be declaring the datacontracts
My Operation contract has a Method:
Apple GetApples()
My data Contract Apple looks Like
[DataContract]
public class Apple
{
[DataMember]
public int Id { get; set; }
[DataMember]
public FruitType type { get; set; }
}
As there is another member of type FruitType.
[DataContract]
public class FruitType
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string type { get; set; }
}
OR
as a simple class
public class FruitType
{
public int Id { get; set; }
public string type { get; set; }
}
What is the difference between these two? other than that the simple type is not a datacontract and will depende on how i want to use it.?
how should i declare it??
Those attributes give you the control over how your properties will be represented in different formats. For example for XML you can specify the XML Namespace and XML node names.
Even if you are happy with default property names and default namespace, when you try to serialize data to XML, your XML nodes will have weird names such as typek_BackingField.
In other words, if you use WCF you should use DataContract and DataMember attributes, even if you think it works fine the formatted data may not look what you expect. As a result it removes compatibility with other (non-WCF) systems. Or even when you don't share your types (contracts) with other WCF systems.

What is the best way to represent a type-safe property bag in a class?

I have a 3rd party application that provides an object with many "attributes", which are simply pairs of (string) keys and values. The value types can be either strings, DateTime, Int32 or Int64.
I need to create my own class to represent this object, in a convenient way. I'm creating a WCF service that provides this object to clients, so I need it to be very easy and clean.
The keys of the attributes will be presented as an Enum for the clients (to hide the information of the specific key strings of the 3rd party application). However, I'm not sure how to represent the values. Here are some of the options:
Option 1: Have different collection per attribute values, seems ugly but will be very easy for clients to use
public class MyObject
{
public Dictionary<MyTextAttributeKeysEnum, string> TextAttributes { get; set; }
public Dictionary<MyDateAttributeKeysEnum, DateTime> DateAttributes { get; set; }
public Dictionary<MyNumAttributeKeysEnum, long> NumericAttributes { get; set; }
public string Name { get; set; }
public string Id{ get; set; }
Option 2: Convert all of the attributes to strings
public class MyObject
{
public Dictionary<MyAttributeKeysEnum, string> MyAttributes { get; set; }
public string Name { get; set; }
public string Id{ get; set; }
Option 3: Keep them as objects, let the clients bother with casting and converting
public class MyObject
{
public Dictionary<MyAttributeKeysEnum, object> MyAttributes { get; set; }
public string Name { get; set; }
public string Id{ get; set; }
Using several dictionaries just doesn't look nice :) But might work in some scenarios.
If you are absolutely sure that string is enough for all - go with strings. But if some other code would need to parse it - that's going to be expensive.
If you want a really simple straightforward solution - just go with objects. Even though it would introduce boxing/unboxing for value types (forget it if you don't operate thousands of objects) and you'd lose type information on values this solution might still work just fine.
Also you might consider introducing an intermediate class for a value. Something like
public Dictionary<MyAttributeKeysEnum, PropertyBagValue> MyAttributes { get; set; }
public class PropertyBagValue
{
public object AsObject { get; set; }
public string AsString { get; set; }
public int AsInt { get; set; }
// ...
}
Internally you could store your value in a variable of the original type (int in an int variable, string in a string variable, etc., i.e. have a separate variable for each type) and then you can avoid type conversion. Also you could wrap your dictionary in another class, add some usefull accessors and make it look nicer. I don't know how does this fit into your infrastructure though.
How about making you DataContract class abstract and provide dictionaries with types you need in derived classes:
[DataContract]
[KnownType(typeof(My3dPartyObjectString))]
[KnownType(typeof(My3dPartyObjectInt64))]
public abstract class My3dPartyObjectBase
{
// some common properties
}
[DataContract]
public class My3dPartyObjectString : My3dPartyObjectBase
{
public Dictionary<3PAttributeKeysEnum, string> MyStringAttributes { get; set; }
}
[DataContract]
public class My3dPartyObjectInt64 : My3dPartyObjectBase
{
public Dictionary<3PAttributeKeysEnum, long> MyStringAttributes { get; set; }
}
Then client will have to analyse real type of returned object and get collection of attributes based on type. That would be close to your 3d option, but client will at least have some type safety at response-object level.

Categories