WCF error serializing cycle reference - c#

I'm trying to return lists of objects that have references to another objects and viceversa.
I only want the lazy load to get the "first level children", I mean, if I have a "Person" object with a "Place" property, I want the place data to be loaded but not every object in "Place" object needs to be loaded... because this would ahead to a cyclic reference...
I've read that I could do this by using [DataContract(IsReference = true)] on every Object.
I've set every object in the model (auto-generated by EF) with that decoration but it's still failing when trying to send it back to the service caller.
Am I missing anything? Thanks in advance.

I have used the [DataContract(IsReference=true)] successfully to solve the cyclic dependency issue in the past. Admittedly they were not objects generated by EF, but I' not sure how much that should matter.
What is the exact error?
Is it that the graph is getting to big?
Could it be that your objects are not the same instances, but different instances of the conceptually same type?
So when your TypeA-instance1 gets serialized and it has a reference to TypeB-instance1 which has a Reference to TypeA-instance1 the 2 actual TypeA-instance1 objects to not compare equal so the serializer does not attempt to reuse the references?
You could override the equals method on your objects and do some equality testing based on properties of your object rather than the default memory address that will be used.

I mean, if I have a "Person" object
with a "Place" property, I want the
place data to be loaded but not every
object in "Place" object needs to be
loaded...
That is not possible when using lazy loading. Once the entity gets serialized the serializer will access every single property. Accessing every navigation property will trigger lazy loading and serializer will continue with loaded properties => it will always serialize complete object graph. In your scenario it can mean:
Serializer will start with a Person which have navigation property to Place
Lazy loading will load the Place and serializer will serialize.
If the Place have navigation property to all Persons lazy loading will trigger and load all persons referencing the Place!
Serializer will start serializing every single loaded Person - if IsReference is set to false you will get exception with cycles in object graph. If not it will create huge message.
That is very basic explanation what happens if you try to serialize object when using lazy loading. If your entities have other navigation properties the same effect will happen for them. In the worst case you can easily build an operation which will try to pull and serialize all data from the database. That will most probably lead to timeout.
There is one more problem with lazy loading. Serialization takes place outside of operation scope. So if you close / dispose ObjectContext in the operation you will get an exception when entity triggers lazy loading.
Don't use lazy loading when exposing entities on WCF or use DTO to control what data should be passed from the operation.

You may want to convert your object tree to other, flat objects and return those instead.

Related

Get Request displaying foreign key tables

When i don't have anything in my 'bookings' table my GET endpoints for my customer and Accommodation table work perfectly. Once i create a booking every get request for each table returns every entry in every table.
This is my data model
This is my get request for customers
// GET: api/Customer
[ResponseType(typeof(Customer))]
public async Task<IHttpActionResult> GetCUSTOMERs()
{
var customers = await db.Customers.ToListAsync();
return Ok(customers);
}
When i call a get request for the customer table i only want customer data how can i do this?
An entity framework model has lazy loading enabled by default.
When you return Ok(customers); the API will attempt to serialize the entities so that they can be sent as (probably) JSON or XML. As it serializes through each customer entity it will encounter the Bookings property. As the serializer is requesting that property, Entity Framework will "lazy load" in the bookings which are associated with the customer. Then the serializer will attempt to serialize each booking and hit the Accommodations property... and so on.
Your code above is returning all customers, so you will end up returning every accommodation which has been booked. I expect if you made a new Accommodation which had no bookings, it would not be returned in the output from this call.
There are several ways you can prevent all this from happening:
Disable Lazy Loading
You can disable lazy loading on an EF model by opening the model, right click on the white background of the model diagram and choose "Properties", then set "Lazy Loading Enabled" to "False".
If you have other functions where you want to access the related properties from an entity, then you can either load them in to the context with an "Include" or load them separately and let the EF fixup join the entities together.
My personal opinion is that disabling lazy-loading is generally a good idea because it makes you think about the queries you are making to the database and you have to be much more explicit about asking for what data should be returned. However, it can be a lot more effort and is probably something to look at when you start trying to optimise your application rather than just getting it working.
This Microsoft page "Loading Related Entities" also explains various options (as well as describing exactly the issue with lazy loading your entire database!).
Map Your Entities and Return DTOs
You have more control about how the code traverses your model if you map the entities from EF into DTO's.
From an API perspective using DTOs is a great idea because it allows you to more or less define the output of an endpoint like an interface. This can often remain the same while the underlying data structure may change. Returning the output of an EF model means that if the model changes, things which use that data may also need to change.
Something like AutoMapper is often used to map an EF entity into DTOs.
Serializer Settings
There may be some media-type formatter settings which allow you to limit the depth of entities which will be traversed for serialisation. See JSON and XML Serialization in ASP.NET Web API for a place to start.
This is probably too broad of a change, and when you want to actually return related objects would cause a problem there instead.

