Lazyloading时,dbcontext以外的访问实体框架关系可以
我使用实体框架有一个ASP.NET应用程序。 我有这两种模型:
public class CustomerModel
{
public int Id{get;set; }
[Required]
public string Name {get;set; }
[Required]
public string Surname { get; set; }
[Required]
[Range(18,110)]
public uint Age { get; set; }
[Required]
public virtual AdressModel Adress { get; set; }
[Required]
public DateTime Created { get; set; }
}
以及
public class AdressModel
{
public int Id { get; set; }
[Required]
public int HouseNumber { get; set; }
[Required]
public string Town { get; set; }
[Required]
public string ZipCode { get; set; }
[Required]
public string Country { get; set; }
}
一个看起来像这样的DBContext类:
public class DemoContext : DbContext
{
public DbSet<CustomerModel> Customers { get; set; }
public DbSet<AdressModel> Adresses { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseLazyLoadingProxies();
options.UseSqlite(@"Data Source=/home/ask/RiderProjects/Parkz/identifier.sqlite");
}
}
然后我有一个控制器,只需加载我在数据库中拥有的所有客户及其Adresses即可。
为此,我有一个:
public IActionResult sendhere()
{
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.Include(c => c.Adress)
.ToList();
}
return Content("hi");
}
我试图进行一些调试。
问题是,一旦我退出“使用”块,所有相关的地址对象仅包含此错误:
System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.LazyLoadOnDisposedContextWarning': An attempt was made to lazy-load navigation 'CustomerModelProxy.Adress' after the associated DbContext was disposed. This exception can be suppressed or logged by passing event ID 'CoreEventId.LazyLoadOnDisposedContextWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition`2.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, TParam1 arg1, TParam2 arg2)
at Microsoft.EntityFrameworkCore.Diagnostics.CoreLoggerExtensions.LazyLoadOnDisposedContextWarning(IDiagnosticsLogger`1 diagnostics, DbContext context, Object entityType, String navigationName)
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.LazyLoader.ShouldLoad(Object entity, String navigationName, NavigationEntry& navigationEntry)
at Microsoft.EntityFrameworkCore.Infrastruc…
这是因为我退出了上下文。
即使我退出了dbcontext,我仍然如何访问客户的地址,因此我可以将它们返回到视图
I have a asp.net application using entity framework.
I have these two models:
public class CustomerModel
{
public int Id{get;set; }
[Required]
public string Name {get;set; }
[Required]
public string Surname { get; set; }
[Required]
[Range(18,110)]
public uint Age { get; set; }
[Required]
public virtual AdressModel Adress { get; set; }
[Required]
public DateTime Created { get; set; }
}
and
public class AdressModel
{
public int Id { get; set; }
[Required]
public int HouseNumber { get; set; }
[Required]
public string Town { get; set; }
[Required]
public string ZipCode { get; set; }
[Required]
public string Country { get; set; }
}
And a dbcontext class that looks like this:
public class DemoContext : DbContext
{
public DbSet<CustomerModel> Customers { get; set; }
public DbSet<AdressModel> Adresses { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseLazyLoadingProxies();
options.UseSqlite(@"Data Source=/home/ask/RiderProjects/Parkz/identifier.sqlite");
}
}
and then I have a controller that just needs to load all the customers that I have in my database, and their adresses.
For that purpose I have this:
public IActionResult sendhere()
{
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.Include(c => c.Adress)
.ToList();
}
return Content("hi");
}
Which I have tried to debug a bit.
The issue is that as soon as I exit my "using" block, all the related adress objects only consist of this error:
System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.LazyLoadOnDisposedContextWarning': An attempt was made to lazy-load navigation 'CustomerModelProxy.Adress' after the associated DbContext was disposed. This exception can be suppressed or logged by passing event ID 'CoreEventId.LazyLoadOnDisposedContextWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition`2.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, TParam1 arg1, TParam2 arg2)
at Microsoft.EntityFrameworkCore.Diagnostics.CoreLoggerExtensions.LazyLoadOnDisposedContextWarning(IDiagnosticsLogger`1 diagnostics, DbContext context, Object entityType, String navigationName)
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.LazyLoader.ShouldLoad(Object entity, String navigationName, NavigationEntry& navigationEntry)
at Microsoft.EntityFrameworkCore.Infrastruc…
Which is because I exited the context.
How do I still get to access the adresses of my customers, even though I exited the Dbcontext, so I can return them to a view
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
与EF合作时,我的一般建议是,不应在其dbcontext范围之外引用实体。当然,您可以与独立实体合作,但是您必须尊重它们从离开DBContext的范围开始以“按原样”状态提供。因此,我建议在DBContext范围之外通过的任何内容都应该是Poco ViewModel或DTO类,以避免混淆实体类是实际上代表数据域状态还是独立的外壳的功能性的,附件的实体。
选项1:处理DTO/ViewModels。
您也可以利用自动应用程序,设置所需的投影规则,并使用
projectto&lt; customerdto&gt;(config)
替换上述select()
的使用。利用投影时,您根本不需要懒惰的代理。可以说,这已成为DefaTso推荐的EF方法。
投影方法的优点是这些DTO(或ViewModels)不能与实体混淆。独立实体的问题是,在可能接受实体的代码中使用方法的地方,这些方法可能希望获得未加载的实体和访问成员。如果它们是附加的,并且在DBContext的范围内,则可以将这些成员延迟加载(由于性能原因不是理想的选择,但功能性),但是,如果它们分离,则会出现错误或NullReFexceptions。投影的另一个优点是从数据库中提取的数据的有效载荷并发送到视图逻辑或END仅由所需的数据组成。
选项2:不要删除DBContext。借助ASP.NET MVC Web应用程序之类的项目,您可以利用IOC容器为控制器提供依赖性注入。通过这种方式,您可以设置DBContext将其注入构造函数,并将寿命范围设置为请求。这样,对于任何给定的请求,您可能调用的所有服务/类都可以由容器管理并可以访问DBContext。
可以将此与选项1结合使用,以避免在每个请求/操作中范围内确定DBContext,并更好地促进您可能希望针对DBContext进行多个调用,并确保使用相同的上下文实例。对于IOC容器,有许多可用的容器,我相信ASP.NET Core带有默认的容器,尽管我个人使用并推荐AutoFac。它具有良好的文档和示例,介绍了如何将其与MVC项目联系起来。
选项3:渴望加载您需要参考的所有内容。您提供的示例实际上应该有效,但是您的真实代码可能缺少急切的载荷(
.include()
),以给定您的示例,您的示例并没有尝试与客户收集您加载。如果您的代码确实如此:
这应该可以随着地址的急切加载而起作用。但是,如果您有:
...没有
include(c =&gt; c.Address)
,则该错误将失败。使用EF Core,如果您要在DBContext范围之外返回实体,并且启用了懒惰的加载代理,则需要暂时关闭代理创建,以避免代理错误。在这种情况下,您不急切的负载将留下#null或默认值。
这应该确保EF在该DBContext实例范围内不使用代理来查询,每当您想在DBContext范围之外传递实体时,应避免使用代理。当您知道您不需要急切加载每个参考的开销时,这可能很有用。但是,在这种情况下,使用投影(选项1)要好得多,以避免将来围绕实体是否实际具有#null数据的混淆,或者仅仅没有渴望加载。
My general advice when working with EF is that entities shouldn't be referenced outside of the scope of their DbContext. You certainly can work with detached entities, but you have to respect that they are provided in an "as-is" state from the moment they leave the scope of the DbContext. For that reason I recommend that anything passed outside of the scope of the DbContext should be a POCO ViewModel or DTO class to avoid confusing whether an entity class is actually a functional, attached entity representing data domain state, or a detached shell.
Option 1: Deal with DTO/ViewModels.
You can alternatively leverage Automapper, set up the desired projection rules and use
ProjectTo<CustomerDTO>(config)
to replace the use ofSelect()
above.When leveraging projection, you do not need lazy loading proxies at all. This has arguably become the defacto recommended approach for EF.
The advantages of the projection method are that these DTOs (or ViewModels) cannot be confused with being entities. The issue with detached entities is that where you have methods in your code that might accept entities, these methods might expect to get entities and access members that aren't loaded. If they are attached and within the scope of a DbContext, those members can be lazy-loaded (not ideal for performance reasons, but functional) however if they are detached you get errors or NullRefExceptions. The other advantage of projection is the payload of data being pulled from the database and sent to the view logic or end consists of just the data needed.
Option 2: Don't de-scope the DbContext. With projects like ASP.Net MVC web applications, you can leverage an IoC Container to provide dependency injection into your Controllers. In this way you can set up the DbContext to be injected into the constructor with a lifetime scope set to the Request. In this way, for any given request, all services/classes you might call can be managed by the container and have access to the DbContext.
This can be combined with Option 1 to avoid needing to scope a DbContext with each request/action and better facilitate situations where you may want to make multiple calls against the DbContext and ensure the same context instance is used. For IoC containers there are a number of different ones available, and I believe ASP.Net Core comes with a default one, though I personally use and recommend Autofac. It has good documentation and examples on how to wire it up with MVC projects.
Option 3: Eager load everything you're going to need to reference. The example you provided should actually work, but your real code is likely missing an eager-load (
.Include()
) for the desired relationship given your example doesn't attempt to do anything with the Customers collection you load.If your code does:
This should work as Addresses was eager loaded. However, if you had:
... without the
Include(c => c.Address)
, then it would fail with that error.With EF Core if you are going to want to return entities outside of the scope of a DbContext and you have lazy loading proxies enabled, you will want to temporarily turn off the proxy creation to avoid proxy errors. In this case anything you don't eager load will be left #null or default.
This should ensure that EF doesn't use proxies for the queries in the scope of that DbContext instance which should be avoided whenever you want to pass entities outside of the scope of the DbContext. This can be useful when you know you won't need the overhead of eager loading every reference. However, it is much better to use projection (Option 1) in this case to avoid future confusion around whether entities might actually have #null data, or merely it wasn't eager loaded.