我有一個使用物體框架的 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");
}
}
然后我有一個控制器,它只需要加載我在我的資料庫中擁有的所有客戶,以及他們的地址。
為此,我有這個:
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,我如何仍然可以訪問客戶的地址,以便我可以將它們回傳到視圖
uj5u.com熱心網友回復:
在使用 EF 時,我的一般建議是不應在其 DbContext 范圍之外參考物體。您當然可以使用分離的物體,但您必須尊重它們從離開 DbContext 范圍的那一刻起就以“原樣”狀態提供。出于這個原因,我建議在 DbContext 范圍之外傳遞的任何內容都應該是 POCO ViewModel 或 DTO 類,以避免混淆物體類實際上是功能性的、表示資料域狀態的附加物體,還是分離的外殼。
選項 1:處理 DTO/ViewModel。
public IActionResult sendhere()
{
using (var db = new DemoContext()) {
var customerDTOs = db.Customers
.Select(c => new CustomerDTO
{
// fill in the fields you want here.
Addresses = c.Addresses.Select(a => new AddressDTO
{
// fill in the address fields you want here.
}).ToList()
}).ToList();
return View(customerDTOs);
}
}
您也可以利用 Automapper,設定所需的投影規則并使用ProjectTo<CustomerDTO>(config)來替換Select()上面的使用。
在利用投影時,您根本不需要延遲加載代理。這可以說已成為 EF 推薦的事實上的方法。
投影方法的優點是這些 DTO(或 ViewModel)不能與物體混淆。分離物體的問題在于,如果代碼中有可能接受物體的方法,這些方法可能期望獲取物體并訪問未加載的成員。如果它們被附加并且在 DbContext 的范圍內,則這些成員可以被延遲加載(出于性能原因,這不是理想的,但功能性)但是如果它們被分離,則會出現錯誤或 NullRefExceptions。投影的另一個優點是從資料庫中提取并發送到視圖邏輯或端的資料的有效負載僅包含所需的資料。
選項 2:不要取消 DbContext 的范圍。對于像 ASP.Net MVC Web 應用程式這樣的專案,您可以利用 IoC 容器將依賴項注入到您的控制器中。通過這種方式,您可以將 DbContext 設定為注入到建構式中,并將生命周期范圍設定為請求。這樣,對于任何給定的請求,您可能呼叫的所有服務/類都可以由容器管理并可以訪問 DbContext。
public class SomeController
{
private readonly DemoContext _context;
public SomeController(DemoContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public IActionResult sendhere()
{
var customers = _context.Customers
.Include(c => c.Address)
.ToList();
return View(customers);
}
}
這可以與選項 1 結合使用,以避免需要為每個請求/操作限定 DbContext 的范圍,并更好地促進您可能希望對 DbContext 進行多次呼叫并確保使用相同的背景關系實體的情況。對于 IoC 容器,有許多不同的可用容器,我相信 ASP.Net Core 帶有一個默認容器,盡管我個人使用并推薦 Autofac。它有很好的檔案和示例,說明如何將它與 MVC 專案聯系起來。
選項 3:急切加載您需要參考的所有內容。您提供的示例實際上應該可以作業,但是您的真實代碼可能缺少所需關系的急切加載 ( .Include()),因為您的示例不會嘗試對您加載的客戶集合做任何事情。
如果您的代碼是:
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.Include(c => c.Address)
.ToList();
}
var firstAddressId = customers.FirstOrDefault()?.Address.Id;
這應該可以作業,因為地址是急切加載的。但是,如果您有:
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.ToList();
}
var firstAddressId = customers.FirstOrDefault()?.Address.Id;
...沒有Include(c => c.Address),那么它將因該錯誤而失敗。
使用 EF Core,如果您想要回傳 DbContext 范圍之外的物體并且您啟用了延遲加載代理,您將需要暫時關閉代理創建以避免代理錯誤。在這種情況下,您不急于加載的任何內容都將保留為 #null 或默認值。
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
db.ContextOptions.ProxyCreationEnabled = false;
customers = db.Customers
.Include(c => c.Address)
.ToList();
}
Return View(customers);
這應確保 EF 不對 DbContext 實體范圍內的查詢使用代理,當您想要傳遞 DbContext 范圍之外的物體時應避免使用代理。當您知道不需要急切加載每個參考的開銷時,這可能很有用。但是,在這種情況下使用投影(選項 1)要好得多,以避免將來混淆物體是否實際上可能具有 #null 資料,或者僅僅是它不是急切加載的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/494607.html
