有二年沒關注EF,今天無意試了下發現跟主流的Hibernate等ORM框架越來越接近了,先看下Entity類的定義:

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace EFSample.Model { [Table("T_ORDER")] public class Order { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { set; get; } [StringLength(50)] [Column("CUSTOMER_NAME")] public String CustomerName { set; get; } [Column("AMOUNT")] public decimal Amount { set; get; } public virtual ICollection<OrderItem> OrderItems { set; get; } } }

1 using System; 2 using System.ComponentModel.DataAnnotations; 3 using System.ComponentModel.DataAnnotations.Schema; 4 5 namespace EFSample.Model 6 { 7 [Table("T_ORDER_ITEM")] 8 public class OrderItem 9 { 10 [Key] 11 [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 12 public int ID { set; get; } 13 14 [ForeignKey("Order")] 15 [Column("ORDER_ID")] 16 public int OrderID { set; get; } 17 18 [StringLength(50)] 19 [Column("PRODUCT_NAME")] 20 public String ProductName { set; get; } 21 22 [Column("PRICE")] 23 public Decimal Price { set; get; } 24 25 [Column("QUANTITY")] 26 public int Quantity { set; get; } 27 28 public virtual Order Order { set; get; } 29 30 31 } 32 }
光看Attribute,已經跟Hibernate的Annotation很相似了。
配置文件:

1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <configSections> 4 <section name="entityFramework" 5 type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> 6 </configSections> 7 8 <entityFramework> 9 <contexts> 10 <context type="EFSample.DAL.OrderContext, EFSample" disableDatabaseInitialization="false"> 11 <databaseInitializer type="EFSample.DAL.OrderInitializer, EFSample" /> 12 </context> 13 </contexts> 14 <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> 15 <parameters> 16 <parameter value="v11.0" /> 17 </parameters> 18 </defaultConnectionFactory> 19 <providers> 20 <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> 21 </providers> 22 </entityFramework> 23 24 <startup> 25 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" /> 26 </startup> 27 28 <connectionStrings> 29 <!--連接字符串,使用SQLSERVER LocalDb--> 30 <add name="MyConn" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=OrderDatabase;Integrated Security=SSPI;" providerName="System.Data.SqlClient" /> 31 </connectionStrings> 32 33 </configuration>
注:LocalDb在開發階段很方便,但是試用中有一個小問題,如果把生成的mdf物理文件刪除后,再次運行總是報錯(不知道是不是個別現象),只能把Initial Catalog=OrderDatabase中的文件名改成其它名稱(比如:Catalog=OrderDb),才能正常運行。
此外,NuGet Package Manager工具搞得象java的maven一樣,可以自動聯機下載所需的依賴項,Tools->NuGet Package Manager
輸入
Install -Package EntityFramework
就能自動向project添加相關的dll引用
DbContext

1 using EFSample.Model; 2 using System.Data.Entity; 3 using System.Data.Entity.ModelConfiguration.Conventions; 4 5 namespace EFSample.DAL 6 { 7 public class OrderContext:DbContext 8 { 9 public OrderContext() : base("MyConn") { } 10 11 public DbSet<Order> Orders { set; get; } 12 13 public DbSet<OrderItem> OrderItems { set; get; } 14 15 protected override void OnModelCreating(DbModelBuilder modelBuilder) 16 { 17 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 18 } 19 } 20 }
示例代碼:

1 using EFSample.DAL; 2 using EFSample.Model; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 7 namespace EFSample 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 using (var db = new OrderContext()) 14 { 15 16 //直接執行Sql語句 17 db.Database.ExecuteSqlCommand("delete from t_order"); 18 19 var orders = new List<Order>(){ 20 new Order{ CustomerName="jimmy.yang",Amount=200, OrderItems=new List<OrderItem>(){ 21 new OrderItem(){ Price=10, Quantity=20, ProductName="Mobile"} 22 }}, 23 new Order{ CustomerName="楊俊明",Amount=300, OrderItems=new List<OrderItem>(){ 24 new OrderItem(){ Price=15, Quantity=20, ProductName="架構之美"} 25 }}}; 26 27 //批量添加記錄 28 db.Orders.AddRange(orders); 29 30 //提交到db 31 db.SaveChanges(); 32 33 //查詢 34 var query = db.Orders.Where(c => c.CustomerName == "Jimmy.yang").AsQueryable(); 35 36 //輸出Sql語句 37 Console.WriteLine(query.ToString()); 38 39 List<Order> orderEntities = query.ToList(); 40 41 foreach (var order in orderEntities) 42 { 43 Console.WriteLine(String.Format("ID:{0}/CustomerName:{1}/Amount:{2}/ItemCount:{3}", order.ID, order.CustomerName, order.Amount,order.OrderItems.Count)); 44 } 45 } 46 47 Console.WriteLine("ok!"); 48 Console.Read(); 49 } 50 } 51 }
輸出:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[CUSTOMER_NAME] AS [CUSTOMER_NAME],
[Extent1].[AMOUNT] AS [AMOUNT]
FROM [dbo].[T_ORDER] AS [Extent1]
WHERE N'Jimmy.yang' = [Extent1].[CUSTOMER_NAME]
ID:9/CustomerName:jimmy.yang/Amount:200/ItemCount:1
ok!
對Oracle的支持
MS默認并沒有提供EF對Oracle的支持,需要到Oracle官網下載 http://www.oracle.com/technetwork/topics/dotnet/downloads/index.html
下載比較慢,耐心等待,完成后,一路Next即可。
注意:tnsnames.ora文件的配置,一般在x:\app\client\Administrator\product\12.1.0\client_1\Network\Admin 目錄下,參考內容如下:
1 XE = 2 (DESCRIPTION = 3 (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.187.128)(PORT = 1521)) 4 (CONNECT_DATA = 5 (SERVER = DEDICATED) 6 (SERVICE_NAME = XE) 7 ) 8 )
然后在vs中創建ADO.NET Entity Data Model時就能連接到Oracle db了,目前尚不支持Code-First,只能使用EF5,估計EF6要等明年才會正式發布
使用細節,可參考官網教程:http://www.oracle.com/webfolder/technetwork/tutorials/obe/db/dotnet/EntityFrameworkOBE/EntityFrameworkOBE.htm#t10
Sequence的處理:
Oracle中沒有自增主鍵,msdn和oracle建議用trigger+sequence來處理自增主鍵的問題,即:insert前寫個觸發器,檢測主鍵是否為空,如果為空,則將sequece.nextval賦值給它,參見:https://social.msdn.microsoft.com/Forums/th-TH/73453344-bbb9-4904-b77a-a9ba807dcfd2/oracle-e-entityframework?forum=mvcpt
示例代碼:
1 create sequence S_CD_TIPO minvalue 1 maxvalue 9999999 start with 1 increment by 1; 2 3 create or replace trigger tp_procedimento_trigger 4 before insert on tp_procedimento for each row 5 begin 6 if :new.cd_tipo is null then select s_cd_tipo.nextval into :new.cd_tipo from dual; 7 endif; 8 end;
但我個人并不喜歡這種做法,感覺對db有點“侵入”,我比較喜歡在c#代碼層掌控一切的感覺,如果大家跟我有一樣的癖好,可以這么干:
1 using System; 2 using System.Data; 3 using System.Data.Entity; 4 5 namespace EntityFrameworkTest 6 { 7 public static class SequenceHelper 8 { 9 public static int GetNextVal(this DbContext db,String sequenceName) { 10 if (db.Database.Connection.State != ConnectionState.Open) { 11 db.Database.Connection.Open(); 12 } 13 var cmd = db.Database.Connection.CreateCommand(); 14 cmd.CommandText = String.Format("select {0}.nextval from dual", sequenceName); 15 cmd.CommandType = CommandType.Text; 16 17 int result = 0; 18 int.TryParse(cmd.ExecuteScalar().ToString(),out result); 19 20 return result; 21 } 22 } 23 }
對DbContext寫一個擴展方法,手動傳入Sequence名稱,然后在添加記錄時,這樣用:
1 using System; 2 using System.Linq; 3 4 namespace EntityFrameworkTest 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 using (MyDbContext db = new MyDbContext()) 11 { 12 //insert 13 T_BAS_AGENT newAgent = new T_BAS_AGENT(); 14 newAgent.AGENT = "XYZ"; 15 newAgent.CYEAR = 2014; 16 newAgent.RECID = db.GetNextVal("SEQ_T_BAS_CARRIER"); 17 db.T_BAS_AGENT.Add(newAgent); 18 db.SaveChanges(); 19 20 //query 21 var findAgent = db.T_BAS_AGENT.SingleOrDefault(c => c.RECID == newAgent.RECID); 22 Console.WriteLine(string.Format("{0}/{1}/{2}", findAgent.RECID, findAgent.AGENT, findAgent.CYEAR)); 23 24 } 25 26 Console.Read(); 27 } 28 } 29 }
文章列表