How to serialize into a json from entity?

I'm trying to serialize an (Entity Framework 6) entity into json. I am making sure the entry is in memory before serializing via the AsNoTracking() method however I get an error as it cant receive a value form a different table that is referenced in the entry.
Inner Exception: When an object is returned with a NoTracking merge option, Load can only be called when the EntityCollection or EntityReference does not contain objects.
Exception: JsonSerializationException: Error getting value from 'TABLE_X' on 'System.Data.Entity.DynamicProxies....
Code:
List<Location> locations = new DbContext().Locations.Where(x => x.Type == 1).Take(5).AsNoTracking().ToList();
string s = JsonConvert.SerializeObject(locations, new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
All I want to do is return a string of the serialized entity. Im not worried about other objects, solely the locations entity.
When I tried disposing of the connection and then json serializing I received the error: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I only want to serialize my list, I do not want to return/serialize any foreign dependencies.
This an EF's dynamic proxy issue you have to disable it to have your code working
in your class that inherit from DbContext
public class MyModelEntities : DbContext
{
public MyModelEntities()
{
//just disable it like this
Configuration.ProxyCreationEnabled = false;
}
}
Mainly what's happening is that your JsonConvert is trying to serialize an object like this System.Data.Entity.DynamicProxies.Location_5E43C6C196972BF0754973E48C9C941092D86818CD94005E9A759B70BF6E48E6
due to the proxy, which cannot be found because it's dynamically created
You don't need to call AsNoTracking method to load to memory the entities you need. The ToList method is going to do that job.
Now about your issue, is because the JSON serializer is trying to access to each property on an Location instance and you can end up querying for your entire database just because lazy loading is enabled. So, you have two options:
Disable Lazy loading (As #BRAHIMKamel recommended)
Use JsonIgnore atribute over the navigation properties you don't want to be loaded.
Personally, I prefer the first one, and when I need to load a entity with some specific related entity, I use eager loading to load it as part of the query:
context.Locations
.Include(l=>l.State)//eager loading an hypothetical related entity
.Where(x => x.Type == 1)
.Take(5)
.ToList();
If your object graph is not too complicated, different approach could be to create simple POCO class, where your Location will be mapped from. Let's say LocationModel. This could be mapped by hand or for example with AutoMapper.

Entity framework and DataContractSerializer

I've been reading about serialization of entities graph in an entity framework context using Linq to entities and the different possible serializers: Binary, XmlSerializer and DataContractSerializer.
As i understood the binary and XmlSerializer serialize the entity without its relationships. In case relationships are serialized it would cause a problem because of the nature of the resulting xml file structure ( for XmlSerializer).
The DataContractSerializer serialize the graph in its whole depth unless the lazy load is disabled.
My question is: I want to serialize a part of the graph. For example if I have an entity A and three related entities B, C and D, only B and D would be serialized with A. I want to use the the DataContractSerializer. If I delete the [DataMemberAttribute] of the unwanted navigational properties would that work?
You can actually disable lazy-loading, serialize/deserialize, and then re-enable lazy-loading.
context.ContextOptions.LazyLoadingEnabled = false;
StackOverflow Source
Since attributes are static metadata you can't mess with them at run-time. And if you remove them from your entity they will be permanently removed.
The Lazy loading isn't probably what you want, since when you load you bring the entire graph, the partial graph cenario only usually comes up on updates or partial inserts.
Your cenario, from what I gathered is when you want to update something, you want to update a partial graph only, and not the entire graph that you have on the client. One way to achieve this is to remove manualy the other DataMembers and set them to null, serialize them, update, and them repair the null references you previously set, finally make sure the ChangeTrackers are all in their previous state.
In our specific development cenario, we achieved this behaviour through a T4 template, that generates all the messy code, generating part of a "DataManager" that handles all the Self Tracking Entities once they exist on the client.
In my experience, it seemed like the only reliable way to disable lazy-loading is to go to the Entity Designer winder, right-click in the background, select "Properties", and set "Lazy Loading Enabled" to false in the Properties window.

How are relational members from LINQ to SQL DataContext handled when objects are sent via WCF?

Let's say I have a relational database with tables: OrderableCategories and Orderables. They are in a relation of one-to-many with one OrderableCategory attached to multiple Orderables. Therefore, an OrderableCategory instance in LINQ has members: ID, Name and an EntitySet<Orderable> Orderables. While sent via WCF (wsHttpBinding if it matters anyhow), the EntitySet is translated to simple Orderable[]. An Orderable Instance also contains a member called OrderableCategory which is simply an instance of this orderable's category. While sent via WCF, I guess something like this happens: an Orderable instance fills its OrderableCategory instance with fields from this category, but its Orderable[] is also filled with other orderables in this category. These orderables have its OrderableCategory filled with this category again and so on, so that I could theoretically call (for a received orderable o): o.OrderableCategory.Orderables[0].OrderableCategory.Orderables[0]. (...) and so on. I'm only guessing that the server gets into an infinite loop and when message size exceeds the quota, it disconnects and I see an exception of service shutting down. How can I avoid this scenario and have the benefits of relations in my database? I think my suspicions are correct, because when I disabled one of the properties (made it internal in LINQ Class Designer), the data is filled "one-way" only and the Orderable has no longer its OrderableCategory member, it works. But I would like to know if this could be achieved without compromising the property.
This must be handled by marking entities with DataContract attribute and setting its IsReference property to true. This will instruct DataContractSerializer to track references instead of serialize objects as you descirbed.
Linq-To-Sql designer / SqlMetal should do this for you by setting Serialization Mode to Unidirectional.
If you send entities over WCF, nice features like lazy loading go out the window, of course.
You basically need to decide which of the two options you'd like to use:
if you ask for the entity OrderableCategory, you can return just its basic "atomic" properties, e.g. ID, Name and so on. The benefit is smaller size - you're sending back less data
or alternatively: if you ask for the entity OrderableCategory, you can return its basic properties plus you could load a whole list of Orderables that this category contains, and return both at the same time; benefit: you have that data available right away, but on the downside, you'll have to send a lot more data.
Obviously, you cannot really do an infinite eager pre-loading - at some point, you have to stop and leave retrieval of more data to a later WCF service call. Your client would have to ask specifically and explicitly for yet another OrderableCategory if you're interested in that.

NHibernate correct way to reattach cached entity to different session

I'm using NHibernate to query a list of objects from my database. After I get the list of objects over them I iterate over the list of objects and apply a distance approximation algorithm to find the nearest object. I consider this function of getting the list of objects and apply the algorithm over them to be a heavy operation so I cache the object which I find from the algorithm in HttpRuntime.Cache.
After this point whenever I'm given the supplied input again I can just directly pull the object from Cache instead of having to hit the database and traverse the list. My object is a complex object that has collections attached to it, inside the query where I return the full list of objects I don't bring back any of the sub collections eagerly so when I read my cached object I need lazy loading to work correctly to be able to display the object fully.
Originally I tried using this to re-associate my cached object back to a new session
_session.Lock(obj, LockMode.None);
However when accessing the page concurrently from another instance I get the error
Illegal attempt to associate a
collection with two open sessions
I then tried something different with
_session.Merge(obj);
However watching the output of this in NHProf shows that it is deleting and re-associating my object's contained collections with my object, which is not what I want although it seems to work fine.
What is the correct way to do this? Neither of these seem to be right.
Lock should work. I think your problem is that the original session is not disposed yet. Try disposing the original session after you cache the object.

Categories

Resources