1. 程式人生 > 實用技巧 >深入理解ADO.NET Entity Framework(02)

深入理解ADO.NET Entity Framework(02)

1.ORM

物件對映關係對資料庫進行操作,解決面向物件與關係資料庫不匹配的現象。

1.1 ORM效能問題

  • 複雜的物件管理機制:實時跟蹤,保持物件的一致性的同時降低了效能。
  • 高度封裝執行機制:編寫的任何表示式都要解析成SQL語句。
  • 低效的SQL語句:對映機制轉將物件操作換為SQL語句,效率低。

1.2 EF的狀態管理

在程式中實現資料的增、刪、改操作,EF會監控這些狀態的變化,在執行SaveChange()方法時,會根據物件狀態的變化執行相應的操作。

using (MySchoolContext db = new MySchoolContext())

{

Grade grade = new Grade() { GradeName = "Y3" };

//輸出當前物件狀態

Console.WriteLine(db.Entry(grade).State); //通過Entry()方法獲取模型狀態

db.Grade.Add(grade);

Console.WriteLine(db.Entry(grade).State);

db.SaveChanges();

Console.WriteLine(db.Entry(grade).State);

}

方法或屬性

說明

CurrentValues

獲取由此物件表示的跟蹤實體的當前屬性值

OriginalValues

獲取由此物件表示的跟蹤實體的原始屬性值

State

獲取或設定實體的狀態

Reload()

從資料庫重新載入物件的值

其中,State屬性是一個EntityState列舉型別,其取值如下:

Detached 表示物件存在,但沒有被跟蹤
Unchanged 表示物件尚未經過修改
Added 表示物件為新物件,並且已新增到物件上下文
Deleted 物件已從物件上下文中刪除
Modified 表示物件的一個標量屬性已更改

1.3查詢不進行跟蹤

  • AsNoTracking()方法

using (MySchoolContext db = new MySchoolContext())

{

var result = db.Student.AsNoTracking().FirstOrDefault();

Console.WriteLine(db.Entry(result).State);

}

  • 設定Configuration.AutoDetectChangesEnabled 屬性為false

using (MySchoolContext db = new MySchoolContext())

{

//禁用自動跟蹤變化

db.Configuration.AutoDetectChangesEnabled = false;

for (int i = 0; i < 5000; i++)

{

var stu = new Student() { StudentName = "alex",

GradeId = 1, Age = 20 };

db.Student.Add(stu);

}

db.SaveChanges();

}

在使用EF修改或刪除資料時,必須先查詢物件,然後再對其進行修改或刪除。然而現實開發中很多情況都是通過主鍵刪除一條資料。我們可以通過實體的狀態特性來進行優化。

using (MySchool1Entities entities = new MySchool1Entities())

{

//建立替身物件

var stu = new Student { StudentNo = "10001" };

//給實體附加到上下文中

entities.Student.Attach(stu);

//刪除

entities.Student.Remove(stu);

entities.SaveChanges();

}

程式碼中的Attach()方法可以將EntityState.Unchangee狀態的物件附加到上下文中。

2.載入

  • 延遲載入

  每次呼叫時再去查詢,兩個條件:①Poco類是Public且不能為Sealed。②導航屬性需要標記為Vritual。

  • 貪懶載入

  一次性將資料讀取出來,從快取中讀取,不用在查詢資料庫,兩個條件:先關閉延遲載入。查詢主表的同時通過Include把從表資料也查詢出來。

  • 顯示載入

  --步驟:①單個實體用:Reference ②集合用:Collection ③最後需要Load一下

注意:預設用延遲,多次讀資料庫用貪婪。

//延遲載入

using (dbContext1 db = new dbContext1())

{

Console.WriteLine("---------------- 01-延遲載入 ---------------");

//EF預設就是延遲載入,預設下面的語句就是true,關閉則false

db.Configuration.LazyLoadingEnabled = true;

var list = db.Student.ToList(); //此處載入的資料,沒有對從表進行任何查詢操作

foreach (var stu in list)

{

Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId, stu.studentName);

//下面呼叫導航屬性(一對一的關係) 每次呼叫時,都要去查詢資料庫

var stuAddress = stu.StudentAddress;

Console.WriteLine("地址編號:{0},地址名稱:{1}",

stuAddress.studentAddressId, stu.studentName);

}

}

//貪懶載入

using (dbContext1 db = new dbContext1())

