[英]Using Entity Framework, how can I add a foreign key on two models to reference each other
[英]How should I disable Entity Framework table reference(foreign) list from each objects?
我正在使用Sqlite 數據庫和System.Data.SQLite 1.0.92這里有 2 個表:
表人:
人名
人名
表學生:
學生卡
PersonId(參考表Person FK)
學生號
現在每次我在 EF5 中獲得 Persons 集合時:
using (var ctx = new myEntities)
{
AllPersons = ctx.Persons.ToList();
}
也有AllPersons.student集合會包含在結果中;
但我不需要它。 當然這只是一個例子,有很多大表有這么多引用,因此它總是有性能問題。
所以我試圖不讓它出現在我的結果中。 所以我改變它:
using (var ctx = new myEntities)
{
ctx.Configuration.ProxyCreationEnabled = false;
ctx.Configuration.LazyLoadingEnabled = false;
AllPersons= ctx.Persons.ToList();
}
現在很好,因為AllPersons.student
集合將始終為null
但現在我發現:如果我把 Person 和 Student放在一起:
using (var ctx = new myEntities)
{
ctx.Configuration.ProxyCreationEnabled = false;
ctx.Configuration.LazyLoadingEnabled = false;
AllPersons= ctx.Persons.ToList();
AllStudents = ctx.Student.ToList();
}
現在參考仍然包含在。
那么在這種情況下,無論如何不要讓參考包含在任何時候? 謝謝你。
更新
對於一些朋友的要求,我解釋一下為什么我需要它:
1:當我將其轉換為 json 時,它將是一個死循環。 甚至我已經使用 Json.net ReferenceLoopHandling
,json 大小非常大,導致服務器崩潰。(如果沒有引用,它只是一個非常小的 json)
2:每次獲取客戶端數據需要保存時,都會顯示模型狀態異常,直到我將其設置為null
。
例子:
using (myEntities ctx = new myEntities())
{
ctx.Configuration.LazyLoadingEnabled = false;
ctx.Configuration.ProxyCreationEnabled = false;
Person model= ThisIsAModel();
model.students = null; // This is a key, I need set the students collection references to null , otherwise it will throw exception
ctx.Entry(model).State = EntityState.Modified;
ctx.SaveChanges();
}
3:這是更重要的問題。 我已經在服務器上獲取了所有數據和緩存。 但它會讓服務器啟動時加載時間很長。 (因為數據和參考文獻太多了,這才是主要問題),不知道又會遇到什么樣的問題....
public List<Person> PersonsCache; // global cache
public List<Student> StudentsCache; // global cache
using (myEntities ctx = new myEntities())
{
ctx.Configuration.LazyLoadingEnabled = false;
ctx.Configuration.ProxyCreationEnabled = false;
// There is so many references and data, will let it very slow , when I first time get the all cache. even I only get the Person model, not other , just because some Collection has some references problem. It will very slow....
PersonsCache = ctx.Persons.ToList();
StudentsCache= ctx.Student.ToList();
}
問題
正如您所說,當您加載父和子列表時,即使禁用了 LazyLoading,然后查看 parent.Childs,您也會看到子項也已加載。
var db = new YourDbContext();
db.Configuration.LazyLoadingEnabled = false;
var parentList= db.YourParentSet.ToList();
var childList= db.YourChildSet.ToList();
發生了什么? 為什么孩子包含在父母中?
父實體下的子項是您使用db.YourChildSet.ToList();
加載的子db.YourChildSet.ToList();
正是他們自己; 事實上,Entity Framework 永遠不會再次為父級加載子級,但由於 edmx 中父級和子級之間的關系,它們被列在那里。
那會影響性能嗎?
根據childs只加載一次的事實,不會因為加載數據而影響性能。
但是為了序列化或其他原因,我該如何擺脫它?
您可以使用這些解決方案:
解決方案1:
使用 2 個不同的 YourDbContext 實例:
var db1 = new YourDbContext();
db1.Configuration.LazyLoadingEnabled = false;
var parentList= db.YourParentSet.ToList();
var db2 = new YourDbContext();
db2.Configuration.LazyLoadingEnabled = false;
var childList= db.YourChildSet.ToList();
解決方案2:
使用投影並根據您的意願塑造您的輸出並使用它們。
var db1 = new YourDbContext();
db1.Configuration.LazyLoadingEnabled = false;
var parentList= db.YourParentSet
.Select(x=>new /*Model()*/{
Property1=x.Property1,
Property2=x.Property2, ...
}).ToList();
其他資源
作為使用實體框架的開發人員,強烈建議閱讀這些資源:
我將專注於您的第三個問題,因為這似乎是您最緊迫的問題。 然后我會嘗試對另外兩個問題給出一些提示。
您應該注意兩個實體框架功能:
當您將數據加載到上下文中時,實體框架將嘗試在對象關聯的任何位置連接對象。 這稱為關系修正。 你不能阻止 EF 這樣做。 因此,如果您分別加載Persons
和Students
, Person
的Students
集合將包含學生,即使您沒有Include()
他們。
默認情況下,上下文緩存它從數據庫中獲取的所有數據。 此外,它將有關對象的元數據存儲在其更改跟蹤器中:它們的各個屬性和所有關聯的副本。 因此,通過加載許多對象,內部緩存會增加,但元數據的大小也會增加。 並且不斷運行的關系修復過程變得越來越慢(盡管通過關閉自動更改檢測可能有助於推遲它)。 總而言之,上下文變得臃腫和緩慢,就像一頭松弛的犀牛。
我了解您希望為每個實體在單獨的集合中緩存數據。 兩個簡單的修改將使這更快:
AsNoTracking
獲取數據來更改跟蹤。這樣做,您的代碼將如下所示:
public List<Person> PersonsCache;
public List<Student> StudentsCache;
using (myEntities ctx = new myEntities())
{
ctx.Configuration.ProxyCreationEnabled = false;
PersonsCache = ctx.Persons
.AsNoTracking()
.ToList();
}
using (myEntities ctx = new myEntities())
{
ctx.Configuration.ProxyCreationEnabled = false;
StudentsCache= ctx.Student
.AsNoTracking()
.ToList();
}
關閉ProxyCreationEnabled
的原因是您將獲得輕量級對象,並且之后您永遠不會無意中觸發延遲加載(拋出上下文不再可用的異常)。
現在,您將擁有不相互關聯的緩存對象,並且可以像使用 EF 一樣快速獲取這些對象。 如果這還不夠快,您將不得不求助於其他工具,例如 Dapper。
順便說一句,你的第一個代碼片段和問題描述......
using (var ctx = new myEntities) { AllPersons = ctx.Persons.ToList(); }
也有 AllPersons.student 集合會包含在結果中;
...建議實體框架自發地執行(學生)的熱切加載,而無需Include
-ing 他們。 我必須假設您的代碼片段不完整。 EF 永遠不會自動執行預先加載。 (除非,也許,您有一些古怪且有問題的查詢提供程序)。
至於第一個問題,序列化。 您應該能夠以如上所示的類似方式解決該問題。 只需加載要單獨序列化的數據並禁用代理創建。 或者,正如其他人所建議的,序列化完全包含您需要的視圖模型或匿名類型。
至於第二個問題,驗證異常。 我只能想象如果您默認初始化學生集合,空的Student
對象會發生這種情況。 這些肯定是無效的。 如果不是這種情況,我建議您就這個特定問題提出一個新問題,顯示有關所涉及的類和映射的足夠詳細信息。 不應該在這個問題中處理。
明確選擇要從數據庫返回的內容。
使用Select new
。 使用select new
子句,您可以創建匿名類型的新對象作為查詢的結果,並且不要讓引用包含在其中。此語法允許您構造匿名數據結構。 這些是在評估(懶惰)時創建的。 像這樣:
using (var ctx = new myEntities())
{
var AllPersons = ctx.People.Select(c => new {c.PersonId, c.PersonName}).ToList();
}
甚至您也不再需要禁用延遲加載。
運行上面的查詢后:
此查詢當前使用select new { }
分配匿名類型,這需要您使用var
。 如果要分配已知類型,請將其添加到 select 子句中:
private IEnumerable<MyClass> AllPersons;//global variable
using (var ctx = new myEntities())
{
AllPersons = ctx.People
.Select(c => new MyClass { PersonId = c.PersonId, PersonName = c.PersonName }).ToList();
}
和:
public class MyClass
{
public string PersonId { get; set; }
public string PersonName { get; set; }
}
如果實體是自動生成的,則將其復制粘貼到自己的代碼中並刪除生成的關系,如子集合和外鍵。 或者你不需要所有這些功能,可以使用像dapper這樣的輕量級框架
通常,您的學生收藏不會從數據庫中填充。 當您到達財產時,它已滿。 此外,如果您使用 ToList() 方法,那么實體框架將從數據中讀取數據以填充您的集合。
請檢查這個。 https://msdn.microsoft.com/en-us/data/jj574232.aspx#lazy https://msdn.microsoft.com/en-us/library/vstudio/dd456846(v=vs.100).aspx
在這種情況下,無論如何不要讓參考文獻包含在任何時候?
解決這個問題的方法似乎很簡單:不要映射關聯。 刪除Student
集合。 我不能再多說了。
如果您使用的是 4.5+,則使用[IgnoreDataMember]
裝飾任何屬性
也聽起來像您正在嘗試進行表繼承,這與 EF 是不同的問題
http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx
如果我理解正確,你只是想確保你只得到你特別要求的東西,對嗎?
上面提到了一點,但要正確執行此操作,您只需選擇一個匿名類型。
var students = from s in _context.Students
select new{
StudentId,
StudentNo};
然后,當你想更新這個集合/對象時,我建議使用 GraphDiff。 GraphDiff 確實有助於解決斷開連接的實體和更新的問題( https://github.com/refactorthis/GraphDiff )
所以你的方法看起來類似於:
void UpdateStudent(Student student){
_context.UpdateGraph(student, map =>
map
.AssociatedEntity(c => c.Person));
_context.SaveChanges();
}
這樣,您就可以更新對象上的任何屬性,無論是否斷開連接,而不必擔心關聯。
這是假設您正確映射了實體,老實說,我發現將對象聲明為屬性(而不僅僅是 ID)並使用映射文件正確映射它更容易。
所以:
class Person{
int Id{get;set;}
string Name{get;set}
}
class Student{
int Id{get;set;}
string StudentNo{get;set;}
Person Person{get;set;}
public class StudentMap : EntityTypeConfiguration<Student>
{
public StudentMap()
{
// Primary Key
HasKey(t => t.Id);
// Table & Column Mappings
ToTable("Students");
Property(t => t.Id).HasColumnName("StudentId");
// Relationships
HasRequired(t => t.Person)
.HasForeignKey(d => d.PersonId);
}
}
希望這是有道理的。 您不需要創建視圖模型,但您絕對可以。 但是,這種方式確實可以更輕松地將斷開連接的項目映射回數據庫。
我有完全相同的情況。 在我要求 Persons.ToList() 之前,我為解決它所做的只是要求 Student.ToList() 我不必禁用延遲加載。 只需要先加載引用其他表的表,然后您就可以加載其他表,並且第一個表結果已經在內存中,並且不會對所有引用進行“修復”。
它們通過EntityKey
自動鏈接到ObjectContext
。 根據您想對Persons
和Students
做什么,您可以將它們從ObjectContext
Detach
出來:
using (var ctx = new myEntities)
{
ctx.Configuration.ProxyCreationEnabled = false;
ctx.Configuration.LazyLoadingEnabled = false;
AllPersons= ctx.Persons.ToList();
foreach(var c in AllPersons)
{
ctx.Detach(c);
}
AllStudents = ctx.Student.ToList();
foreach(var c in AllStudents )
{
ctx.Detach(c);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.