項目中使用EntityFramework
項目代碼:http://yunpan.cn/cKAw3sSJWQwUX 提取碼 871c
文筆不好,技術也差。來這就是來提高自己的,歡迎批評。最后還留下一個小問題,如果你知道的話可以告訴我,謝謝。(郵箱:jin_wangjia@163.com)
前言 1
1. 建立解決方案項目結構 1
2. 導入EntityFramework類庫 1
3. 編寫實體類 1
4. 編輯連接字符串 3
5. 編寫BaseBLL類 3
6. 編寫業務處理超類 4
7. 編寫業務處理類 6
8. 應用 6
9. 疑難雜症 7
前言
數據庫的使用在軟件編程中占有很大的一個比例。尤其是在管理系統中,基本上沒有哪個系統能離開數據庫而單獨存在。在編寫數據庫訪問程序時最讓人頭疼的就是拼SQL,一般都需要在數據庫和VS里顛倒好多次才能寫出我們想要的那個語句。ORM的出現很好的解決了這個問題,它可以把SQL相關程序的編寫變成類似強類型的語句,方便編寫和調試,大大提高了工作效率。下面就把我在項目中如何使用EntiryFramework做一下簡單的記錄。開發環境:VS2010,SQLServer208,EntityFramework6
1. 建立解決方案項目結構
EntityFramework是用來訪問數據庫的,根工程的類型沒有關系,我們可以把它用用C/S的工程當中,也可以把它用到 B/S的工程當中。為了展示EntityFramework的使用方法,我們首先得建立一個例子解決方案。這個解決方案的最基礎的普通三層結構,因為數據的訪問主要靠EntityFramework來實現,所以數據訪問層的實現基本看不出來了,使得整個解決方案好像就是個兩層的一樣。下面我們通過表格的形式來說明一下這個解決方案中各個工程的類型和做用。
工程名 |
層次 |
類型 |
作用 |
是否引入EF |
JZAPP |
展示層 |
應用程序 |
應用程序 |
否 |
BLL |
業務邏輯層 |
類庫 |
邏輯運算,數據訪問 |
是 |
Model |
實體類 |
類庫 |
數據庫表到類的映射 |
是 |
2. 導入EntityFramework類庫
用Nuget把最新版本的EntityFramework導入到BLL,Model兩個項目中。
3. 編寫實體類
下面我們以下面圖中這個實體類做為樣本,來了解一下EntityFramework中的實體類如何定義。實體類是數據庫表的映射,所以這個類的名稱,屬性名稱,屬性類型都是根我們設計的表有關聯了。具體數據庫中的類型映射為C#中的什么類型沒有做過詳細的研究,大概用過的總結如下,如果以后還會用到其他的,還會添加到這里來的。
SqlServer中的類型 |
C#中的類型 |
Varchar(N) |
string |
bit |
bool |
datetime |
DateTime |
int |
integer |
數據庫定義如下:
根據表格,我們可以把上面的數據庫表寫為如下實體類,實體類統一放到Model工程里:

