IQueryable<> Serialization - C#-VS2008-WCF - c#

I built my *.dbml file with the required tables and that generated the relationships, 1 to many.
One of my methods in my WCF Service library has this query
IQueryable<Client>localClient = from c in db.Clients
where c.ClientID.Equals(2612)
select c;
foreach(Client currentClient in localClient)
{
//Call serialize method here
}
One of the table which client has a 1 to many relationships is Client - Employee
With 'localClient', I want to serialize that and return to the invoker. However it tells me that the the XML document could not be formed.
This is the InnerException:
A circular reference was detected while serializing an object of type TestDB_Public.Employee.
My serialization code
public string Serialize(object o, XmlSerializerNamespaces ns)
{
try
{
System.IO.MemoryStream m = new System.IO.MemoryStream();
if (ns != null)
serializer.Serialize(m, o, ns);
else
serializer.Serialize(m, o);
m.Position = 0;
byte[] b = new byte[m.Length];
m.Read(b, 0, b.Length);
return System.Text.UTF8Encoding.UTF8.GetString(b);
}
catch (Exception ex)
{
return "Ex = " + ex.ToString();
}
}
Is serialization of IQueryable<> with 1 to many relationships not possible?

What exactly is the error message? Note that you have to serialize something concrete like a list or array of objects (not a query).
If you want queryable over the wire, look at ADO.NET Data Services, which does this.
Also - have you set the serialization mode to "unidirectional" in the dbml designer? As long as there are no loops, it should work fine.

You can't serialise an object graph that has cyclical relationships:
class Employee
{
Employee Manager;
List<Employee> Employees;
}
var bossMan = new Employee();
var emp2 = new Employee{Manager = bossMan}
var bossMan.Employees.Add(emp2);
If you now try to serialise bossman or emp2, you will get the exception.
Have a look at this post, check the Cyclic Object Graphs for a solution.

Marc:
For some reason it is not allowing me to add a comment;
I added this
[Table(Name="dbo.Client")]
[DataContract(IsReference=true)]
public partial class Client: INotifyPropertyChanging, INotifyPropertyChanged
{
..//
private EntitySet<ClEmp> _ClEmp;
[Association(N...)]
[DataMember(Order=70, EmitDefaultValue=false)]
public EntitySet<ClEmp> ClEmps
}
My serialization is this:
DataContractSerializer ser =
new DataContractSerializer(typeof(Client));
var ms = new System.IO.MemoryStream();
ser.WriteObject(ms, r);
ms.Seek(0, System.IO.SeekOrigin.Begin);
var sr = new System.IO.StreamReader(ms);
var xml = sr.ReadToEnd();
when i look at var xml, i do not get my enity set ClEmp.

Related

XML deserialization of different classes in file

I get a file with different content (currently 4 different classes), either
<ClassA><!-- content --></ClassA>
or
<ClassB><!-- content --></ClassB>
or ...
At time of parsing I have no further information which class is in the file.
So, currently, I try to parse by trial and error:
try
{
ClassA result = (ClassA)new XmlSerializer(typeof(ClassA)).Deserialize(reader);
if(!(result is null)) { \\do something }
}
catch (Exception) {}
And the same for ClassB and so on ...
Is there a more elegant way to parse the classes?
I can give all classes the same base class, although the are quite different in their form.
I solved this issue with XmlSerializer.CanDeserialize(XmlReader)
using (var reader = XmlReader.Create(stream))
{
foreach (var type in types)
{
var serializer = new XmlSerializer(type);
if (serializer.CanDeserialize(reader))
{
return serializer.Deserialize(reader);
}
}
throw new XmlException("Invalid xml type");
}

XML deserialize brings back empty list

