關於Entity Framework自動關聯查詢與自動關聯更新導航屬性對應的實體注意事項說明


一、首先了解下Entity Framework 自動關聯查詢:

Entity Framework 自動關聯查詢,有三種方法:Lazy Loading(延遲加載),Eager Loading(預先加載),Explicit Loading(顯式加載),其中Lazy Loading和Explicit Loading都是延遲加載。

(注:由於Entity Framework版本的不同,以及采用不同的模式(DB First,Model First,Code First)來構建的Entity,最終導致可能自動關聯查詢的方法也有所不同,本文中的示例代碼均以Entity Framework 6.0,且采用Code First模式來構建的Entity為基礎)

一、Lazy Loading(延遲加載):這是默認情況(默認實體上下文對象的屬性:Configuration.LazyLoadingEnabled=true,若該屬性設為false則無效),若實體類型包含其它實體類型(POCO類)的屬性(也可稱為導航屬性),且同時滿足如下條件即可實列延遲加載,

1.該屬性的類型必需為public且不能為Sealed;

2.屬性標記為Virtual

作用:在您訪問導航屬性時,會從數據源自動加載相關實體,若實體尚未在 實體上下文對象中,則您訪問的每個導航屬性都會導致針對數據源執行一個單獨的查詢。

示例代碼如下:

    [Table("User",Schema="dbo")]
    public class User
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Display(Name = "用戶ID")]
        public int ID { get; set; }

        [Required]
        [MinLength(6)]
        [MaxLength(15)]
        [Unique("User", "UserName")]
        [Display(Name = "用戶名")]
        public string UserName { get; set; }

        [Required]
        [Display(Name = "密碼")]
        public string Password { get; set; }

        [Required]
        [Display(Name = "用戶類型")]
        public int UserTypeID { get; set; }

        [ForeignKey("UserTypeID")]
        public virtual UserType UserType { get; set; } //此屬性在查詢User時,會自動依據UserTypeID 關聯查旬UserType ,並將結果賦值給UserType 屬性
    }

二、Eager Loading(預先加載):在LazyLoadingEnabled設為false或導航屬性沒有使用Virtual的情況下,使用IQueryable的擴展方法Include指定預先加載關聯的實體。

作用:Include方法中的查詢路徑指定將哪些相關實體作為初始查詢的一部分返回,當在查詢語句中定義了Include查詢路徑,查詢時僅需對數據庫請求一次,即可在單個結果集中返回查詢路徑所定義的所有實體。

示例代碼如下:

var entitiesContext=new TEMSContext();
entitiesContext.Configuration.LazyLoadingEnabled=false;
var users = entitiesContext.Users.Include("UserType");
//var users = entitiesContext.Set<User>().Include("UserType");與上面語句相同
Assert.AreNotEqual(null, users.First().UserType);

三、Explicit Loading(顯式加載):在LazyLoadingEnabled設為false或導航屬性沒有使用Virtual的情況下,使用DbEntityEntry.Reference方法來顯式加載指定導航屬性的單個值,DbEntityEntry.Collection方法來顯式加載指定導航屬性的值集合,若采用DB First時,可使用ObjectContext.LoadProperty方法來顯式加載指定導航屬性。

作用:查詢時並不會從數據庫查詢並加載導航屬性的值,僅當使用Reference或Collection方法來指定導航屬性,並調用Load方法時或調用ObjectContext.LoadProperty,才會從數據庫查詢數據並賦值給指定的導航屬性,若查詢的結果集較多時,可能會出現多次往返查詢數據。

示例代碼如下:

//這是Code First模式下顯式加載數據
var entitiesContext = new TEMSContext();
            entitiesContext.Configuration.LazyLoadingEnabled = false;
            var user = entitiesContext.Users.First();
            var u = entitiesContext.Entry(user);
            u.Reference(t => t.UserType).Load();
            Assert.AreNotEqual(null, user.UserType);

//這是DB First模式下顯示式加載數據
var db = new aTestEntities();
            db.ContextOptions.LazyLoadingEnabled = false;
            var comp = db.Companies.First();
            db.LoadProperty(comp, t => t.Departments);
            Assert.AreNotEqual(0, comp.Departments.Count);

二、自動關聯更新導航屬性對應的實體注意事項說明

若開啟了Lazy Loading(延遲加載)模式,即滿足上面第一種關聯查詢條件時,在執行實體新增的時,需注意:如果直接賦值給導航屬性,則當提交到數據庫時,會自動關聯新增導航屬性對應的表記錄,不論你是否在實體中有指定導航屬性對應的外鍵屬性的值,仍會以關聯新增導航屬性對應的表記錄后更新該外鍵屬性的值,可能表達有點不清楚,請看下面的示例:

var entitiesContext = new TEMSContext();
User user = new User() {UserName="testuser",RealName="測試賬號",CompanyID = 1, Company = UserBusiness.CurrentUser.Company };
entitiesContext.Users.Add(user);
entitiesContext.SaveChanges();

 以上代碼中,CompanyID為導航屬性Company的外鍵屬性,我這里直接將兩個屬性都賦值了,Company的ID也是1(Company表中存在ID為1的記錄),UserBusiness.CurrentUser為我系統當前登錄的用戶,按理講,執行新增后,應該只會在User表中新增一條記錄,而實際卻是先在Company表中新增一條記錄(忽略指定的主鍵,即:ID=1),並將返回的新ID賦值給CompanyID(比如為2),然后再在User表中新增一條記錄,最終形成User.CompanyID=2,而不是1,這個問題也是困擾我好幾天了,也沒有找到根本原因,目前的解決辦法是只賦值外鍵屬性CompanyID,而導航屬性Company則不賦值,這樣在保存時就不會出錯了。如果大家知道原因還請告之,非常感謝!

 

來自:zuowj.cnblogs.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM