看起来像我的导航属性在打开延迟加载时急切加载。我有这样的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 = "[email protected]",
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; }
}
因为延迟加载设置为false。我期望“用户”为空
下面是Microsoft EF Core instructions,这是我的处理方式(我另外将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 = "[email protected]",
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"
});
});
}
}
您可以找到工作资料库at GitHub
即使禁用了延迟加载,如果DbContext已经在跟踪您引用的实体,该引用也会自动填充。
例如,如果我有两个父记录,每个记录都包含一个子记录:
父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已加载并跟踪了父ID#2。在DbContext的生存期内,这可能发生在调用之前的任何地方,尤其是生存期范围遍及整个请求的情况。当我们以后去检索我们的子级,并假设懒惰加载已关闭并且我们不希望加载其父级时,您会发现,子级ID#1的父级引用为#null,但DbContext会将父级#2与孩子#2。当上下文填充“子级2”引用时,它看到与父级2的关联,并希望“父级ID 2”,它是本地跟踪缓存知道“父级2”,因此该引用自动填充。
如果改为junk
行:
var junk = context.Parents.AsNoTracking().Single(x => x.ParentId == 2);
然后,子级2的父级引用也将为#null,因为DbContext不会跟踪该父级引用。
随着应用程序的成熟以及新代码的引入或重构,这可能导致各种古怪的行为。只需要有人添加AsNoTracking()
作为“性能优化”,并且以前引用某些内容的某些行为现在已经失效。
作为避免复杂情况的一般规则,我的建议是不要返回超出产生它们的DbContext范围的实体,而应依靠ViewModels或DTO来表示要传递给视图或返回给API的数据消费者,而不是为此目的选择性地填充实体。实体应始终反映完整的数据状态,方法是填充其所有数据或访问所有数据(通过代理)。当不完整的实体被传递时,最终它们进入期望完整实体的方法(基于方法签名),这在假定数据是完整的但该实体的来源被遗弃的情况下导致了问题。