使用 Entity Framework 新增一筆資料是極其簡單的事。然而建立相關聯的資料呢?也相當簡單!以下介紹常見的狀況。
DataSchema
在這個範例中,使用最簡單的 master-detail 的資料表。一個是 Order,一個是 OrderDetail。Sql Script 如下
CREATE TABLE [dbo].[Order]( [OrderId] [int] IDENTITY(1,1) NOT NULL, [OrderDate] [datetime] NOT NULL, CONSTRAINT [PK_Order] PRIMARY KEY CLUSTERED ( [OrderId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) go CREATE TABLE [dbo].[OrderDetail]( [DetailId] [int] IDENTITY(1,1) NOT NULL, [OrderId] [int] NOT NULL, [ProductName] [nchar](50) COLLATE Chinese_Taiwan_Stroke_CI_AS NOT NULL, CONSTRAINT [PK_OrderDetail] PRIMARY KEY CLUSTERED ( [DetailId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ) go ALTER TABLE [dbo].[OrderDetail] WITH CHECK ADD CONSTRAINT [FK_OrderDetail_Order] FOREIGN KEY([OrderId]) REFERENCES [dbo].[Order] ([OrderId]) GO ALTER TABLE [dbo].[OrderDetail] CHECK CONSTRAINT [FK_OrderDetail_Order] GO
範例一
以下是最簡單的方法,也是基本上我認為這樣的寫法最適當
class Program { static void Main(string[] args) { AddData1(); ShowData(); } private static void ShowData() { using (Database1Entities context = new Database1Entities()) { var orders = context.Orders.Include("OrderDetails"); foreach (var order in orders) { Console.WriteLine("order id = {0}", order.OrderId); foreach (var d in order.OrderDetails) Console.WriteLine("detail prod={0}", d.ProductName); } } } private static void AddData1() { using (Database1Entities context = new Database1Entities()) { var order = new Order() { OrderDate = new DateTime(2010, 4, 12) }; var detail = new OrderDetail { ProductName = "Prod1" }; order.OrderDetails.Add(detail); context.Orders.AddObject(order); context.SaveChanges(); } } }
要注意的是,我們使用了 order.OrderDetails.Add(detail) 將order 與 detail 的關聯建立起來。接下來使用 context.Orders.AddObject(order) 將 order 新增到 context 中,因此當 context.SaveChanges() 時,context 會查覺到 order 是新增的資料,才會將資料寫到資料庫中。
這樣的方法相當完美,原因在開發人員只需關注物件模型的關係即可,不必知道物件模型關聯的 key 是哪一個 property。
範例二
下面的方法也可以執行。
private static void AddData2() { using (Database1Entities context = new Database1Entities()) { var order = new Order() { OrderDate = new DateTime(2010, 4, 12) }; context.Orders.AddObject(order); context.SaveChanges(); var detail = new OrderDetail { OrderId = order.OrderId, ProductName = "Prod1" }; context.OrderDetails.AddObject(detail); context.SaveChanges(); } }
與範例一不同的是,範例二並不強調 order 與 detail 的物件關聯,而是直接指定 detail 的 OrderId。因此order 與 detail 是分開寫入資料庫的。
範例二就是比較差的寫法了,原因是在開發人員需要了解關聯的property 為何,此例為 OrderDetai 的 OrderId 關聯到 Order 的 OrderId。長久下來,開發人員會習慣以 Data driven 的寫作方式看待 Entity Framework,故不建議這樣做。
範例三
範例三與範例二大致相同
private static void AddData3() { using (Database1Entities context = new Database1Entities()) { var order = new Order() { OrderDate = new DateTime(2010, 4, 12) }; context.Orders.AddObject(order); context.SaveChanges(); var detail = new OrderDetail { Order = order, //這一行換了 ProductName = "Prod1" }; context.OrderDetails.AddObject(detail); context.SaveChanges(); } }
範例三與範例二的概念相同。原來範例二中 detail 是指定 OrderId ,而範例三中 detail 改指定 Order 為 order
只增加 Detail
如果已經知道 master 的 primary key,只想新增 detail 的資料呢?下面的範例四是可以執行的。
範例四
static void Main(string[] args) { AddData1(); AddNewDetail1(); ShowData(); } private static void AddNewDetail1() { using (Database1Entities context = new Database1Entities()) { var order = context.Orders.Where(o => o.OrderId == 1).First(); var detail = new OrderDetail { ProductName = "Prod2" }; order.OrderDetails.Add(detail); context.SaveChanges(); } }
因為物件模型的關係,範例四的做法相當直覺,也相當多的開發人員這樣寫。但是,我們已經知道了 OrderId 為1,範例四會先載入 OrderId 為1的資料並載入記憶體,再進行後續的新增detail 動作。這樣一來,反而效能不彰。可不可以像以前一樣,直接新增一筆 OrderDetail 就好了,不需要載入 Order 啊?
下面的範例五可以在不載入 master (即 order) 到記憶體的情況下增加 detail 資料。
範例五
static void Main(string[] args) { AddData1(); AddNewDetail(); ShowData(); } private static void AddNewDetail() { using (Database1Entities context = new Database1Entities()) { var detail = new OrderDetail { OrderId = 1, //指定了 master 的 key ProductName = "Prod2" }; context.OrderDetails.AddObject(detail); context.SaveChanges(); } }
結論
在 Entity Framework 中,關聯資料的寫法可以有相當多種,但其中的差別需要開發人員注意哦!