为什么 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 意味着调用无需担心共享引用。
我有一个简单的 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 意味着调用无需担心共享引用。