2011-11-25 75 views
6

我們正在構建一個利用NH進行數據訪問的ASP.NET MVC應用程序。使用NH Profiler,我看到很多警告,例如「WARN:將代理縮小到Domain.CaseTask - 此操作中斷==」。執行查詢時,其被映射到每個子類的表格,例如類,使用NH LINQ提供程序我得到這些非常頻繁:NHibernate縮小代理警告

Query<ICaseTask>().Where(c => c.Assignee == Of || c.Operator == Of) 

其中類CaseTask從任務繼承,觸發報警。

有關在互聯網上的警告信息是稀缺的,主要是暗示這是被忽略的東西......這個警告到底警告什麼?這應該是我應該尋求糾正的事情嗎?

回答

2

此警告是關於具有屬性或屬於子類的字段的類。 IE:

public class Animal 
{ 
    public int Id {get;set;} 
} 

public class Cat : Animal 
{ 
    public int Weight {get;set;} 
} 

public class Person 
{ 
    public Cat Pet {get;set;} 
} 

當它加載人體實體時,NHibernate會不高興,因爲它不想爲您施放,因爲行爲變得不可預知。除非你告訴NHibernate如何處理Equals(其他邏輯),否則它不會知道如何自己進行比較。

解決這個問題的基本思路是讓NHibernate將基類對象放入圖中,然後處理轉換(注意,這個設置會使用一些稍微不同的映射 - 這樣做是爲了簡化代碼,但它顯然可以通過保持屬性爲完整的獲取者/設置者來完成):

public class Animal 
    { 
     public int Id {get;set;} 
    } 

public class Cat : Animal 
{ 
    public int Weight {get;set;} 
} 

public class Person 
{ 
    private Animal _pet; 
    public Cat Pet { 
     get{return _pet as Cat;} 
    } 
} 
+0

這是一個大問題?如果我們忽略這個WARN會發生什麼? – Beatles1692

+1

這是一件大事,取決於你願意接受的風險水平。由於您的代碼和數據庫之間始終存在斷開連接,因此您無法始終確保該代碼可以正常工作。這將導致可能難以診斷的錯誤,並且可能無法在不更改數據庫或代碼的情況下解決。 – Fourth

2

現實比較複雜。當使用session.Load加載實體或者訪問延遲加載的屬性時,NHibernate會返回一個代理對象。當您第一次訪問其任何屬性時,該代理對象將通過水合(數據將從數據庫加載)。爲了實現這個NHibernate生成的代理類擴展了實體類並覆蓋了所有屬性獲取器和設置器。當沒有使用繼承時,這是完美的,因爲你無法區分代理和實體類(代理基類),例如簡單測試proxy is MyEntity將始終有效。

現在,假設我們有一個Person實體:

class Person { 
    // lazy-loaded 
    public Animal Pet { get; set; } 
} 

而且我們也有Animal類層次結構:

public abstract class Animal { ... } 
public class Cat { ... } 
public class Dog { ... } 

現在假設Pet屬性是延遲加載,當你問的NHibernate的人寵物你將得到一個代理對象:

var pet = somePerson.Pet; // pet will be a proxy 

但由於Pet是延遲加載的屬性,NH不會知道它是否將是CatDog的實例,所以它將盡其所能,並將創建一個代理來擴展Animal。代理將通過pet is Animal的測試,但將通過pet is Catpet is Dog的測試失敗。

現在假設您將訪問pet對象的某些屬性,迫使NH從數據庫加載數據。現在NH會知道你的寵物是一個Cat但代理已經生成並且不能更改。 這將強制NHibernate發出警告,pet的原代理擴展Animal的範圍將縮小到Cat。這意味着從現在開始動物代理對象pet.Id您使用session.Load<Animal>(pet.Id)創建的將從現在開始擴展Cat。這也意味着,由於Cat現在被存儲爲會話的一部分,如果我們加載與第一個人共享cat的第二個人,NH將使用已有的Cat代理實例來填充延遲加載的屬性。

其中一個後果將是pet的對象引用將不同於session.Load<Animal>(pet.Id)(在object.ReferencesEqual意義上)獲得的引用。

現在,當這可能對你造成危害:

  1. 當你把你的實體爲Set秒或在你的代碼Dictionary IES或者如果您使用需要Equals/GetHashCode對任何其他的工作結構。這可以通過提供定製Equals/GetHashCode實現很容易解決(參見:http://www.onjava.com/pub/a/onjava/2006/09/13/dont-let-hibernate-steal-your-identity.html?page=1

  2. 當您嘗試投你的代理對象爲目標類型例如(Cat)pet,但同樣也有知道的解決方案(例如Getting proxies of the correct type in NHibernate

所以道德是避免儘可能多地繼承你的域模型。