简体   繁体   中英

Preventing automatic population of circular navigation properties in Entity Framework

I have a problem when retrieving entities which have a circular reference. My entity navigation properties are not lazy loaded, so I would expect them to return null unless specifically included in a query, however I've found that when there is a circular reference between two entities this isn't the case and instead a recursive hierarchy is returned.

For example, let's say we have two entities UserEntity and PostEntity . A UserEntity may have many posts, but a post must only have a single UserEntity . So, the configuration is as follows:

// UserEntity configuration
HasMany(u => u.Posts)
  .WithRequired(p => p.User);

If I query the database for either a UserEntity or a PostEntity without using Include() on the respective Posts or User navigation properties, the navigation properties are null as expected.

However, if I query for a UserEntity and include its PostEntity s, an circular hierarchy is returned, even though I never requested that the PostEntity.User navigation property be populated:

using (var db = new MyDbContext())
{
  var user = await db.Users
    .Include(u => u.Posts)
    .SingleOrDefaultAsync(u => u.ID == 0);

  // The [UserEntity] contains a list of [PostEntitiy]s
  //   each with the [UserEntity] each with a list of [PostEntitiy]s...
  //     and so on.
}

This isn't too much trouble, but when a list of PostEntity s are retrieved and their UserEntity s are included things get very weird:

using (var db = new MyDbContext())
{
  var posts = await db.Posts
    .Include(p => p.User)
    .SingleOrDefaultAsync(p => p.ID == 0);

  // This throws a [System.InvalidOperationException]:
  // "Sequence contains more than one element"
  // How can this be? The ID is unique.

  var posts = await db.Posts
    .Include(p => p.User)
    .Where(p => p.ID == 0)
    .ToListAsync();

  // This returns several copies of the PostEntity
  //   which should be unique in the database. Is
  //   this a side effect of the circular reference?
}

Obviously getting rid of the circular reference would fix this, but there are several reasons why keeping it is beneficial, if possible. Why is EntityFramework returning this circular hierarchy despite only a single, one-way relationship being requested with Include() , and why is this resulting in several PostEntity s being returned when their UserEntity s are included?

You could try projecting your entities into DTOs to work around this. Use projection into some type that does not have such references and avoid the exception.

In other words, pick up just the properties that you need from the EF model, not adding fields which are nested complex types that also have properties that point back to the same object and thus create circular references

using (var db = new MyDbContext())
{
    var posts = db.Posts
        .Include(p => p.User)
        .Where(p => p.ID == 0)
        .Select(p => new PostsDTO
        {
            Name = p.Name,
            Username = p.User.Username
        });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM