Using .Include() in a database query creates an infinite loop of data - c#

The problem: I'm using a LINQ to query against an Entity Framework context to a relational database. Following along with EFCore Relationships, I made a database with a few relationships, but when I sent the data to the server using ASP.NET, I got null values for these relationships.
With a little more digging, I found that I needed to use _context.Post.Include("Blog") to send the data since it uses lazy loading. However, by doing this, it created an "infinite loop" in the JsonResult.
The data that ends up getting returned appears to be cut in half and I get JSON parse errors when loading it because it's missing the second half of the JSON data.
Example return value
{"id": 0, "blogId": 1, "blog": {"postId":0
That's all it returns, as anything after that postId would be a reference back to the original post, which in turn has a reference to the blog. Is there any way that I can use LINQ to exclude the post object in the return, similar to how the blog was included in the first place?

This is JSON serialization problem.
Search how to solve "circulare references" problem in your JSON stream serializer documentation.
Different stream serializers provide different solutions:
configure max depth
history based on references cache
configure specific types serializers to do not use "navigation properties"
Other solution - do not use stream serializers - configure serializer function at place.
And another one - use DTO classes (where there are no circular references).
You can also try to detach your entities and set null to every navigation property you want to ignore but this is ugly and can't be recommended.

Fixed! I took the model that came from the database with the relationship included, then used a foreach to null out the reverse reference.
var blog = await _context.Blog
.Include(x => x.Posts)
.SingleAsync(x => x.Id == id);
foreach (Post post in blog.Posts)
{
post.Blog = null;
}
return Ok(blog);

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.

Mapping List<dynamic> using Slapper

I've got the following code snippet in a repository class, using Dapper to query and Slapper.Automapper to map:
class MyPocoClass{
MyPocoClassId int;
...
}
//later:
var results = connection.Query<dynamic>("select MyPocoClassID, ...");
return AutoMapper.MapDynamic<MyPocoClass>(results).ToList();
results above has many items, but the list returned by AutoMapper.MapDynamic has only one item (which is clearly wrong). However, I found that adding the following configuration to AutoMapper fixes the problem:
AutoMapper.Configuration.AddIdentifier(typeof(MyPocoClass), "MyPocoID");
Why does Slapper.AutoMapper need to know the key of my class to simply map a list to another list? Is it trying to eliminate duplicates? I'll also note that this only happens while mapping a certain one of my POCOs (so far)...and I can't figure out why this particular POCO is special.
Turns out this is a bug in Slapper.AutoMapper.
The library supports case-insensitive mapping and convention-based keys. The SQL result set has MyPocoClassID and the class itself has MyPocoClassId -- which is not a problem for Slapper.AutoMapper as far as mapping goes. But internally Slapper.AutoMapper identifies (by convention) that MyPocoClass has MyPocoClassId as its identifier, and it can't find that field in the result set. The library uses that key to eliminate duplicates in the output list (for some reason), and since they're all 'null/empty', we get only one record.
I may submit a pull request to fix this problem, but since the library appears to be unmaintained I don't think it'll help.

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.

Does LINQ load every object in a request?

Let me explain my self.
With the help of LINQ i'm requesting an object to my database such :
int idGetClient=1;
Client clientToUpdate = context.Client.FirstOrDefault(p=>p.idClient == idGetClient);
in my Model, a client is related to another object called Site. So i can easily get my Site object from my SQL database just by calling :
Site siteToUpdate = clientToUpdate.Site;
So i wonder what's happening here, did LINQ ALREADY loaded up the result in my first request OR is he requesting again taking up my client information and looking up in my database ?
The second one looks the most logic to me but i want to make sure, since if what happen if the first case, it's going to cause some performance issues.
If nothing else is specified, your property Site will be lazy loaded, so just at the moment you try to access to the property, Entity will query the database server.
If a second access to the database server will cause performance issue, you can prevent that with:
int idGetClient=1;
Client clientToUpdate = context.Client
.Include("Site")
.FirstOrDefault(p=>p.idClient == idGetClient);
And you can add as many Include as you want, and you can even load properties of properties :
int idGetClient=1;
Client clientToUpdate = context.Client
.Include("Site")
.Include("Site.SubProperty")
.FirstOrDefault(p=>p.idClient == idGetClient);
The Include will force the eager loading of the specified properties.
More info here :
https://msdn.microsoft.com/en-us/data/jj574232.aspx
Presuming you are using entity framework, you can hook into the sql statements yourself and see what's going on when you access your object - e.g.
context.Database.Log = Console.Write;
It's also possible to ensure the relation is loaded by using include:
context.Client.Include("Site").FirstOrDefault(p=>p.idClient == idGetClient);
In most cases the navigation property .Site causes the Site object to be lazy loaded. That is: a specific database query is issued.
The question is a bit scarce on details, so I assume that:
.Site is a navigation property representing a relation to another table in the database.
No global configuration on early loading is done (which is possible with some linq providers).
I recommend using a SQL profiler to see what queries are actually issued to the database (see this blogpost for some reasons on why).

Circular reference exception when serializing LINQ to SQL classes

I have a set of linq to sql classes and serialized them into JSON using the .NET JavaScriptSerializer.
However, as soon as I add record onto a relating table, serialization throws a "Circular reference exception". Aaarggh!
It's described in detail here.
I have a few options
Convert the linq to sql class to a class with no relationships thus avoiding the circular reference
snip the circular reference by nulling associations - i don't consider this to be a real option
Use ScriptIgnoreAttribute (somehow). I couldn't easily apply this because the properties are in generated classes and LINQ to SQL doesn't automatically honor buddy classes
Use JSON.NET and somehow use attributes + buddy classes to stop the serializer trying to walk across relationships.
Has anyone else encountered this? I would really prefer the last option if possible but I don't know how to do this.
Any help is greatly appreciated
The latest version of Json.NET supports serializing circular relationships. Check out Preserving Object References in the help.
Additional link for accepted answer
Json.NET Help, Preserving Object References (with example)
It seems it works fine with LINQ to SQL
James's solution solved part of my problem. I needed to exclude certain List Types within the object. To solve my problem, I just copied the parts of the object I needed. The following is an example.
var DB = new DBDataContext();
var lUsers = new List<User>();
DB.Users.ToList().ForEach(x => lUsers.Add(new User()
{
ID = x.ID,
FIRST_NAME = x.FIRST_NAME
}) );

Categories

Resources