Entity Framework всегда включает данные, которые находятся в контексте, даже если я не прошу об этом

Я использую MVC.NET web api, EF с DB сначала, и у меня есть ленивая загрузка, отключенная в моем контексте. EF возвращает слишком много данных, даже если LazyLoading отключен.

Например, у меня есть пользователи с одной ролью. Когда я запрашиваю для Users и Include Role, свойство Role.Users автоматически заполняется данными, так как пользователи загружаются в контекст.

Почему я не могу заставить EF дать мне ТОЛЬКО то, что я прошу? Или я пропустил что-то большое здесь?

public partial class User
{
    public int UserID { get; set; }
    public string Title { get; set; }
    public string Email { get; set; }
    public int RoleID { get; set; }

    ....

    public virtual Role Role { get; set; }
} 

public partial class Role
{
    public int RoleID { get; set; }
    public string RoleName { get; set; }

    ....

    public virtual ICollection<User> Users { get; set; }
} 




return db.Users.Include(u => u.Role);
// ^^ user.Role.Users is filled with 1000s of users

TL; DR - Я хочу, чтобы EF никогда не загружал данные в свойства/коллекции навигации, если я не включил() его напрямую. При сериализации в JSON я хочу только то, что я прошу явно. Похоже, что даже при ленивой загрузке загружаются и возвращаются свойства навигации, которые уже находятся в контексте (т.е. Обычно "круговые ссылки" ).

Ответы

Ответ 1

Поведение, которое вы видите, называется Fixup Relationship, и вы не можете его отключить.

Если вы загружаете роли пользователей для их сериализации и отправляете их куда-то, я предполагаю, что вы не хотите отслеживать изменения объектов в том контексте, в котором они были загружены. Таким образом, нет необходимости присоединять их к контекст, и вы можете использовать:

return db.Users.Include(u => u.Role).AsNoTracking();

Или используйте проекцию в объект, специализированный для сериализации, как предлагается @STLRick.

Ответ 2

Я не хочу, чтобы он загружал что-либо помимо того, что я говорю, чтобы он включал.

Похоже, вам нужно использовать Явная загрузка. В принципе, вы можете загружать определенные объекты следующим образом:

context.Include("Roles")

К моим лучшим знаниям, которые не должны включать связанные объекты. Lazy load действительно должен быть отключен, и вы можете явно загрузить навигационные свойства с помощью Load.

Ответ 3

Вы можете выбрать только то, что вам нужно, используя Select().

var users = _db.Users.Select(x => new
{
    UserID = x.UserID,
    Title = x.Title,
    Email = x.Email,
    RoleID = x.RoleID
}).AsEnumerable();

Ответ 4

Вы правы, что при ленивой загрузке вы получите свойства навигации, потому что они "тронуты" сериализатором, который заставляет их загружаться. Ленивая загрузка должна быть отключена, если вы хотите, чтобы свойства возвращались как null. Тем не менее, "кажется", как только объекты загружаются в контекст (например, через другие запросы), они будут обрабатываться сериализатором. Поэтому ответ заключается в том, чтобы заставить сериализатор не возвращать свойства навигации. Лучший способ, которым я смог найти это, - использовать DTO (объекты передачи данных). Это позволяет вам точно вернуть данные, а не ваши фактические объекты.

Ваш DTO может выглядеть примерно так:

public partial class UserDto
{
    public UserDto(user User)
    {
        UserID = user.UserID;
        Title = user.Title;
        //... and so on
    }
    public int UserID { get; set; }
    public string Title { get; set; }
    public string Email { get; set; }
    public int RoleID { get; set; }

    //exclude the Role navigation property from your DTO
}

... и тогда вы можете сделать что-то вроде этого:

return db.Users.Include(u => u.Role).Select(user => new UserDto(user));

Ответ 5

Во-первых: Поворот на ленту.

Второе: если вы хотите отфильтровать то, что вы получаете и возвращаете, затем создайте пользовательский объект возврата или что-то в этом роде.

from u in db.Users
join r in db.Roles
  on u.RoleID equals r.RoleID
select new { u.UserID, u.Title, u.Email, r.RoleName }

Или что-то в этом роде. У вас будет минимальный объект возврата, а ваш граф объектов будет крошечным.