Entity Framework 未延迟加载引用和集合属性
Entity Framework is not loading the reference and collection properties lazily
我有 Backpack 和 Book 实体。书籍参考背包(一对多)。
我正在创建一个背包实例和一堆书。所以在这种情况下,背包里有一堆书。我正在将这些实体保存到数据库中。我正在验证那些已保存到数据库中。当我尝试加载背包时,它加载正常,并且设置了除导航属性之外的所有属性。我也在检查是否禁用了 LazyLoading。我的导航属性有 virtual 关键字。
我不确定我做错了什么。如果我尝试使用 Include() 加载背包,它会加载书籍:
dbContext.Backpacks.Where(b=>b.Name!="").Include("Books").FirstOrDefault()
我想弄清楚为什么它不延迟加载书籍?我在加载这本书时遇到了同样的问题。当我装书时,它没有附上背包。我看到 BackpackId 在那里。
在我的 属性 getter/setter 中,我有一些逻辑会被触发,但我不确定这怎么会是个问题。
预先加载是使用 Include()
方法实现的,因此您使用 Include("Books")
.
强制预先加载
改变这个:
dbContext.Backpacks.Where(b=>b.Name!="").Include("Books").FirstOrDefault()
对此:
dbContext.Backpacks.Where(b=>b.Name!="").FirstOrDefault()
您现在应该看到 Books
不再急切加载。
参考:
由于手头资料有限,我可以看到以下针对您的问题的解释。
延迟加载或代理创建被禁用
确保启用了延迟加载和代理创建,如果没有后者,前者将无法工作。
dbContext.Configuration.ProxyCreationEnabled = true;
dbContext.Configuration.LazyLoadingEnabled = true;
(详见this SO post)
配置上下文后访问实体
简化后的延迟加载是这样的:
- 每当您从上下文中检索一个实体时,您实际上会得到一个自动创建的 class 子 class 的对象,您期望它会覆盖您的
virtual
导航属性,它是代理.
- 每当您随后访问所述导航属性时,代理将访问数据库并在需要时加载链接的实体。
当然,这最后一步只有在 entity/proxy 仍然 附加 到上下文时才有可能,因此可以查询数据库以检索所述对象。
using( var dbContext = new MyContext() )
{
dbContext.Configuration.ProxyCreationEnabled = true;
dbContext.Configuration.LazyLoadingEnabled = true;
// Note: You can modify or derive from `MyContext` so that this is
// done automatically whenever a new `MyContext` is instantiated
var backpack = dbContext.Backpacks.Where(b=>b.Name!="").FirstOrDefault();
// This should work
var firstBook = backpack.Books.FirstOrDefault();
}
// This will probably not, because the context was already disposed
var firstDrink = backpack.Drinks.FirstOrDefault();
希望对您有所帮助,如果没有帮助,请随时提供更多信息
调试了几天,终于找到问题所在了。如上所述,您必须启用 LazyLoading 和 ProxyCreating。即使启用了 LazyLoading 和 ProxyCreating,我也遇到了问题。还要确保将导航属性声明为 virtual
,否则 EF 将无法延迟加载实体。
所以我遇到的问题是,EF 没有创建代理,因为我的实体没有 public 或没有参数的受保护构造函数。在创建没有参数的 public (在我的例子中是受保护的)构造函数之后它起作用了。
注意: 没有 public/protected 不带参数的构造函数不会影响预先加载。
Here is a link that explains the requirements for the LazyLoading
我必须使用 .NET Core 3.1 和 Microsoft.EntityFrameworkCore 3.1.5...
执行的步骤
1) 添加 Microsoft.EntityFrameworkCore.Proxies
NuGet 包
2) 将您的 DbContext
配置为 UseLazyLoadingProxies
(在 Startup.cs
中)
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<DataContext>(optionsBuilder =>
{
optionsBuilder
.UseLazyLoadingProxies() // <--- You need this bit
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
...
}
3) 将所有合适的属性(那些你想要延迟加载的)标记为 virtual
public class MyEntity
{
public virtual OtherEntity? { get; set; } // Lazy loaded coz `virtual`
public ICollection<OtherEntity> { get; set; } // NOT lazy loaded coz not `virtual`
}
我有 Backpack 和 Book 实体。书籍参考背包(一对多)。 我正在创建一个背包实例和一堆书。所以在这种情况下,背包里有一堆书。我正在将这些实体保存到数据库中。我正在验证那些已保存到数据库中。当我尝试加载背包时,它加载正常,并且设置了除导航属性之外的所有属性。我也在检查是否禁用了 LazyLoading。我的导航属性有 virtual 关键字。 我不确定我做错了什么。如果我尝试使用 Include() 加载背包,它会加载书籍:
dbContext.Backpacks.Where(b=>b.Name!="").Include("Books").FirstOrDefault()
我想弄清楚为什么它不延迟加载书籍?我在加载这本书时遇到了同样的问题。当我装书时,它没有附上背包。我看到 BackpackId 在那里。
在我的 属性 getter/setter 中,我有一些逻辑会被触发,但我不确定这怎么会是个问题。
预先加载是使用 Include()
方法实现的,因此您使用 Include("Books")
.
改变这个:
dbContext.Backpacks.Where(b=>b.Name!="").Include("Books").FirstOrDefault()
对此:
dbContext.Backpacks.Where(b=>b.Name!="").FirstOrDefault()
您现在应该看到 Books
不再急切加载。
参考:
由于手头资料有限,我可以看到以下针对您的问题的解释。
延迟加载或代理创建被禁用
确保启用了延迟加载和代理创建,如果没有后者,前者将无法工作。
dbContext.Configuration.ProxyCreationEnabled = true;
dbContext.Configuration.LazyLoadingEnabled = true;
(详见this SO post)
配置上下文后访问实体
简化后的延迟加载是这样的:
- 每当您从上下文中检索一个实体时,您实际上会得到一个自动创建的 class 子 class 的对象,您期望它会覆盖您的
virtual
导航属性,它是代理. - 每当您随后访问所述导航属性时,代理将访问数据库并在需要时加载链接的实体。
当然,这最后一步只有在 entity/proxy 仍然 附加 到上下文时才有可能,因此可以查询数据库以检索所述对象。
using( var dbContext = new MyContext() )
{
dbContext.Configuration.ProxyCreationEnabled = true;
dbContext.Configuration.LazyLoadingEnabled = true;
// Note: You can modify or derive from `MyContext` so that this is
// done automatically whenever a new `MyContext` is instantiated
var backpack = dbContext.Backpacks.Where(b=>b.Name!="").FirstOrDefault();
// This should work
var firstBook = backpack.Books.FirstOrDefault();
}
// This will probably not, because the context was already disposed
var firstDrink = backpack.Drinks.FirstOrDefault();
希望对您有所帮助,如果没有帮助,请随时提供更多信息
调试了几天,终于找到问题所在了。如上所述,您必须启用 LazyLoading 和 ProxyCreating。即使启用了 LazyLoading 和 ProxyCreating,我也遇到了问题。还要确保将导航属性声明为 virtual
,否则 EF 将无法延迟加载实体。
所以我遇到的问题是,EF 没有创建代理,因为我的实体没有 public 或没有参数的受保护构造函数。在创建没有参数的 public (在我的例子中是受保护的)构造函数之后它起作用了。
注意: 没有 public/protected 不带参数的构造函数不会影响预先加载。
Here is a link that explains the requirements for the LazyLoading
我必须使用 .NET Core 3.1 和 Microsoft.EntityFrameworkCore 3.1.5...
执行的步骤1) 添加 Microsoft.EntityFrameworkCore.Proxies
NuGet 包
2) 将您的 DbContext
配置为 UseLazyLoadingProxies
(在 Startup.cs
中)
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<DataContext>(optionsBuilder =>
{
optionsBuilder
.UseLazyLoadingProxies() // <--- You need this bit
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
...
}
3) 将所有合适的属性(那些你想要延迟加载的)标记为 virtual
public class MyEntity
{
public virtual OtherEntity? { get; set; } // Lazy loaded coz `virtual`
public ICollection<OtherEntity> { get; set; } // NOT lazy loaded coz not `virtual`
}