簡體   English   中英

我應該如何禁用每個對象的實體框架表引用(外部)列表?

[英]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();
  • 現在,當您查看 parent.Childs 時,其中沒有 Child。

解決方案2:

使用投影並根據您的意願塑造您的輸出並使用它們。

var db1 = new YourDbContext();
db1.Configuration.LazyLoadingEnabled = false;
var parentList= db.YourParentSet
                  .Select(x=>new /*Model()*/{
                      Property1=x.Property1,
                      Property2=x.Property2, ...
                  }).ToList();
  • 這樣在序列化時就沒有什么煩人的了。
  • 使用自定義模型類是可選的,在某些情況下是推薦的。

其他資源

作為使用實體框架的開發人員,強烈建議閱讀這些資源:

我將專注於您的第三個問題,因為這似乎是您最緊迫的問題。 然后我會嘗試對另外兩個問題給出一些提示。

您應該注意兩個實體框架功能:

  1. 當您將數據加載到上下文中時,實體框架將嘗試在對象關聯的任何位置連接對象。 這稱為關系修正 你不能阻止 EF 這樣做。 因此,如果您分別加載PersonsStudentsPersonStudents集合包含學生,即使您沒有Include()他們。

  2. 默認情況下,上下文緩存它從數據庫中獲取的所有數據。 此外,它將有關對象的元數據存儲在其更改跟蹤器中:它們的各個屬性所有關聯的副本。 因此,通過加載許多對象,內部緩存會增加,但元數據的大小也會增加。 並且不斷運行的關系修復過程變得越來越慢(盡管通過關閉自動更改檢測可能有助於推遲它)。 總而言之,上下文變得臃腫和緩慢,就像一頭松弛的犀牛。

我了解您希望為每個實體在單獨的集合中緩存數據。 兩個簡單的修改將使這更快:

  • 通過單獨的上下文加載每個集合來避免不可避免的關系修復
  • 停止緩存(在上下文中)並通過使用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集合。 我不能再多說了。

如果我理解正確,你只是想確保你只得到你特別要求的東西,對嗎?

上面提到了一點,但要正確執行此操作,您只需選擇一個匿名類型。

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 根據您想對PersonsStudents做什么,您可以將它們從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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM