为什么 EF Core 加载 child 个实体,当我在没有 .Include() 的情况下查询它时

Why EF Core loads child entities, when I query it without .Include()

我有一个简单的 One-To-Many 关系。 Parent:

public class Customer
    {
        public Customer(string name, string email)
        {
            Name = name;
            Email = email;
        }

        public Customer(string name, string email, long mobile)
            : this(name, email)
        {
            Mobile = mobile;
        }

        //for EF
        private Customer() { }

        public int Id { get; private set; }
        public string Name { get; private set; }
        public string Email { get; private set; }
        public long? Mobile { get; private set; }
        public List<Transaction> Transactions { get; private set; }

        public void AddTransactions(IEnumerable<Transaction> transactions)
        {
            if(Transactions == null)
                Transactions = new List<Transaction>();

            Transactions.AddRange(transactions);
        }
    }

Child:

public class Transaction
    {
        public Transaction(DateTimeOffset date, decimal amount, Currency currency, Status status)
        {
            TransactionDate = date;
            Amount = amount;
            CurrencyCode = currency;
            Status = status;
        }

        //for EF
        private Transaction() { }
        public int Id { get; private set; }
        public DateTimeOffset TransactionDate { get; private set; }
        public decimal Amount { get; private set; }
        public Currency CurrencyCode { get; private set; }
        public Status Status { get; private set; }
        public int CustomerId { get; set; }
        public Customer Customer { get; private set; }
    }

有一个简单的方法,它查询一个 Customer 并对其调用 SingleOrDefault。之后它查询交易,当它们被加载时,客户的交易从 null 变为 Count=5(我加载的交易)。为什么?在配置中我没有指定 .UseLazyLoadingProxies().

var customerQuery = _dbContext.Customers.AsQueryable();

            if (!string.IsNullOrEmpty(request.Email))
                customerQuery = customerQuery.Where(c => c.Email == request.Email);

            if (request.CustomerId.HasValue)
                customerQuery = customerQuery.Where(c => c.Id == request.CustomerId.Value);

            var customer = await customerQuery.SingleOrDefaultAsync(cancellationToken)
                .ConfigureAwait(false);
//here customer has null collection of transactions
            if (customer == null)
                throw new NotFoundException("Not Found.");

            var transactions = await _dbContext.Transactions
                .Where(t => t.CustomerId == customer.Id)
                .OrderByDescending(t => t.TransactionDate)
                .Take(5)
                .ToListAsync(cancellationToken);

//here customer has 5 transactions.
            customer.AddTransactions(transactions);
//here it has 10, because of method (following the DDD, it is used for providing business invariant)

EF 配置:

 public class CustomerEntityConfiguration : IEntityTypeConfiguration<Customer>
    {
        public void Configure(EntityTypeBuilder<Customer> builder)
        {
            builder.Property(c => c.Id)
                .HasMaxLength(10);

            builder.Property(c => c.Email)
                .HasMaxLength(25)
                .IsRequired();

            builder.Property(c => c.Mobile)
                .HasMaxLength(10);

            builder.Property(c => c.Name)
                .HasMaxLength(30)
                .IsRequired();

            //uniqueness constraint
            builder.HasIndex(c => c.Email)
                .IsUnique();

            builder.HasMany(t => t.Transactions)
                .WithOne(t => t.Customer)
                .HasForeignKey(t => t.CustomerId);
        }

////////////////////////////
public class TransactionEntityConfiguration : IEntityTypeConfiguration<Transaction>
    {
        public void Configure(EntityTypeBuilder<Transaction> builder)
        {
            builder.Property(t => t.Amount)
                .HasColumnType("decimal(10, 2)");
        }
    }

这是正常行为,是长期存在的 DbContext 的结果。也许可以解释为什么这种行为是不可取的?

选项 1:使用 AsNoTracking()。这告诉 EF 不要将加载的实例与 DbContext 相关联。不会发生自动接线。

选项 2:使用寿命较短的 DbContext。可以通过多种方法访问模块级 DbContext。使用绑定在 using 块中的较短生命周期的 DbContext 意味着调用无需担心共享引用。