【问题标题】:Why EF Core loads child entities, when I query it without .Include()当我在没有 .Include() 的情况下查询 EF Core 时,为什么会加载子实体
【发布时间】:2019-09-20 20:51:52
【问题描述】:

我有一个简单的一对多关系。 家长:

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);
        }
    }

孩子:

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; }
    }

有一个简单的方法,它查询一个客户并在其上调用 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)");
        }
    }

【问题讨论】:

  • 这是正常行为。 Relationship fix-up.
  • @Gert Arnold,那我该怎么处理呢?确保 .AsNoTracking()。我认为这不是一个好的解决方案(它解决了问题,但如果我想 SaveChanges()?)。我不想让一些单独的查询影响我之前的查询。
  • 为什么要处理它?有什么问题?
  • 使用短暂的上下文,或者只为不希望填充其他导航属性的代码段创建一个新的上下文。

标签: entity-framework asp.net-core eager-loading


【解决方案1】:

这是正常行为,也是长期 DbContexts 的结果。或许可以解释一下为什么这种行为是不可取的?

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

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

【讨论】:

    猜你喜欢
    • 2013-01-08
    • 2022-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 2023-02-04
    相关资源
    最近更新 更多