I have a dictionary of abilityobjects <id, abilityObj> that I'm trying to serialize in XML. Because you can't XML Serialize a dictionary, I change it into a list on serialization
public class BattleSerializable : TyrantSerializable
{
[XmlIgnoreAttribute]
[NonSerialized]
[DoNotSerialize]
public Dictionary<int, AbilityObjectSerializable> battleObjects;
public List<AbilityObjectSerializable> serializedBattleObjects
{
get
{
if (battleObjects == null)
{
battleObjects = new Dictionary<int, AbilityObjectSerializable>();
}
return battleObjects.Select(x => x.Value).ToList();
}
set
{
battleObjects = value.ToDictionary(x => x.entityId, x => x);
}
}
It serializes correctly. I.e. the XML that gets saved makes sense
<BattleSerializable>
...
<serializedBattleObjects>
<AbilityObjectSerializable xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance" d3p1:type="FireballObject">
<hexDirection>southeast</hexDirection>
<gridX>0</gridX>
<gridZ>7</gridZ>
<entityId>3</entityId>
<lastAnimation>STATUE</lastAnimation>
<timer>0</timer>
<abilityPos>2</abilityPos>
<abilityType>FIREBALL</abilityType>
<health>100</health>
<tilesPerTurn>2</tilesPerTurn>
<jump>1</jump>
<fall>99</fall>
<damage>5</damage>
<lineTraversed>
<xDisplace>1</xDisplace>
<zDisplace>-2</zDisplace>
<endTileFacing>east</endTileFacing>
</lineTraversed>
<moveDirection>
<xDisplace>1</xDisplace>
<zDisplace>-2</zDisplace>
<endTileFacing>east</endTileFacing>
</moveDirection>
</AbilityObjectSerializable>
</serializedBattleObjects>
</BattleSerializable>
But when I try to then -load- this XML and turn it into actual C# objects, this list comes in empty for some reason, causing the app to blow up.
What am I missing? All the other lists in this class serialize/deserialize correctly.
My load code:
public BattleSerializable Load(string path)
{
var serializer = new XmlSerializer(typeof(BattleSerializable));
try
{
using (var stream = new FileStream(path, FileMode.Open))
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(stream);
string xmlString = xmlDoc.InnerXml;
BattleSerializable bs = (BattleSerializable)this.LoadFromXML(xmlString);
return bs;
}
}
catch (Exception e)
{
throw new SettingLoadException("Settings failed validation");
}
}
The way a lot of serializers work is by calling Add on a list, only actually assigning anything back to the setter if the serializer created the list (perhaps because it was null, or fixed size such as an array). So imagine the serializer doing:
var list = obj.SomeProperty;
while (moreOfTheSame)
list.Add(ReadOneOfThose());
It never calls the setter, so any logic in there: irrelevant. You'll probably need a custom list type here, or perhaps more simply: have a nice simple POCO/DTO model that just maps to the serialization shape with no fun logic, and project between this model and your domain model separately to serialization.

Reading CollectionJson content in Web Api Project

I have a project similar(Almost identical) to Conference API project which is taking similar approach to the noted project for returning CollectionJson content. I am having difficulty Setting the Collection property of the ReadDocument (Line 30) as it does not have any setter. I could bypass this problem by doing the following change
public CollectionJsonContent(Collection collection)
{
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Newtonsoft.Json.Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
collection.Version = "1.0";
Headers.ContentType = new MediaTypeHeaderValue("application/vnd.collection+json");
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
//var readDocument = new ReadDocument(); {IReadDocument.Collection = collection};
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer,collection);
writer.Flush();
}
_memoryStream.Position = 0;
}
Although above code compiles and to some extent sorts out the problem but then again I will have another problem of not being able to consume the JsonCollection content in my controller unit tests. Consider the following unit test code snippet:
using (var request = CreateRequest())
{
var controller = new TestController(DataService) {Request = request};
var temp = await controller.ListAsync(gridSearchData, sampleSearchData);
if ((temp is NotFoundResult) && (sampleCollection.Any()))
{
Assert.Fail("Controller did not return any result but query did");
}
var json = await temp.ExecuteAsync(cancellationTokenSource);
var readDocument = json.Content.ReadAsAsync<ReadDocument>(new[] {new CollectionJsonFormatter()}, cancellationTokenSource).Result;
}
Since I did not set the collection property of ReadDocument readDocument is always empty and I cant read its content.
How do you asynchronously read the contents of JsonCollection on the client side in WEB API projects?
To get a Clear picture of the approach look at the Conference Web Api
and the authors blog
OK all, this has been fixed. The Collection property is now settable again.
I have just pushed release 0.7.0 with this fix, a major naming refactoring as well as a nice improvement to serialization to not write out empty collections.
Please see the release notes for the changes (especially the naming as the package names and namespaces have changed)
As far as I see from your code, you do not serialize a ReadDocument object, but only a property of it (Collection), and then you try to deserialize that value into a new ReadDocument object.
A sample ReadDocument should serialize like this
"{"Collection": [1,2,3,4,5] }"
But you serialize collection, so you get
"[1,2,3,4,5]"
I recommend a surrogate class for serialization like this
class SerializableReadDocument
{
public Collection Collection { get; set; }
}
and update your serialization code like this
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
var readDocument = new SerializableReadDocument() { Collection = collection };
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, readDocument);
writer.Flush();
}
But, this will not resolve your problem when you try to deserialize your output since ReadDocument does not have a settable Collection property, deserialization will either fail, or return a ReadDocument object with an empty Collection.
You can use SerializableReadDocument if you like in your unit tests.
I am looking into this and will come up with a solution hopeful this weekend, which will either be to make it a public setter, or make the setter internal and have public ctor that accepts a collection.
Sorry for the difficulty.

