WCF: generic list serialized to array - c#

So I am working with WCF and my services return types that contain generic lists. WCF is currently converting these to arrays over the wire. Is there a way I configure WCF to convert them back to lists afterwards? I know there is a way by clicking advanced when you add a service reference but I am looking for a solution in configuration files or something similar.
[DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public ICollection<AnotherObject> Objects { get; set; }
}
It is very odd, also, because one service returns it as a list and the other as an array and I am pretty sure they are configured identically.

At the advanced tab when adding your service reference you can set this option as well. standard Arrays are set.

I think this is dent with purely from the way that the client tool generates the contracts from the WSDL. In my case, I made a reusable .dll that contains my [OperationContract] and [DataContract] classes, and use it from both the client and the server, instead of generating one with SvcUtil. This way I preserve my lists of generics.

In addition, take care not to have both arrays and generics in the classes from which you serialize the instances with WCF, because you'll get a problem during deserialization : everything will be converted either to ArrayOf (if you don't change the configuration) or to Collection Type.
As result you will get errors during deserialization from the WCF code trying to assign an array where you wait a Collection and conversely.
This was just my 2cent advice from what I learned during a small project with WCF. :)

I found a solution that was much simpler and worked well enough for me, although it might not work for others. I simply switched from using ICollection (IList also produces this result) to List. It worked fine after that.
Solution from Here.
I also found a possible configuration solution from Here near the bottom.
<CollectionMappings>
<CollectionMapping TypeName="ChangeTracker.ChangeTrackingCollection'1" Category="List" />
</CollectionMappings>

Instead of use ICollection<AnotherObject> in your data contract, that will be generated in client application as a AnotherObject[].
Try this:
define a new data contract
[CollectionDataContract]
public class AnotherObjectCollection : List<AnotherObject> {}
in your code:
DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public AnotherObjectCollection Objects { get; set; }
}
in Visual Studio (same to svcUtil), the client proxy code will appear like this:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.CollectionDataContractAttribute(Name="AnotherObjectCollection", Namespace="http://schemas.datacontract.org/2004/07/SampleObject", ItemName="AnotherObject")]
[System.SerializableAttribute()]
public class AnotherObjectCollection : System.Collections.Generic.List<AnotherObject> {}
DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public AnotherObjectCollection Objects { get; set; }
}
This also works for built-in .NET types.
antonio

Related

How to serialize circular referenced ef poco class using protobuf-net

I'm using marc gravell's protobuf-net and ef core in my project.
long story short, I'm using Inverseproperty attribute on my POCO class which causes a circular reference when I fetch results from database which causes me trauble when I try to serialize data using protobuf net.
I'm currenyl serializing data with Jsonconvert by setting ReferenceLoopHandling = ReferenceLoopHandling.Ignore and returning a json string to the client to keep the app in a working state but do not want to use this method as it doesnot make any sense.
I would like to know if it is possible to either prevent EF core generating circular reference when using Inverseproperty attribute or if protobuf-net has an ignore referenceloop handling feature when serializing data..
a simplified version of my poco class is like this:
[ProtoContract]
[Table("CATEGORIES_M")]
public class CATEGORIES_M
{
public CATEGORIES_M()
{
CATEGORIES_M_COLLECTION = new HashSet<CATEGORIES_M>();
//Product = new HashSet<Product>();
CM_DATE = DateTime.Now;
}
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[ProtoMember(1)]
public int CM_ROWID { get; set; }
[ProtoMember(2)]
public string CM_NAME { get; set; }
[ProtoMember(3)]
public int? CM_PARENT_REFNO { get; set; }
[ProtoMember(4)]
[ForeignKey(nameof(CM_PARENT_REFNO))]
[InverseProperty(nameof(CATEGORIES_M_COLLECTION))]
public CATEGORIES_M CATEGORIES_M_PARENT { get; set; }
[ProtoMember(5)]
[InverseProperty(nameof(CATEGORIES_M_PARENT))]
public ICollection<CATEGORIES_M> CATEGORIES_M_COLLECTION { get; set; }
}
any help is appreciated
Protobuf-net does not have good support for this scenario. V2 has some limited reference tracking capabilities, but these are deprecated in V3 because it caused more problems than it solved. My suggestions, as the library author:
serialize a simple tree model, and build your real model afterwards from it, or
use a different tool
Backreferences (parent level) can be tagged with [ProtoIgnore] to avoid circular references. That might change the behavior as a client might expect values there. Though, usually the client has the parent objects already and you might just need a key here. If that's the case, add an additional serializable property for the key value and mark as [ProtoMember(nn)], then.
Hint: Write test cases and use the Serialize class's static methods to check the behavior and get useful exceptions before trying to debug your server code.

FluentAssertions - Comparing objects containing properties with similar names but different types

