[英]Caching and lazy loading with entity framework
假設我有一個應用程序,例如一個網站,我的objectcontext在請求期間離開。 我應該緩存一些使用EF加載的數據,以避免讀取數據庫並提高性能。
好吧,我用EF讀取數據,我將對象放在緩存中(說AppFabric,而不是內存緩存),但是可以延遲加載的相關數據現在為空(對此屬性的訪問會導致nullreferenceexception)。 我不想在一個請求中加載所有內容,因為它會太長,所以我希望按需加載,一旦讀取,我想用新獲取的數據完成緩存。
注意 :
我怎樣才能做到這一點 ?
編輯:我用northwind數據庫構建了這個樣本,它正在工作:
class Program
{
static void Main(string[] args)
{
// normal use
List<Products> allProductCached = null;
using (NORTHWNDEntities1 db = new NORTHWNDEntities1())
{
allProductCached = db.Products.ToList().Clone<DbSet<Products>>();
foreach (var product in db.Products.Where(e => e.UnitPrice > 100))
{
Console.WriteLine(product.ProductName + " => " + product.Suppliers.CompanyName);
}
}
// try to use cache, but missing Suppliers
using (NORTHWNDEntities1 db = new NORTHWNDEntities1())
{
foreach (var product in allProductCached.Where(e => e.UnitPrice > 100))
{
if (product.Suppliers == null)
product.Suppliers = db.Suppliers.FirstOrDefault(s => s.SupplierID == product.SupplierID).Clone<Suppliers>();
Console.WriteLine(product.ProductName + " => " + product.Suppliers.CompanyName);
}
}
// try to use full cache
using (NORTHWNDEntities1 db = new NORTHWNDEntities1())
{
foreach (var product in allProductCached.Where(e => e.UnitPrice > 100))
{
Console.WriteLine(product.ProductName + " => " + product.Suppliers.CompanyName);
}
}
}
}
public static class Ext
{
public static List<Products> Clone<T>(this List<Products> list)
{
return list.Select(obj =>
new Products
{
ProductName = obj.ProductName,
SupplierID = obj.SupplierID,
UnitPrice = obj.UnitPrice
}).ToList();
}
public static Suppliers Clone<T>(this Suppliers obj)
{
if (obj == null)
return null;
return new Suppliers
{
SupplierID = obj.SupplierID,
CompanyName = obj.CompanyName
};
}
}
問題是我必須復制所有內容(不丟失屬性)並在屬性為null時測試到處並加載所需的屬性。 我的代碼當然越來越復雜,如果我錯過了某些東西,這將成為一個問題。 沒其他解決方案?
如果沒有ObjectContext
或DbContext
則無法在EF中訪問數據庫。
您仍然可以有效地使用緩存,即使你沒有原始上下文了。
也許您的場景是這樣的......想象一下,您有一些經常使用的參考數據 。 您不希望每次需要時都訪問數據庫,因此將其存儲在緩存中 。 您還擁有不想緩存的每用戶數據 。 您具有從用戶數據到參考數據的導航屬性。 您希望從數據庫加載用戶數據,並讓EF 自動“修復”導航屬性以指向參考數據。
對於請求:
DbContext
。 DbSet.Attach()
) EF中的延遲加載通常使用動態代理來完成。 我們的想法是,您可以創建所有可能動態加載虛擬的屬性。 每當EF創建實體類型的實例時,它實際上替換了派生類型,並且該派生類型在其屬性的重寫版本中具有延遲加載邏輯。
這一切都很好,但在這種情況下,您將實體對象附加到非EF創建的上下文中。 您使用名為Clone
的方法創建了它們。 您實例化了真正的POCO實體,而不是一些神秘的EF動態代理類型。 這意味着您不會在這些實體上進行延遲加載。
解決方案很簡單。 Clone
方法必須使用另一個參數: DbContext
。 不要使用實體的構造函數來創建新實例。 而是使用DbSet.Create()
。 這將返回動態代理。 然后初始化其屬性以創建引用實體的克隆。 然后將其附加到上下文中。
以下是您可能用於克隆單個Products
實體的代碼:
public static Products Clone(this Products product, DbContext context)
{
var set = context.Set<Products>();
var clone = set.Create();
clone.ProductName = product.ProductName;
clone.SupplierID = product.SupplierID;
clone.UnitProce = product.UnitPrice;
// Initialize collection so you don't have to do the null check, but
// if the property is virtual and proxy creation is enabled, it should get lazy loaded.
clone.Suppliers = new List<Suppliers>();
return clone;
}
namespace EFCacheLazyLoadDemo
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
class Program
{
static void Main(string[] args)
{
// Add some demo data.
using (MyContext c = new MyContext())
{
var sampleData = new Master
{
Details =
{
new Detail { SomeDetail = "Cod" },
new Detail { SomeDetail = "Haddock" },
new Detail { SomeDetail = "Perch" }
}
};
c.Masters.Add(sampleData);
c.SaveChanges();
}
Master cachedMaster;
using (MyContext c = new MyContext())
{
c.Configuration.LazyLoadingEnabled = false;
c.Configuration.ProxyCreationEnabled = false;
// We don't load the details here. And we don't even need a proxy either.
cachedMaster = c.Masters.First();
}
Console.WriteLine("Reference entity details count: {0}.", cachedMaster.Details.Count);
using (MyContext c = new MyContext())
{
var liveMaster = cachedMaster.DeepCopy(c);
c.Masters.Attach(liveMaster);
Console.WriteLine("Re-attached entity details count: {0}.", liveMaster.Details.Count);
}
Console.ReadKey();
}
}
public static class MasterExtensions
{
public static Master DeepCopy(this Master source, MyContext context)
{
var copy = context.Masters.Create();
copy.MasterId = source.MasterId;
foreach (var d in source.Details)
{
var copyDetail = context.Details.Create();
copyDetail.DetailId = d.DetailId;
copyDetail.MasterId = d.MasterId;
copyDetail.Master = copy;
copyDetail.SomeDetail = d.SomeDetail;
}
return copy;
}
}
public class MyContext : DbContext
{
static MyContext()
{
// Just for demo purposes, re-create db each time this runs.
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
}
public DbSet<Master> Masters { get { return this.Set<Master>(); } }
public DbSet<Detail> Details { get { return this.Set<Detail>(); } }
}
public class Master
{
public Master()
{
this.Details = new List<Detail>();
}
public int MasterId { get; set; }
public virtual List<Detail> Details { get; private set; }
}
public class Detail
{
public int DetailId { get; set; }
public string SomeDetail { get; set; }
public int MasterId { get; set; }
[ForeignKey("MasterId")]
public Master Master { get; set; }
}
}
這是一個與您的不同的示例模型,它說明了如何使其在原理上正常工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.