EntityFramework ленивая загрузка не работает в другом контексте - PullRequest
0 голосов
/ 02 июля 2018

Я пытаюсь понять, как ленивая загрузка работает в Entity Framework 6. Мне кажется, что ленивая загрузка работает только в DbContext, где к свойству «быть загруженным» уже обращались. Пожалуйста, взгляните на приведенный ниже пример - тест LazyLoad_InSameContext успешно выполнен, а тест LazyLoad_InDifferentContext не пройден.

Это предполагаемое поведение? Разве оба теста не пройдут успешно?

using System.Transactions;
using NUnit.Framework;
using System.Data.Entity;

namespace EfTestProject
{
    [TestFixture]
    public class Test
    {
        [Test]
        public void LazyLoad_InDifferentContext()
        {
            Team team;
            Employee employee;
            using (var context1 = new Context())
            {
                team = new Team();
                context1.Teams.Add(team);
                context1.SaveChanges();
            }
            using (var context2 = new Context())
            {
                employee = new Employee {TeamId = team.Id};
                context2.Employees.Add(employee);
                context2.SaveChanges();

                Assert.NotNull(employee.Team); // <- This fails!
            }
        }

        [Test]
        public void LazyLoad_InSameContext()
        {
            Team team;
            Employee employee;
            using (var context1 = new Context())
            {
                team = new Team();
                context1.Teams.Add(team);
                context1.SaveChanges();

                employee = new Employee { TeamId = team.Id };
                context1.Employees.Add(employee);
                context1.SaveChanges();

                Assert.NotNull(employee.Team); // <- This works!
            }
        }
        [SetUp]
        public void SetUp()
        {
            new Context().Database.CreateIfNotExists();
        }
    }

    public class Context : DbContext
    {
        public Context() : base("name=Context") { }
        public virtual DbSet<Team> Teams { get; set; }
        public virtual DbSet<Employee> Employees { get; set; }
    }

    public class Team
    {
        public int Id { get; set; }
    }

    public class Employee
    {
        public int Id { get; set; }
        public int? TeamId { get; set; }
        public virtual Team Team { get; set; }
    }
}

Редактировать1:

Тест можно «исправить», добавив context2.Teams.Attach(team); перед созданием сотрудника. Хотя я не понимаю, почему это необходимо.

using (var context2 = new Context())
{
    context2.Teams.Attach(team);
    employee = new Employee {TeamId = team.Id};
    context2.Employees.Add(employee);
    context2.SaveChanges();
    employee = context2.Employees.Find(employee.Id);

    Assert.NotNull(employee.Team);
}

Редактировать2:

Также успешно, если я создаю новую сущность, используя context2.Employes.Create():

using (var context2 = new Context())
{
    employee = context2.Employees.Create();
    employee.TeamId = team.Id;
    context2.Employees.Add(employee);
    context2.SaveChanges();

    Assert.NotNull(employee.Team);
}

1 Ответ

0 голосов
/ 03 июля 2018

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

Например: с учетом отношения «Родитель / ребенок», когда у одного родителя много детей и в базе данных уже есть запись «Родитель» с идентификатором ParentID, равным 10.

using (var context = new TestDbContext())
{
  var child = new Child { Name = "test", ParentId = 10 }; 
  context.Children.Add(child); 
  context.SaveChanges();
  Assert.IsNull(child.Parent);
}

Родительский идентификатор будет установлен на 10, но Родительская ссылка будет нулевой. Контекст просто не знает об этом. Если вы позже загрузите этот родительский элемент, даже не связывая его с сущностью, presto, прокси вернет его. DbContext просматривает загруженные сущности для всего, что может ссылаться на загруженный родительский элемент, и связывает его. (Такое поведение, вероятно, объясняет замедление долгоживущих DbContexts.)

using (var context = new TestDbContext())
{
  var child = new Child { Name = "test", ParentId = 10 };
  context.Children.Add(child);
  context.SaveChanges();
  Assert.IsNull(child.Parent);
  var parent = context.Parents.Find(10);
  Assert.IsNotNull(child.Parent); // Poof, Parent is now a proxy pointing to the Parent.
}

Это может быть неловко, когда DbContexts являются относительно долгоживущими, поскольку его знание связанных сущностей может варьироваться в зависимости от того, что может или не могло быть загружено связанным кодом, что приводит к прерывистым исключениям NullReferenceExceptions и тому подобному.

Это одна из веских причин избегать использования свойств FK в сущностях, имеющих свойства навигации, вместо этого просто использовать свойство навигации с ключом, отображаемым за кулисами, или свойством shadow. (EF Core) Когда вы используете FK и свойства навигации, вы должны знать о некоторых потенциально странных действиях при получении информации из ссылок навигации после установки или изменения FK. Обычно безопаснее иметь дело со свойствами навигации.

...