I have several classes in our code that are automatically generated by the XSD generator tool from XSD file definitions. The classes look very similar with similar names, but based on the XSD schema (which we've received from an external vendor), the classes that are generated are all of different types. And these are pretty complex classes, with lots of deep nested properties and enum values. Hence, we used to work with the classes directly, because it was hard to do a generalized approach to work with the classes.
But I undertook the challenge, and I've (kind of) succeeded. To avoid code duplication when working with these classes, I have added properties to the classes using interface definitions outside the XSD generated file, so as to prevent them from being overwritten when generating the classes again, taking advantage of the partial class declaration like so:
Simplified example of XSD generated classes
public partial class xsdGeneratedClass1
{
public xsdGeneratedClass1Header header { get; set }
public xsdGeneratedClass1Body body { get; set; }
}
public partial class xsdGeneratedClass2
{
public xsdGeneratedClass2Header header { get; set }
public xsdGeneratedClass2Body body { get; set; }
}
Simplified example of an interface, where the properties also consists of type interfaces, which we've written to match the properties in the XSD generated classes
public interface IXsdGeneratedClass
{
IXsdGeneratedClassHeader header { get; set; }
IXsdGeneratedClassBody body { get; set; }
}
Simplified example of us implementing the interface, outside of the XSD generated file
public partial class xsdGeneratedClass1 : IXsdGeneratedClass
{
public IXsdGeneratedClassHeader header { get; set; }
public IXsdGeneratedClassBody body { get; set; }
}
public partial class xsdGeneratedClass2 : IXsdGeneratedClass
{
public IXsdGeneratedClassHeader header { get; set; }
public IXsdGeneratedClassBody body { get; set; }
}
In this simplified example, this construct allows me to work with the header and body properties using interfaces instead of the concrete implementations, for the dozens of classes that we have with the same structure, but with different class types, without editing in the autogenerated code of the XSD tool. This all works fine and dandy.
The problem comes when trying to compare the objects in our unit tests, using Fluent Assertions. It seems that Fluent Assertions has trouble knowing which properties of the instantiated objects to compare. In this simple example, an instantiated object of xsdGeneratedClass1 will have four properties:
public xsdGeneratedClass1Header header { get; set }
public xsdGeneratedClass1Body body { get; set; }
public IXsdGeneratedClassHeader header { get; set; }
public IXsdGeneratedClassBody body { get; set; }
The objects that I want to compare are the header and body properties with the interface types, since these will be the only ones that have actual data in them. The concrete class properties are always all null. So I've made tests like these:
class1.Should().BeEquivalentTo(expectedClass);
But it seems like Fluent Assertions constantly compares class1's IXsdGeneratedClassHeader header with the expectedClass' xsdGeneratedClass1Header header, which is null.
I have tried using the RespectingRuntimeTypes option, which makes the test pass, but then it seems like it doesn't compare the objects as it should. If I change a property value in the expectedClass' header property for example that I know won't match the one in class1, the test still passes.
I've tried scouring the interwebs for answers, and I've come to an end in my search, and basically am pondering on whether I should just write my own tool or make a gazillion manual assertions. Obi-Wan Assertions, please help!
Thank you very much Jonas Nyrup! Seems to be a defect that is now amended in the master branch of Fluent Assertions. In the meantime, someone posted an answer in the github thread for a work-around, that also worked for me. Very happy. Thanks!
Declare this class somewhere, available for use in your unit tests
public class ReflectionMemberMatchingRule : IMemberMatchingRule
{
public SelectedMemberInfo Match(SelectedMemberInfo expectedMember, object subject, string memberPath, IEquivalencyAssertionOptions config) => expectedMember;
}
Use the extension class in your unit tests, by adding the configuration to Fluent Assertions.
AssertionOptions.AssertEquivalencyUsing(x => x.Using(new ReflectionMemberMatchingRule()));
It now works as expected!

Conditional JSON Deserialization using C#

I'm writing a C# test automation to validate web services that return JSON strings. I created a DataContract that mapped to what was being returned. Assume this is what was being returned:
{"DataModule" : {"required":"false", "location":"toolbar"}}
My test automation was working fine, but then I started getting this error:
"The data contract type 'DataModule' cannot be deserialized because
the required data members 'required, location' were not found."
I checked the JSON and the data module was now
{"DataModule" : {"width":"400", "height":"320"}}
I discovered that the dev implementation is that if the first type of data module is encountered, the client parses that and creates a button on a toolbar. If the second type of data module is returned, the button appears on the toolbar AND a panel appears in another location with those measurements.
Is there a way to either create optional members in a data contract OR implement conditional deserialization to account for JSON objects that may have multiple implementations?
If you declare the model with all of the likely properties, only the ones found in the JSON string will be populated:
public class DataModule
{
public bool Required { get; set; }
public string Location { get; set; }
public string Width { get; set; }
public string Height { get; set; }
}
#dave-s
I had already tried adding all of the properties, but since I knew I was on the right track, your suggestion keyed me into something else. The properties were all decorated with
[System.Runtime.Serialization.DataMemberAttribute(IsRequired = false)]
But the class itself, was decorated with only [Serializable]. When I changed [Serializable] to
[System.Runtime.Serialization.DataContractAttribute()]
it started working.

Serializer ignores properties that use another serialized property

I am working on an endpoint in asp.net that serializes and returns some data, using the default serializer.
The consuming applications are transitioning between changing names for properties (in other words, some existing applications are using names like ...Vat; while newer ones are using ...Tax. I therefore need to keep both names in the response for the moment, until these changes are complete.
The return type is IList.
public class Product
{
...
public decimal PriceIncVat { get; set; }
public decimal PriceIncTax { get { return PriceIncVat; } }
public int TaxCode { get; set; }
...
}
However, when I examine the response in fiddler, only the PriceIncVat property exists in the json list of products.
I can't think of any reason why the above wouldn't work. I added TaxCode at the same time as PriceIncTax, and it is returned, so I know the code of the endpoint is up to date.
And on the client side of a newer client project we have:
public class ProductDto
{
...
public decimal PriceIncTax { get; set; }
public string TaxCode { get; set; }
...
}
Very confused here.
The serializer assumes you will need to deserialize the data some time. Hence by default only properties with a getter and a setter are considered.
When using the DataContractJsonSerializer, it's possible to turn on serialization of read-only properties using the SerializeReadOnlyTypes property (despite its rather misleading name).
Side note: Check-out the Json.NET serializer, which gives more options and better control over the (de)serialization process.

InvalidDataContractException on WP7 application on class that is serializable

I'm having an strange error when trying to save an object into isolated storage. I have a class that has some properties, here's the code :
[DataContract]
public class ExerciseStatistic
{
[XmlIgnore]
public int CorrectAnswers
{
get
{
return Attempts.Where(a => a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int IncorrectAnswers
{
get
{
return Attempts.Where(a => !a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int AnswerAttempts
{
get { return Attempts.Count; }
}
public List<AnswerAttempt> Attempts { get; set; }
public ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>();
}
}
public class AnswerAttempt
{
public DateTime AttemptDate { get; set; }
public string AttemptTargetName { get; set; }
public string AttemptName { get; set; }
public bool AttemptAnswerIsCorrect { get; set; }
}
However, when trying to save it with this sentence :
IsolatedStorageSettings.ApplicationSettings["a"] = new ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>()
{
new AnswerAttempt()
{
AttemptAnswerIsCorrect = true,
AttemptDate = DateTime.Now,
AttemptName = "lala",
AttemptTargetName = "lala2"
},
new AnswerAttempt()
{
AttemptAnswerIsCorrect = false,
AttemptDate = DateTime.Now,
AttemptName = "lalab",
AttemptTargetName = "lalab2"
}
}
};
I'm getting an exception like this one (i changed a bit the signature of the code with fake names, but for the example it serves its purpose) :
Type 'XX.Model.FirstClass.SecondClass' cannot be serialized. Consider
marking it with the DataContractAttribute attribute, and marking all
of its members you want serialized with the DataMemberAttribute
attribute.
I don't understand why the serializer is trying to serialize an object of my model (which is not serializable) when the class that I'm giving it doesn't have any references to that kind of type... what am i missing? -> nope, i don't want to add datacontract attributes to classes that i don't need and am not planning to serialize, so please don't answer with this :)
You might experience this problem if you work through the reference procedure in "Walkthrough: Consuming OData with MVVM for Windows Phone" at http://msdn.microsoft.com/en-us/library/hh394007(v=VS.92).aspx
When you get to the point where you call :
Return DataServiceState.Serialize(_context, collections);
You might get an InvalidDataContractException with the message:
Type 'DataBoundApp1.Northwind.NorthwindEntities' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Thanks to the answer by Daniel Perez, I was able to resolve this problem and I am documenting my steps to clarify the solution for others:
Show hidden files in Solution Explorer
Open the file "Reference.cs" (under your Service Reference, expand
Reference.datasvcmap)
If your Data Service Context class is missing the [DataContract]
attribute, add it as shown here:
.
namespace OCC.WindowsPhone.OrlandoCodeCampService
{
[DataContract] <--- I ADDED THIS
public partial class OrlandoCodeCampEntities : global::System.Data.Services.Client.DataServiceContext
{..}
Once I added the DataContract attribute, the problem went away!
It seems to me you try to exclude properties from serialization by using XmlIgnore.
From the documentation:
You can opt out members from serialization by using the IgnoreDataMemberAttribute.
so try using IgnoreDataMemberAttribute instead of XmlIgnore to opt out members from serialization.
I also had some troubles with DataContract in the very same situation as you, therefore I reverted to plain old XML serialization to strings, which i then stored in isolated storage. This also eases debugging.
I don't think that this is a proper answer, but it's what i had to do in order to fix it.
After changing some more the code, i realised that this was failing EVEN if I wasn't saving anything to the isolated storage. Just declaring a DataContract attribute on the type made the error arise. I must think that WP7's framework at some point is parsing all classes that have this attribute, and for some strange and obscure reason (which i can't find) it's looking for them in other classes as well. I added the DataContract attributes in the classes that the framework is complaning about, and also some KnownType attributes as well, and everything started to run smoothly... weird weird... if someone can shed some light into this, i'd be happy (i hate it when i solve a problem but without knowing the exact cause)

Categories

Resources