{

Console.WriteLine("------------------- 03-立即載入 ------------------");

//1.關閉延遲載入

db.Configuration.LazyLoadingEnabled = false;

//2. 獲取主表資料的同時,通過Include將從表中的資料也全部加載出來

var list = db.Student.Include("StudentAddress").ToList();

foreach (var stu in list)

{

Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId,

stu.studentName);

//這裡獲取從表中的資料,均是從快取中獲取,無需查詢資料庫

var stuAddress = stu.StudentAddress;

Console.WriteLine("地址編號:{0},地址名稱:{1}",

stuAddress.studentAddressId, stu.studentName);

}

}

//顯示載入

using (dbContext1 db = new dbContext1())

{

Console.WriteLine("----------------- 04-顯式載入 ------------------");

//1.關閉延遲載入

db.Configuration.LazyLoadingEnabled = false;

//2.此處載入的資料,不含從表中的資料

var list = db.Student.ToList();

foreach (var stu in list)

{

Console.WriteLine("學生編號:{0},學生姓名:{1}", stu.studentId,

stu.studentName);

//3.下面的這句話,可以開啟重新查詢一次資料庫

//3.1 單個屬性的情況用Refercence

db.Entry<Student>(stu).Reference(c => c.StudentAddress).Load();

//3.2 集合的情況用Collection

//db.Entry<Student>(stu).Collection(c => c.StudentAddress).Load();

//下面呼叫導航屬性(一對一的關係) 每次呼叫時,都要去查詢資料庫

var stuAddress = stu.StudentAddress;

Console.WriteLine("地址編號:{0},地址名稱:{1}",

stuAddress.studentAddressId, stu.studentName);

}

}

3.快取

  • EF的dbset<T>的Local屬性提供快取
  • DbSet<T>提供了 Find()方法,用於通過主鍵查詢實體速度比First方法快的多,並且如果相應的實體已經被DbContext快取,EF會在快取中直接返回對應的實體,不執行資料庫訪問。

4.事務

  • SaveChanges
  • DbContextTransaction
  • TransactionScope

using (DbContext db = new CodeFirstModel())

{

//增加

TestInfor t1 = new TestInfor()

{

id = Guid.NewGuid().ToString("N"),

txt1 = "txt1",

txt2 = "txt2"

};

db.Set<TestInfor>().Add(t1);

//刪除

TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "1").FirstOrDefault();

if (t2 != null)

{

db.Set<TestInfor>().Remove(t2);

}

//修改

TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault();

t3.txt2 = "我是李馬茹23";

//SaveChanges事務提交

int n = db.SaveChanges(); //一次性統一或回滾

Console.WriteLine("資料作用條數:" + n);

}

using (DbContext db = new CodeFirstModel())

{

DbContextTransaction trans = null;

try

{

//開啟事務

trans = db.Database.BeginTransaction(); //多個SaveChanges

//增加

string sql1 = @"insert into TestInfor values(@id,@txt1,@txt2)";

SqlParameter[] pars1 ={

new SqlParameter("@id",Guid.NewGuid().ToString("N")),

new SqlParameter("@txt1","txt11"),

new SqlParameter("@txt2","txt22")

};

db.Database.ExecuteSqlCommand(sql1, pars1);

//刪除

string sql2 = @"delete from TestInfor where id=@id";

SqlParameter[] pars2 ={

new SqlParameter("@id","22")

};

db.Database.ExecuteSqlCommand(sql2, pars2);

//修改

string sql3 = @"update TestInfor set txt1=@txt1 where id=@id";

SqlParameter[] pars3 ={

new SqlParameter("@id","3"),

new SqlParameter("@txt1","二狗子")

};

db.Database.ExecuteSqlCommand(sql3, pars3);

//提交事務

trans.Commit();

Console.WriteLine("事務成功了");

}

catch (Exception ex)

{

Console.WriteLine(ex.Message);

trans.Rollback(); //回滾

}

finally

{

//也可以把該事務寫到using塊中,讓其自己託管,就不需要手動釋放了

trans.Dispose();

}

}

using (DbContext db = new CodeFirstModel())

