Using Datacontract: WCF - c#

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.

Related

How to fix XML serialization issues in ASMX web service

We have an asmx web service A which is consuming a WCF service B with below DataContract structure.
[DataContract]
public class Animal
{
[DataMember]
string AnimalName { get; set; }
[DataMember]
Head[] head { get; set; }
}
[DataContract]
public class Person
{
[DataMember]
string PersonName { get; set; }
[DataMember]
Head[] head { get; set; }
}
[DataContract]
public class Head
{
[DataMember]
string Title { get; set; }
}
We are receiving below error while running an asmx web service A.
Types 'Animal.Head' and 'Person.Head' both use the XML type name, 'Head', from namespace 'http://tempuri.org/'. Use XML attributes to specify a unique XML name and/or namespace for the type.
We also tried putting a different XMLElement namespace in DataContract but didn't help.
[XmlElement(Namespace = "urn:/A/Animal/")]
[DataMember]
Head[] head { get; set; }
Any help on this is much appreciated.
XmlElement is used for XmlSerializer, wcf by default uses DataContractSerializer to
serialize and deserialize model.
If you want to use XmlSerializer , you could refer to
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-the-xmlserializer-class
DataContract also has a Namespace property which could specify namespace for your model.

Get generic field value from list of generic types

I have a bunch of settings that can be one of many types. For example, URL, text, number, etc. Based on other posts I came up with the following that seems to work okay:
public abstract class SettingViewModel
{
public string Name { get; set; }
public string Description { get; set; }
public SettingType Type { get; set; }
public string DefaultValue { get; set; }
public Module? Module { get; set; }
}
public class SettingViewModel<T> : SettingViewModel
{
[Required]
public T Value { get; set; }
}
My biggest problem with this is that when it comes time to read the values in Value for each setting I want to do have a method like. I wouldn't want to change the shape of this method really, because I want to keep it generic and it will be exposed as a Controller Method in MVC
public void Update(SettingViewModel Setting)
{
GS_SysConfig setting = new GS_SysConfig();
//might also just use a util function to convert to string based on the type of Value
setting.ConfigVal = Setting.Value.ToString(; //Value is not a field in the abstract class
db.SaveChanges();
}
This won't work because Value is not a field in the parent class, and I suppose I could come up with some way of doing casting and and getting types and such, but it seems messy and not very elegant. Is there a better approach to tryign to accomplish what I'm trying to do?
Update:
ConfigVal is of type String

DataContractSerializer using default properties

Recently when I read the default behavior of DataContractSerializer, I get the rules from MSDN, however I do not understand the first rule which I extracted as below:
The DataContractSerializer infers a data contract from types without attributes using the default properties of the newly created types.
How do I interpret this statement, if some one has clear idea, could you help, I know that "without attributes", the attribute means DataContract attribute, however what does that "default properties" refer to. Is there something called "default properties" in a custom type?
If you a have type referenced within another class that has [DataContract] attribute, then DataContractSerializer will serialize the referenced type even if it is not attributed with [DataContract]. Serialization will happen on all public properties, unless the property is attributed with [IgnoreDataMember].
For example:
[DataContract]
public class ClassA
{
public ClassB MyData { get; set; }
public string SomeString { get; set; }
public int SomeNumber { get; set; }
}
public class ClassB
{
public string SomeOtherInfo { get; set; }
public int SomeOtherNumber { get; set; }
}
In the above code, ClassB will be serialized based on its default properties, which in this case are all the public properties: "SomeOtherInfo" and "SomeOtherNumber".

Serializing a List of Interfaces using the DataContractSerializer

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.

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