using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations; namespace Model { [Table("User")] public class UserDefinition { /// <summary> /// 用?戶§id /// </summary> [Key,DatabaseGenerated(DatabaseGeneratedOption.None)] public string UserId { set; get; } public string LoginName { set; get; } public string Name { set; get; } public string Password { set; get; } public bool Sex { set; get; } public string DeptId { set; get; } public DateTime CreateDate { set; get; } public int Status { set; get; } public string SizeId { get; set; } public bool Visible { get; set; } } }
- [Table("User")]:定義UserDefinition對應的數據庫表為User
- [Key]:定義UserId 為主鍵
- [DatabaseGenerated(DatabaseGeneratedOption.None)]:定義字段的數據生成項;DatabaseGeneratedOption有三個值;Identity:自增長;None:不處理;Computed:計算列。
4. 編輯連接字符串
連接字符串放在JZAPP工程的配置文件里(app.config),如果工程中有這個文件那就直接使用,如果沒有就新建一個。
在配置文件里添加一個connectionStrings節。它是configuration的子節點。

<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <add name="ABC" connectionString="Data Source=127.0.0.1;Initial Catalog=abc;Persist Security Info=True;User ID=sa;Password=landmark" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>
- Name:本連接字符串的名稱,它在本程序中用於BaseBLL類的構造函數中
- Data Source:數據庫服務器的名稱,也可以是IP,URL
- Initial Catalog:數據庫的名稱貸
- User ID:數據庫的用戶名
- Password:數據庫密碼
5. 編寫BaseBLL類
這個類是訪問數據庫的基礎,有了這個類才可以寫其他的業務處理。如果沒有業務處理,可以直接用這個類操作數據庫。它必須繼承自DbContext。

using System.Data.Entity; using Model; using System.Data.Entity.SqlServer; namespace BLL { public class BaseBLL:DbContext { public DbSet<UserDefinition> User { get; set; } public BaseBLL() : base("abc") { } } }
編寫這個類要注意三點:
- BaseBLL必須繼承自DbContext
- 用DbSet<>類型實例化每一個實體類
- 在構造函數中賦值連接字符串的名稱
6. 編寫業務處理超類
我們可以針對不同的業務來編寫代碼,在程序開發中,遇到重復性太強的地方,把它總結一下,寫出了以下幾個常函數,把它們定義成了一個父類:

public class ParentBLL<T> where T : class { public virtual void Add(T p) { using (BaseBLL bll = new BaseBLL()) { bll.Set<T>().Add(p); bll.SaveChanges(); } } public T Find(object p) { using (BaseBLL bll = new BaseBLL()) { return bll.Set<T>().Find(p); } } public IList<T> Query() { using (BaseBLL bll = new BaseBLL()) { return (from o in bll.Set<T>() select o).ToList(); } } public virtual void Remove(T p) { using (BaseBLL bll = new BaseBLL()) { bll.Set<T>().Attach(p); bll.Set<T>().Remove(p); bll.SaveChanges(); } } public virtual void Update(T p) { using (BaseBLL bll = new BaseBLL()) { bll.Entry(p).State = EntityState.Modified; bll.SaveChanges(); } } }
7. 編寫業務處理類
業務處理類一開始應該是非常簡單的,我們直接用業務處理類的父類來實現它就我們開始的使用了。

using Model; using System.Data.Entity.SqlServer; namespace BLL { public class UserBLL : ParentBLL<UserDefinition> { } }
8. 建庫SQL

create table "User" ( UserId varchar(100) not null, LoginName varchar(100) null, Name varchar(100) null, Password varchar(100) null, Sex bit null, DeptId varchar(100) null, CreateDate datetime null, Status int null, SizeId varchar(100) not null, Visible bit null, constraint PK_USER primary key (UserId) ) Go
9. 應用
下面的程序中用上面的框架寫的一個添加數據程序。

private void button1_Click(object sender, EventArgs e) { var bll = new UserBLL(); var p = new UserDefinition(); p.UserId = Guid.NewGuid().ToString(); p.Name = Guid.NewGuid().ToString(); p.SizeId = Guid.NewGuid().ToString(); p.CreateDate = DateTime.Now; bll.Add(p); }
10. 疑難雜症
寫到這里關於EF的應用就算是已經說完了。憑借上面的代碼,我們已經可以用EF來訪問數據庫了。如果這里面還有一人雷。如果把上面的代碼直接復制到工程里去執行可能做引發一個很莫名其妙的錯誤,那就是EF的一個程序集沒有輸出到JZAPP的輸出目錄里面。
EF6在安裝到項目時在項目里添加了兩個程序集分別為EntityFramework和EntityFramework.Sqlserver.我們的解決方案里。Model和BLL工程引用了EntityFramework和EntityFramework.Sqlserver;JZAPP又引用了 Model和BLL。JZAPP沒有直接引用EntityFramework和EntityFramework.Sqlserver。編譯解決方案后,Model和BLL工程的輸出目錄里有EntityFramework和EntityFramework.Sqlserver程序集。JZAPP工程的輸出目錄里只有EntityFramework一個程序集。工程可以編譯通過,但是運行就會出錯誤,這個問題剛開始很讓我莫名其妙。后來經過哥兒們的幫忙看到一個博客。里面有一句話(間接引用程序集,如果只是引用而沒有調用,程序集不會被輸出到目標目錄)讓我知道了原因。經過測試這句話是正確的。解決方法就是在程序中調用一下這個程序集里的程序。如下:

using Model; using System.Data.Entity.SqlServer; namespace BLL { public class UserBLL : ParentBLL<UserDefinition> { private string UserName() { using (var bll = new BaseBLL()) { return SqlFunctions.UserName(); } } } }
隨便找一個地方寫一個沒用的函數就可以了。經過測試這個方法編譯可以通過,也能解決上面的問題。但是調用會出錯。沒關系了,解決問題就好了。SqlFunctions類具體怎么用以后再研究吧。
好像還有其他更好的方法來解決這個問題,如果你知道,請告訴我,謝謝。