{

//自動脫管,不需要手動釋放多資料庫連線

using (DbContextTransaction trans = db.Database.BeginTransaction())

{

try

{

TestInfor t1 = new TestInfor()

{

id = Guid.NewGuid().ToString("N"),

txt1 = "111111111",

txt2 = "222222222222"

};

db.Entry(t1).State = EntityState.Added;

db.SaveChanges();

TestInfor t2 = new TestInfor()

{

id = Guid.NewGuid().ToString("N") + "123",

txt1 = "111111111",

txt2 = "222222222222"

};

db.Entry(t2).State = EntityState.Added;

db.SaveChanges();

trans.Commit();

}

catch (Exception)

{

trans.Rollback();

}

}

}

5.從實體框架回歸SQL

EF在DbContext類的Database屬性裡提供了ExecuteSqlCommand()和SqlQuery()兩個方法,用來直接訪問資料庫。

  • ExecuteSqlCommand()
  • SqlQuery()

using (MySchool1Entities db = new MySchool1Entities())

{

//執行update語句

string sql = "update grade set gradeName=@gradeName where

gradeId=@gradeId";

SqlParameter[] ps =

{

new SqlParameter("@gradeName","第二學年"),

new SqlParameter("@gradeId",3)

};

int result=db.Database.ExecuteSqlCommand(sql, ps);//返回影響行數

if (result>0)

{

Console.WriteLine("資料更新完成!");

}

//執行查詢語句

sql = "select * from from student where studentNo=@stuNo";

ps = new SqlParameter[] { new SqlParameter("@stuNo", "S1001234") };

var stu = db.Database.SqlQuery<Student>(sql, ps);//返回集合

Console.WriteLine(stu.ToList()[0]);

}

