EF 的安裝
-
基礎階段用控制檯項目。使用NuGet 安裝EntityFramework。會自動在App.config中增加兩個entityFramework 相關配置段;
-
在 web.config 中配置連接字符串
<add name="conn1" connectionString="Data Source=.;Initial Catalog=test1;UserID=sa;Password=123" providerName="System.Data.SqlClient" />
易錯點:不能忘了寫providerName="System.Data.SqlClient"增加兩個entityFramework 相關配置段;
EF 簡單DataAnnotations 實體配置
-
數據庫中建表T_Perons,有Id(主鍵,自動增長)、Name、CreateDateTime字段。
-
創建Person類[Table(“T_Persons”)]因爲類名和表名不一樣,所以要使用Table標註
[Table("T_Persons")] public class Person { public long ID { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } }
因爲EF約定主鍵字段名是Id,所以不用再特殊指定Id是主鍵,如果非要指定就指定[Key]。因爲字段名字和屬性名字一致,所以不用再特殊指定屬性和字段名的對應關係,如果需要特殊指定,則要用[Column(“Name”)]
(*)必填字段標註[Required]、字段長度[MaxLength(5)]、可空字段用int?、如果字段在數據庫有默認值,則要在屬性上標註[DatabaseGenerated]注意實體類都要寫成public,否則後面可能會有麻煩。
-
創建DbContext類(模型類、實體類)
public class MyDBContext: DbContext { //表示使用連接字符串中名字爲conn1 的去連接數據庫 public MyDBContext() : base("name=strcon") { } //通過對Persons 集合的操作就可以完成對T_Persons的操作 public DbSet<Person> Persons { get; set; } }
-
測試
protected void Button1_Click(object sender, EventArgs e) { MyDBContext context = new MyDBContext(); Person p=new Person(); p.Name =TextBox1.Text; p.CreateTime = DateTime.Now; context.Persons.Add(p); context.SaveChanges(); }
注意:MyDbContext 對象是否需要using有爭議,不using也沒事。每次用的時候new MyDbContext就行,不用共享同一個實例,共享反而會有問題。SaveChanges()纔會把修改更新到數據庫中。
EF的開發團隊都說要using DbContext,很多人不using,只是想利用LazyLoad 而已,但是那樣做是違反分層原則的。我的習慣還是using。
異常的處理:如果數據有錯誤可能在SaveChanges()的時候出現異常,一般仔細查看異常信息或者一直深入一層層的鑽InnerException 就能發現錯誤信息。
舉例:創建一個Person對象,不給Name、CreateDateTime賦值就保存。
EF 模型的兩種配置方式
EF 中的模型類的配置有DataAnnotations、FluentAPI 兩種。
上面這種在模型類上[Table(“T_Persons”)]、[Column(“Name”)]這種方式就叫DataAnnotations這種方式比較方便,但是耦合度太高,一般的類最好是POCO(Plain Old C# Object,沒有繼承什麼特殊的父類,沒有標註什麼特殊的Attribute,沒有定義什麼特殊的方法,就是一堆普通的屬性);不符合大項目開發的要求。微軟推薦使用FluentAPI
的使用方式,因此後面主要用FluentAPI 的使用方式。
FluentAPI 配置T_Persons 的方式
-
數據庫中建表T_Perons,有Id(主鍵,自動增長)、Name、CreateDateTime 字段。
-
創建 Person 類。模型類就是普通C#類
public class Person { public long ID { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } }
-
創建一個 PersonConfig 類,放到ModelConfig 文件夾下(PersonConfig、EntityConfig這樣的名字都不是必須的)
public class PersonConfig : EntityTypeConfiguration<Person> { public PersonConfig() { this.ToTable("T_Person"); } }
-
創建 DbContext 類
public class MyDBContext:DbContext { public MyDBContext() : base("name=strcon") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); } public DbSet<Person> Persons { get; set; } }
下面這句話:
modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
代表從這句話所在的程序集加載所有的繼承自EntityTypeConfiguration 爲模型配置類。還有很多加載配置文件的做法(把配置寫到OnModelCreating中或者把加載的代碼寫死到OnModelCreating 中),但是這種做法是最符合大項目規範的做法。
和以前唯一的不同就是:模型不需要標註Attribute;編寫一個XXXConfig類配置映射關係;DbContext 中override OnModelCreating;
-
測試
protected void Button1_Click(object sender, EventArgs e) { MyDBContext context = new MyDBContext(); Person p = new Person(); p.Name = TextBox1.Text; p.CreateTime = DateTime.Now; context.Persons.Add(p); context.SaveChanges(); }
EF 的基本增刪改查
獲取DbSet除了可以ctx.Persons之外,還可以ctx.Set()。
-
增加,一個點:如果Id是自動增長的,創建的對象顯然不用指定Id的值,並且在SaveChanges ()後會自動給對象的Id屬性賦值爲新增行的Id字段的值。
-
刪除。先查詢出來要刪除的數據,然後Remove。這種方式問題最少,雖然性能略低,但是刪除操作一般不頻繁,不用考慮性能。後續在“狀態管理”中會講其他實現方法。
MyDBContext context = new MyDBContext(); if (e.CommandName=="BtnDelete") { int id = Convert.ToInt32(e.CommandArgument); var p = context.Persons.Where(per => per.ID == id).SingleOrDefault(); if (p!=null) { context.Persons.Remove(p); } int i= context.SaveChanges(); if (i>0) { Repeater1.DataSource = context.Persons.ToList(); Repeater1.DataBind(); } }
怎麼批量刪除,比如刪除Id>3 的?查詢出來一個個Remove。性能坑爹。如果操作不頻繁或者數據量不大不用考慮性能,如果需要考慮性能就直接執行sql 語句
-
修改:先查詢出來要修改的數據,然後修改,然後SaveChanges()
MyDbContext ctx = new MyDbContext(); var ps = ctx.Persons.Where(p => p.Id > 3); foreach(var p in ps) { p.CreateDateTime = p.CreateDateTime.AddDays(3); p.Name = "haha"; } ctx.SaveChanges();
性能問題?同上。
-
查。因爲DbSet 實現了IQueryable 接口,而IQueryable 接口繼承了IEnumerable
接口,所以可以使用所有的linq、lambda 操作。給表增加一個Age 字段,然後舉例orderby、groupby、where
操作、分頁等。一樣一樣的。 -
查詢 order by 的一個細節
EF調用Skip之前必須調用OrderBy:如下調用var items = ctx.Persons.Skip(3).Take(5); 會報錯“The method ‘OrderBy’ must be called before the method ‘Skip’.)”,要改成:var items = ctx.Persons.OrderBy(p=>p.CreateDateTime).Skip(3).Take(5);
這也是一個好習慣,因爲以前就發生過(寫原始sql):分頁查詢的時候沒有指定排序規則,以爲默認是按照Id 排序,其實有的時候不是,就造成數據混亂。寫原始SQL 的時候也要注意一定要指定排序規則。