2014-05-08 43 views
15

我們正在使用EF 6.1代碼首次安裝中的一個相當大的模型,我們正在使用實體ID的整數。我們可以使用枚舉作爲類型安全實體ID嗎?

不幸的是,這不像我們想要的那樣安全,因爲可以很容易地混淆id,例如比較不同類型實體(myblog.Id == somePost.Id)或類似實體的id。甚至更糟糕的是:myBlog.Id ++。

因此,我想出了使用輸入ID的想法,所以你不能混淆ID。 所以我們需要一個BlogId類型爲我們的博客實體。現在,顯而易見的選擇是使用包裝在結構中的int,但不能將結構用作關鍵字。而且你不能擴展int ... - 等等,你可以!使用枚舉!

於是我想出了這一點:

public enum BlogId : int { } 
public class Blog 
{ 
    public Blog() { Posts = new List<Post>(); } 
    public BlogId BlogId { get; set; } 
    public string Name { get; set; } 
    public virtual List<Post> Posts { get; set; } 
} 
internal class BlogConfiguration : EntityTypeConfiguration<Blog> 
{ 
    internal BlogConfiguration() 
    { 
     HasKey(b => b.BlogId); 
     Property(b=>b.BlogId) 
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
    } 
} 

所以現在我們有類型安全的標識 - 一個比較和BlogId一個帖子ID是一個編譯時錯誤。 我們不能將3添加到BlogId中。 空的枚舉可能看起來有點奇怪,但更多的是實現細節。 我們必須在我們的映射中顯式設置DatabaseGeneratedOption.Identity選項,但這是一次性工作。

在我們開始將所有的代碼轉換爲這種模式之前,是否有任何明顯的問題?

編輯: 我可能需要澄清爲什麼我們必須首先使用ID而不是完整的實體。有時我們需要匹配EF Linq查詢中的實體 - 並且比較實體在那裏不起作用。例如(建立在博客示例上並假設一個更豐富的域模型):在當前用戶博客條目中查找評論。請記住,我們希望在數據庫中做到這一點(我們有很多數據),並且我們假設沒有直接的導航屬性。並且當前用戶未被連接。幼稚的做法是

from c in ctx.Comments where c.ParentPost.Blog.Author == currentUser 

這不起作用,因爲你不能比較EF Linq中的實體。 所以我們儘量

from c in ctx.Comments where c.ParentPost.Blog.Id == currentUser.Id 

這編譯和運行,但錯誤的 - 它應該是

from c in ctx.Comments where c.ParentPost.Blog.Author.Id == currentUser.Id 

類型安全的IDS會抓住它。而且我們有比這更復雜的查詢。嘗試「查找由當前用戶以後未自己評論的特定其他用戶創建的當前用戶博客條目的評論」。

問候,尼爾斯

+0

這是一個聰明的想法,但我不完全相信。使用O/R映射器通常應避免直接處理ID,所以我不確定可以獲得多少ID。如果您正在構建低層基礎架構(如緩存或分層實體),則更有可能需要處理ID,這可能有助於避免錯誤。另一方面,如果你不得不在一個地方處理不同的實體類型,並且你有不同的密鑰,它也可能成爲你的方式。 –

+0

挑戰在於我們有時會在不同的上下文中重用主數據對象。例如,假設每篇博文都與全局類別列表中的類別相關聯。現在,在保存博客帖子時,我們只想設置帖子的CategoryId而不將類別實體對象附加到上下文 - 尤其是因爲許多帖子可能同時使用同一個類別項目。 –

+1

+1爲聰明! – Jim

回答

3

這是一個有趣的方法,但問題是:這是值得的,並會帶來什麼後果?

你仍然可以做這樣的事情

if ((int)blog.BlogId == (int)comment.CommentId) { } 

個人而言,我會在育人,編寫好的測試投入更多的時間,和代碼審查,而不是嘗試添加某種形式的額外的複雜性是影響你的方式,使用和查詢您的實體。

思考 - 例如:

  • 這是否鑄有對LINQ查詢的性能有什麼影響?
  • 如果您通過WCF的Web API公開您的操作,您將如何執行此操作?
  • 這是否與導航屬性一起工作?
  • 此外,您在可用作主鍵的類型方面受到限制;我不相信這與Guids合作。

一種額外的保護方式是讓你的域層通過接受實體實例而不是ID來處理這些事情。

+0

