Свойство Navigation Eagerly загружается при включенной отложенной загрузке

Похоже, что мое свойство навигации загружается, когда включена отложенная загрузка. У меня есть настройка DBContext, как это

public class BBBankContext : DbContext
{
    public BBBankContext(DbContextOptions<BBBankContext> options)
        : base(options)
    { }

    public DbSet<Account> Accounts { get; set; }
    public DbSet<User> Users { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Account>(b =>
        {
            b.HasData(new Account
            {
                Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
                AccountNumber = "0001-1001",
                AccountTitle = "Raas Masood",
                CurrentBalance = 2342.34,
                Email = "raasmasood@hotmail.com",
                PhoneNumber = "6096647000",
                AccountStatus = AccountStatus.Active

            });
            b.OwnsOne(e => e.User).HasData(new
            {
                AccountId = "37846734-172e-4149-8cec-6f43d1eb3f60",
                Id = Guid.NewGuid().ToString(),
                AuthID = Guid.NewGuid().ToString(),
                Name = "Raas Masood",
                ProfilePicUrl = "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50"
            });
        });

    }
}

Я не хочу, чтобы свойство навигации «Пользователь» загружалось, если я не использую «Включить». но это автоматически загружается. в моем стартапе у меня есть такая конфигурация.

       services.AddDbContext<BBBankContext>(
b => b.UseSqlServer(connection)
        .UseLazyLoadingProxies(false)  //Install-Package Microsoft.EntityFrameworkCore.Proxies -Version 3.1.1

        );

и сущности выглядят так

    public class Account : BaseEntity
{
    public string AccountNumber { get; set; }
    public string AccountTitle { get; set; }
    public double CurrentBalance { get; set; }
    public string Email { get; set; }
    public string PhoneNumber { get; set; }
    public AccountStatus AccountStatus { get; set; }
    public virtual User User { get; set; }

}
public enum AccountStatus
{
    Active = 0,
    InActive = 1
}

public class User : BaseEntity
{
    public string AuthID { get; set; }
    public string Name { get; set; }
    public string ProfilePicUrl { get; set; }
}

 public class BaseEntity
{
    [Key]
    public string Id { get; set; }
}

введите описание изображения здесь

так как отложенная загрузка установлена ​​в ложь. я ожидаю, что "пользователь" будет нулевым

Всего 2 ответа


Следуя инструкциям Microsoft EF Core, я так и сделаю (я дополнительно изменил тип свойства Key на Guid):

public class BaseEntity
{
    [Key]
    public Guid Id { get; set; }
}

public class Account : BaseEntity
    {
        public string AccountNumber { get; set; }
        public string AccountTitle { get; set; }
        public double CurrentBalance { get; set; }
        public string Email { get; set; }
        public string PhoneNumber { get; set; }
        public AccountStatus AccountStatus { get; set; }
        public virtual User User { get; set; }
        public Guid UserId { get; set; }
    }

public class User : BaseEntity
    {
        public string AuthID { get; set; }
        public string Name { get; set; }
        public string ProfilePicUrl { get; set; }
        public virtual Account Account { get; set; }
    }

public enum AccountStatus
    {
        Active = 0,
        InActive = 1
    }

public class BBBankContext : DbContext
    {
        public BBBankContext(DbContextOptions<BBBankContext> options)
        : base(options)
        { }

        public DbSet<Account> Accounts { get; set; }
        public DbSet<User> Users { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Account>(b =>
            {
                b.HasData(new Account
                {
                    Id = Guid.Parse("37846734-172e-4149-8cec-6f43d1eb3f60"),
                    AccountNumber = "0001-1001",
                    AccountTitle = "Raas Masood",
                    CurrentBalance = 2342.34,
                    Email = "raasmasood@hotmail.com",
                    PhoneNumber = "6096647000",
                    AccountStatus = AccountStatus.Active,
                    UserId = Guid.Parse("24ce7f8a-cbeb-4b33-8a3d-952830b92d04")
                });

                b.HasOne(a => a.User)
                    .WithOne(u => u.Account)
                    .HasForeignKey<Account>(a => a.UserId);
            });

            modelBuilder.Entity<User>(b =>
            {
                b.HasData(new User
                {
                    Id = Guid.Parse("24ce7f8a-cbeb-4b33-8a3d-952830b92d04"),
                    AuthID = Guid.NewGuid().ToString(),
                    Name = "Raas Masood",
                    ProfilePicUrl = "https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50"
                });
            });
        }
    }

Вы можете найти рабочий репозиторий на GitHub


Даже если отложенная загрузка отключена, если DbContext уже отслеживает сущность, на которую вы ссылаетесь, эта ссылка будет заполнена автоматически.

Например, если у меня есть 2 родительские записи, каждая из которых содержит дочернюю запись:

Родитель 1 => Ребенок 1

Родитель 2 => Ребенок 2

using (var context = new AppContext())
{
    var junk = context.Parents.Single(x => x.ParentId == 2);

    var children = context.Children.ToList();
    Assert.IsNull(children.Single(x => x.ChildId == 1).Parent);
    Assert.IsNotNull(children.Single(x => x.ChildId == 2).Parent);
}

Это очень грубый пример поведения, но junk представляет собой dbContext, который загрузил и отслеживает идентификатор родителя №2. В течение срока службы DbContext это может происходить в любом месте до нашего вызова, особенно если время жизни превышает весь запрос. Когда мы позже перейдем к извлечению наших детей и, предположив, что отложенная загрузка отключена и мы не стремимся загружать их родителей, вы обнаружите, что родительская ссылка Child ID # 1 будет #null, но DbContext свяжет Parent # 2 с Ребенок № 2. Когда контекст заполняет ссылку Child # 2, он видит связь с parent и хочет Parent ID # 2, его локальный кеш отслеживания знает о Parent # 2, поэтому ссылка заполняется автоматически.

Если junk строка была вместо:

var junk = context.Parents.AsNoTracking().Single(x => x.ParentId == 2);

тогда родительская ссылка дочернего элемента # 2 также была бы #null, поскольку DbContext не отслеживал бы эту родительскую ссылку.

Это может привести ко всем видам причудливого поведения, поскольку приложения становятся более зрелыми, а новый код вводится или повторно анализируется. Все, что нужно, это добавить кого-то AsNoTracking() в качестве «оптимизации производительности», и некоторое поведение, которое ранее имело ссылку на что-то, теперь не работает.

Как правило, чтобы избежать сложностей, я рекомендую никогда не возвращать сущности за пределы DbContext, который их породил, и вместо этого полагаться на ViewModels или DTO для представления данных, которые вы хотите передать в представление или скорее вернуть потребителю API. чем пытаться выборочно заполнить объекты для этой цели. Сущность всегда должна отражать полное состояние данных, либо заполнив все свои данные, либо все данные доступны (через прокси). Когда незавершенные сущности передаются, в конечном итоге они попадают в методы, которые ожидают завершенные сущности (основанные на сигнатуре метода), и это приводит к проблемам, когда предполагается, что данные полны, но источник этой сущности не заполнен.


Есть идеи?

10000