Entity framework 4 的ForeignKey之我見
在Entity framework 4 中新增了一個新功能就是ForeignKey的支援.
但許多人誤會了ForeignKey這項新功能的用意,在此做個說明.
很多人認為EF4中ForeignKey的目的是為了解決EF於LINQ中Join的一些問題,但實際上並不是如此.
因為EF到目前為止還是著重在ORM,ForeignKey的支援是為了解決一個以往在ORM中的問題.
下圖為Northwind資料庫中的產品與類別的對應關係
在EF 1.0版中,如果要新增一個產品透過DataContext類別時必須先Query一次Category Table然後將取得的Category物件指定給Product的Category屬性.譬如底下程式.
18 using (NorthwindEntities dc = new NorthwindEntities())
19 {
20 int categoryid = 1;
21 Category c = dc.Categories.First(c => c.Categoryid == categoryid); //先取得Category物件
22 Product p = new Product();
23 p.Category = c;//將NCategory物件指派給Product
24
25 dc.Products.AddObject(p);
26 dc.SaveChanges();
27 }
由上述程式可得知,程式必須先取得Category物件後Product才能新增.但對於關聯式資料庫而言這動作是沒意義的因為在資料庫中只要指定Product Table的CategoryId即可.
所以到了EF4後透過ForeignKey的支援就可將程式變為如下
32 using (NorthwindEntities dc = new NorthwindEntities())
33 {
34 int categoryid = 1;
35 Product p = new Product();
36 p.Categoryid = categoryid;
37
38 dc.Products.AddObject(p);
39 dc.SaveChanges();
40 }
如同新增在資料庫中新增直接Product Table一樣,直接指定Categoryid即可.
在物件導向程式撰寫中
新增一個Product物件這樣做是合理的
Category c = new Category();
Product p = new Product();
p.Category = c;
反而如果多了ForeignKey就會顯得奇怪.
Product p = new Product();
p.Categoryid = 1;
會產生這種問題就出在關聯式資料庫的描述方式與程式中物件的描述方式並不相同.
所以EF4中的ForeignKey並不是用來在程式中作為操作的屬性.
最常見的在LINQ中就是透過ForeignKey來作Join動作
譬如
var q = from p in dc.Products
join c in dc.Categories on p.Categoryid equals c.Categoryid
where c.Categoryid == 1
select new { p, c };
正確的做法應該是
var q = from p in dc.Products
where p.Category.Categoryid == 1
select new { p, c = p.Category };
也就是說實際程式在操作不應該透過ForeignKey來存取資料,因為在OO中這種存取方式很奇怪,事後在維護上會產生一些問題.
站在OO的觀點應該以物件為出發點而不應該是資料庫的角度.(可能有人會認為考慮效能問題所以必須這樣做,但問題通常都可以解決的).
因此在ForeignKey使用上以目前我的經驗作法是,我會將ForeignKey的屬性設置為internal也就是供內部組件存取,而不透通至使用端.
而使用端的Insert,Update等動作,會封裝至此組件中,然後提供方法或類別供使用端呼叫,如此使用端就不會濫用ForeignKey.