封裝EF的DAL層

  public class BaseDAL<T> where T:class
  2     {
  3         private DbContext db
  4         {
  5             get
  6             {
  7                 DbContext dbContext = CallContext.GetData("dbContext") as DbContext;
  8                 if (dbContext == null)
  9                 {
 10                     dbContext = new MySchoolContext();
 11                     CallContext.SetData("dbContext", dbContext);
 12                 }
 13                 return dbContext;
 14             }
 15         }
 16 
 17         /// <summary>
 18         /// 執行增加,刪除,修改操作(或呼叫儲存過程)
 19         /// </summary>
 20         /// <param name="sql"></param>
 21         /// <param name="pars"></param>
 22         /// <returns></returns>
 23         public int ExecuteSql(string sql, params SqlParameter[] pars)
 24         {
 25             return db.Database.ExecuteSqlCommand(sql, pars);
 26         }
 27 
 28         /// <summary>
 29         /// 執行查詢操作
 30         /// </summary>
 31         /// <typeparam name="T"></typeparam>
 32         /// <param name="sql"></param>
 33         /// <param name="pars"></param>
 34         /// <returns></returns>
 35         public List<T> ExecuteQuery(string sql, params SqlParameter[] pars)
 36         {
 37             return db.Database.SqlQuery<T>(sql, pars).ToList();
 38         }
 39 
 40         /// <summary>
 41         /// 新增
 42         /// </summary>
 43         /// <param name="model"></param>
 44         /// <returns></returns>
 45         public int Add(T model)
 46         {
 47             db.Set<T>().Add(model);
 48             return db.SaveChanges();
 49         }
 50 
 51         /// <summary>
 52         /// 刪除(適用於先查詢後刪除的單個實體)
 53         /// </summary>
 54         /// <param name="model">需要刪除的實體</param>
 55         /// <returns></returns>
 56         public int Del(T model)
 57         {
 58             db.Set<T>().Attach(model);
 59             db.Set<T>().Remove(model);
 60             return db.SaveChanges();
 61         }
 62 
 63         /// <summary>
 64         /// 根據條件刪除(支援批量刪除)
 65         /// </summary>
 66         /// <param name="delWhere">傳入Lambda表示式(生成表示式目錄樹)</param>
 67         /// <returns></returns>
 68         public int DelBy(Expression<Func<T, bool>> delWhere)
 69         {
 70             var listDels = db.Set<T>().Where(delWhere);
 71             foreach(var model in listDels)
 72             {
 73                 db.Set<T>().Attach(model);
 74                 db.Set<T>().Remove(model);
 75             }
 76             return db.SaveChanges();
 77         }
 78 
 79         /// <summary>
 80         /// 修改
 81         /// </summary>
 82         /// <param name="model">修改後的實體</param>
 83         /// <returns></returns>
 84         public int Modify(T model)
 85         {
 86             db.Entry(model).State = EntityState.Modified;
 87             return db.SaveChanges();
 88         }
 89 
 90         /// <summary>
 91         /// 批量修改
 92         /// </summary>
 93         /// <param name="model">要修改實體中 修改後的屬性 </param>
 94         /// <param name="whereLambda">查詢實體的條件</param>
 95         /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param>
 96         /// <returns></returns>
 97         public int ModifyBy(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames)
 98         {
 99             List<T> listModifes = db.Set<T>().Where(whereLambda).ToList();
100             Type t = typeof(T);
101             List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();
102             Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>();
103             proInfos.ForEach(p =>
104             {
105                 if (proNames.Contains(p.Name))
106                 {
107                     dicPros.Add(p.Name, p);
108                 }
109             });
110             foreach (string proName in proNames)
111             {
112                 if (dicPros.ContainsKey(proName))
113                 {
114                     PropertyInfo proInfo = dicPros[proName];
115                     object newValue = proInfo.GetValue(model, null);
116                     foreach (T m in listModifes)
117                     {
118                         proInfo.SetValue(m, newValue, null);
119                     }
120                 }
121             }
122             return db.SaveChanges();
123         }
124 
125         /// <summary>
126         /// 根據條件查詢
127         /// </summary>
128         /// <param name="whereLambda">查詢條件(lambda表示式的形式生成表示式目錄樹)</param>
129         /// <returns></returns>
130         public IQueryable<T> GetListBy(Expression<Func<T, bool>> whereLambda)
131         {
132             return db.Set<T>().Where(whereLambda);
133         }
134         /// <summary>
135         /// 根據條件排序和查詢
136         /// </summary>
137         /// <typeparam name="Tkey">排序欄位型別</typeparam>
138         /// <param name="whereLambda">查詢條件</param>
139         /// <param name="orderLambda">排序條件</param>
140         /// <param name="isAsc">升序or降序</param>
141         /// <returns></returns>
142         public IQueryable<T> GetListBy<Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true)
143         {
144             if (isAsc)
145             {
146                 return db.Set<T>().Where(whereLambda).OrderBy(orderLambda);
147             }
148             else
149             {
150                 return db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda);
151             }
152         }
153         /// <summary>
154         /// 分頁查詢
155         /// </summary>
156         /// <typeparam name="Tkey">排序欄位型別</typeparam>
157         /// <param name="pageIndex">頁碼</param>
158         /// <param name="pageSize">頁容量</param>
159         /// <param name="whereLambda">查詢條件</param>
160         /// <param name="orderLambda">排序條件</param>
161         /// <param name="isAsc">升序or降序</param>
162         /// <returns></returns>
163         public IQueryable<T> GetPageList<Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true)
164         {
165 
166             IQueryable<T> list = null;
167             if (isAsc)
168             {
169                 list = db.Set<T>().Where(whereLambda).OrderBy(orderLambda)
170                .Skip((pageIndex - 1) * pageSize).Take(pageSize);
171             }
172             else
173             {
174                 list = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda)
175               .Skip((pageIndex - 1) * pageSize).Take(pageSize);
176             }
177             return list;
178         }
179         /// <summary>
180         /// 分頁查詢輸出總行數
181         /// </summary>
182         /// <typeparam name="Tkey">排序欄位型別</typeparam>
183         /// <param name="pageIndex">頁碼</param>
184         /// <param name="pageSize">頁容量</param>
185         /// <param name="whereLambda">查詢條件</param>
186         /// <param name="orderLambda">排序條件</param>
187         /// <param name="isAsc">升序or降序</param>
188         /// <returns></returns>
189         public IQueryable<T> GetPageList<Tkey>(int pageIndex, int pageSize, out int rowCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true)
190         {
191             IQueryable<T> list = null;
192             rowCount = db.Set<T>().Where(whereLambda).Count();
193             if (isAsc)
194             {
195                 list = db.Set<T>().Where(whereLambda).OrderBy(orderLambda)
196                    .Skip((pageIndex - 1) * pageSize).Take(pageSize);
197             }
198             else
199             {
200                 list = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda)
201                  .Skip((pageIndex - 1) * pageSize).Take(pageSize);
202             }
203             return list;
204         }
205 
206 
207     }
View Code

Detached:表示物件存在,但沒有被跟蹤

Unchanged:表示物件尚未經過修改

Added:表示物件為新物件,並且已新增到物件上下文

Deleted:物件已從物件上下文中刪除

Modified:表示物件的一個標量屬性已更改