感謝您的想法,@ L-Three。當然,你仍然可以放棄安全 - 但我試圖防止人們意外地在腳上自我射擊,而不是阻止Moriarty。我不認爲應該有任何性能損失 - 它將全部在數據庫中並且在運行時都是整數。 WCF問題將需要考慮 - 但目前,WCF不是我們的優先考慮事項。從我的測試來看,它在導航方面效果很好。是的,它只適用於int鍵,而不是Guid。 –

+0

它不會影響您使用和查詢實體的方式。所有正確的代碼看起來都像以前一樣 - 可能除了爲單元測試創​​建的虛擬實體之外。 –

+0

我不認爲你可能通過向下轉換這兩種類型而濫用的論點是有效的。你可以說類型安全沒有意義,因爲你仍然可以做'if((object)blog ==(object)comment){}'。這只是讓編譯器在編譯時發現錯誤 – Connell

2

我甚至不熟悉這種用法,但做了一點挖掘,甚至EF團隊都說這是可行的。從在EF枚舉支持他們最初的博客文章,這是上市:

枚舉作爲鍵 此外,枚舉類型的屬性可以參與主鍵,唯一約束和外鍵的定義,以及參與併發控制檢查,並聲明瞭默認值。

來源:http://blogs.msdn.com/b/efdesign/archive/2011/06/29/enumeration-support-in-entity-framework.aspx

我還沒有過這樣做我自己,但報價給了我信心。所以這是可能的,但正如L-Three所暗示的那樣:真正考慮它是否是你想要的(專業&缺點,但聽起來像你已經這樣做了),並測試測試測試!

0

我真的不想揍你,但是如何將類型X的Ids與類型Z的Ids混合? 我從來沒有見過任何像myBlog.Id ++這樣的東西(或者至少不是沒有被解僱)。

總之,這裏是接縫要少的工作,更好地維護(especiall爲DB-管理員)的解決方案:

- 在TypeConfiguration,通過流暢的API創建一個ID(你就會明白爲什麼後來)

- 創建一個抽象基類的所有實體:

*屬性:proteced INT標識

*方法:公衆詮釋getIdValue()

*方法:公共布爾isSameRecord(T otherEntity)其中T:EntityBaseClass

我想第一種方法是不言自明的isSameRecord將你的基類的其他實例,做一個類型檢查第一和如果它通過它,它也會進行身份驗證。


這是一個未經測試的方法,您很有可能無法創建受保護的標識符。

如果不起作用,您可以創建public int _id並告訴您的團隊不要直接使用它。

+0

Hi @Richard。我們遇到的問題是使用Linq到EF查詢時。例如:查找關於當前用戶博客條目的評論 'from c in ctx.Comments where c.ParentPost.Blog.Author == currentUser' 這不起作用,因爲你無法比較實體。 所以我們嘗試 '從c中的ctx.Comments其中c.ParentPost.Blog.Id == c.Author.Id' 這編譯和運行,但失敗 - 它應該是從ctx.Comments中的c ',其中c .ParentPost.Blog.Author == c.Author.Id' Typesafe ID會抓住它。而且我們有比這更復雜的查詢! –

+0

測試會發現這樣的問題。但更好的是,您還應該實施存儲庫,以便像這樣出現錯誤的開發人員不會隨意編寫查詢。 –

0

不知道這會在EF工作,但有一兩件事你可以做的是讓你的實體實施IEquatable<T>

例如您Blog類:

public class Blog : IEquatable<Blog> 
{ 
    // other stuff 
    public bool Equals(Blog other) 
    { 
     return this.Id.Equals(other.Id); 
    } 
} 

或者,您也可以使用更靈活ORM如NHibernate。如果這是有趣的,讓我知道,我會擴大我的答案。

+0

不幸的是,這不適用於EF linq查詢。所以它不會真的幫助我們。 –

0

我知道我有點晚了這個晚會,但是我已經使用這種技術,它肯定能行!

類型安全工程完全按照你的建議。編譯器將捕獲錯誤,如

from c in ctx.Comments where c.ParentPost.Blog.Id == currentUser.Id 

,並阻礙了傻數學。

currentUser.Id++; 
currentUser.Id * 3; 

導航性能還是做工精細也是如此,只要在導航的兩端都是一樣的枚舉類型。

和SQL查詢,因爲它們與int做的只是工作。

這當然是一個有趣的想法!

您使用類型安全的實體ID? - 是的!

應該嗎?我不確定。 EF的設計似乎並不像這樣,而且感覺有點冒險。

相關問題