List of Objects Serialization xml fault

I am making a WPF application in which I save list of object on exit of my WPF Application. And get the list of objects on system startup. Everything works fine initially. But Some times it gives the serialization Exception. After getting the exception I looked of the xml serialized file. But it seem to me that the exception was thrown because the xml file was not formed properly. When I corrected it. It again worked fine.
public static class IsolatedStorageCacheManager<T>
{
public static void store(T loc)
{
IsolatedStorageFile appStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
using(IsolatedStorageFileStream fileStream=appStore.OpenFile("myFile21.xml",FileMode.OpenOrCreate))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(fileStream, loc);
}
}
public static T retrieve()
{
T obj = default(T);
IsolatedStorageFile appStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);
if (appStore.FileExists("myFile21.xml"))
{
using (IsolatedStorageFileStream fileStream = appStore.OpenFile("myFile21.xml", FileMode.Open))
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
try
{
obj = (T)serializer.ReadObject(fileStream);
}
catch (SerializationException e)
{
Console.WriteLine(e.StackTrace);
}
}
}
return obj;
}
}
The first thing to do is make sure that the objects passed to store are of a type supported by the DataContractSerializer.
The easiest thing to do is check all store calls yourself.
You could also create a validation method or even better, see if anyone else has implemented one. This method could validate the loc object and return a boolean and be called at the beginning of the store method inside a System.Diagnostics.Debug.Assert call so that it will only run on a debug configuration. Note though that this method could be quite tricky because you will have to validate type T for all the cases mentioned in the specification of DataContractSerializer and if T is a generic validate T's parameter(s) as well.

Serialize .net linq object to json, exception on datacontext

I'd like to serialize my linq object to json.
The linq object is disconnected, meaning the datacontext was disposed long time ago.
There are "related" objects which were not loaded during the object load process which can't be accessed (When accessed, runtime error return "Cannot access a disposed object." because the datacontext is gone)
Is there any json/xml converter with the ability to serialize this object?
I don't want to chanage the dbml
Is there any serialization object with the ability to configure ignore exception properties or so?
To reproduce that issue, create this object:
public class HelpMeToSerialize
{
public string Name;
public int Age
{
get
{
throw new Exception("Can't access this on runtime");
}
set
{
}
}
}
And simply serialize it with this code or any other code you have:
HelpMeToSerialize obj = new HelpMeToSerialize();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(obj.GetType());
x.Serialize(Console.Out, obj);
If your Linq generated object has a relationship to another table, which you did not load at the time of creating the object (via Linq2Sql for example), then you can do the following to serialize it:
//assume that your linq created object with class type StronglyTypedLinqObject has a field, ID, and a relationship called RelatedThing
StronglyTypedLinqObject row_from_db = null;
using(var myDatabase = new MyLinqContext(ConnectionString))
{
myDatabase.DeferredLoadingEnabled = false;
//assume this pulls one item back which has a relationship
row_from_db = (from o in myDatabase.TheTableToSelectFrom select o).Single();
row_from_db.RelatedThing = null; //this line may be unnecessary
} // context is disposed now
return Json(row_from_db); //this call should succeed
If you disable deferred loading and try to serialize then it won't try to lazy load the related objects.
Hope that works.
Mustafa
The DataContractSerializer for XML and DataContractJsonSerializer for JSON. You could also try the NewtonSoft JSON library.

Categories

Resources