EF查詢數據庫框架的搭建


一個簡單的EF查詢框架除了運行項目外,大概需要5個類庫項目,當然這個不是一定要這樣做,這可以根據自己的需要設置有多少個項目。這里介紹的方法步驟只適合EF零基礎的人看看就是了。

在開始之前,先建立一個運行項目,不管是MVC還是winfrom項目,只要能運行就OK,這是為了在后面能測試EF框架的地方。

一、模型項目。這個項目主要負責與數據庫映射的,里面的類都是數據庫表的實體。

首先在解決方案中建一個文件夾EFModel,這里將會收納所有的與EF查詢有關的項目,這樣做只是為了將他們與運行項目分開而已,沒有什么特別的意思。

在EFModel中建立一個類庫,命名為DBModel,然后在這個類庫中創建一個EF

添加之后,選擇從數據庫生成

在這里,服務器名可以輸入數據庫所在的服務器的ip,然后測試一下鏈接,看是否成功。在下面的鏈接到數據庫中選擇你要鏈接的數據庫

下一步后,如果沒有什么特別設置,就直接完成就可以了。然后看看生成的App.config文件,在connectionStrings節點下就是數據庫的鏈接字符串了

由於沒有選擇任何的表,因此在數據庫模型中就是空的,這時需要添加新的模型來映射數據庫中的表,打開model.edmx,然后右鍵:

下面這一步最好選擇是,否則在后面運行程序的時候可能會出現:基礎提供程序在 Open 上失敗。的錯誤

 

找到要添加的表,然后點完成

最后保存model,就能看到在model.tt下添加了一個實體類。至此,模型項目就完成了,如果以后需要添加新的實體類,也從數據庫更新模型就是了。

這種模式下,必須是先在數據庫中把表建好,然后才能添加實體類,如果需要對已經添加了實體類的表做結構上的修改,那么在數據庫中完成修改后,需要在model.edmx中先刪除原來的實體,然后重新添加一次才行。

二、增刪改查公用方法的接口項目IDAO

建立一個類庫項目EF.IDAO,添加兩個引用

然后添加接口IBaseDao,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace  EF.IDAO
{
   public interface IBaseDao<T>
                 where T:class,
                 new ()//約束T類型必須可以實例化
   {
       //根據條件獲取實體對象集合
       IQueryable<T> LoadEntites(); 
    //千萬別用下面兩種方式去查詢,因為這兩種方式是吧整個表所有的數據查出來后再按條件篩選的   
    //IQueryable<T> LoadEntites(Func<T,bool> whereLambda );
    //IQueryable<T> LoadEntites(Func<T,bool> whereLambda, int pageIndex, int pageSize,out int totalCount); 

    //增加
    T AddEntity(T entity);
    //更新
    T UpdateEntity(T entity);
    //刪除
    bool DelEntity(T entity);
    
//根據條件刪除
    bool DelEntityByWhere(Func<T, bool> whereLambda);
}
}

此時基接口中的CRUD方法就定義完成。接下來我們需要使用T4模版生成所有的實體類接口並實現IBaseDao接口。

添加名為IDaoExt的T4模板

加入如下代碼:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs" #>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"..\\DBModel\\Model.edmx";//指定edmx實體模型所在的路徑

EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);

#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBModel;//引用Domain的命名空間

namespace EF.IDAO //實體類接口所在的命名空間
{
<#
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name)) //遍歷edmx模型中映射的實體對象
{#>    
    public interface I<#=entity.Name#>Dao:IBaseDao<<#=entity.Name#>> //生成實體對象接口,這里生成的接口的名稱是根據模型中的實體類名來設置的,而實體的類名是在建數據庫表名的時候設置的名稱
    {
    }
<#};#>
}

其中,有注釋的地方是需要根據自己的情況修改的地方(后面項目中的文本模板文件也是如此)。保存文件,然后就可以在IDaoExt.tt下生成了一個文件IDaoExt.cs,這個文件中有一個接口,這個接口就是上面添加的實體所對應的接口。它是根據模板IDaoExt.tt來生成的,因此每當添加了新的實體后,都需要重新保存一次這些tt文件。當然,也可以不用這個模板文件來生成對應的接口,那樣的話就需要手動的添加了,這根據自己的愛好決定用哪種方式。

在項目中建一個局部接口IDBSession,注意,是局部接口,要用到關鍵字partial。

namespace EF.IDAO
{
    public partial interface IDBSession
    {
        int SaveChange();//用於在業務邏輯層對提交進行管理
    }
}

這個接口主要是提供實體類接口的訪問屬性,以實現對數據訪問層的封裝。建一個文本模板IDBSession.tt

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs" #>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"..\\DBModel\\Model.edmx";

EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);

#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBModel;
using EF.IDAO;

namespace EF.IDAO
{
  public partial interface IDBSession
  {
    <#foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
    {#>    
        I<#=entity.Name#>Dao <#=entity.Name#>Dao { get; set; }
    <#};#>
 }
}

保存之后,就自動生成了IDBSession的局部接口了

再添加一個接口IDBSessionFactory,為業務邏輯層提供統一訪問入口:

namespace EF.IDAO
{
   public interface IDBSessionFactory
    {
        IDBSession GetCurrentDBSession();
    }
}

該接口中定義GetCurrentDBSession()方法,其作用是通過該接口方法獲取需要的實體對象。至此,這個項目基本上完成了,在這里添加了如下文件:

 

三、增刪改查等方法的實現項目DAO

建好項目后添加如下引用:

創建ObjectContextFactory類,用來獲取EF上下文。當網站訪問量增大時,為避免EF產生的臟數據問題,我們使用System.Runtime.Remoting.Messaging 命名空間下的CallContext來解決線程內上下文唯一。

namespace EF.DAO
{
    public class ObjectContextFactory
    {
        public static DbContext GetCurrentObjectContext()
        {
            //從CallContext數據槽中獲取EF上下文
            DbContext objectContext = CallContext.GetData(typeof(ObjectContextFactory).FullName) as DbContext;
            if (objectContext == null)
            {
                //如果CallContext數據槽中沒有EF上下文,則創建EF上下文,並保存到CallContext數據槽中
                objectContext = new DBContent();//當數據庫替換為MySql等,只要在此處EF更換上下文即可。這里的DBContent是model.context.cs中的局部類
                CallContext.SetData(typeof(ObjectContextFactory).FullName, objectContext);
            }
            return objectContext;
        }
    }
}

創建BaseDao類,實現IBaseDao中定義方法,用於所有實體類繼承此基類。但是,這個類和接口IBaseDao沒有任何的關系,他們之間的聯系體現在這個類的派生類實現了IBaseDao這個接口

namespace EF.DAO
{
    public class BaseDao<T> where T : class, new()
    {
        DbContext objectContext = ObjectContextFactory.GetCurrentObjectContext() ;//獲取EF上下文

        /// <summary>
        /// 加載實體集合
        /// </summary>
        /// <param name="whereLambda"></param>
        /// <returns></returns>
        public virtual IQueryable<T> LoadEntites()
        {
            return objectContext.Set<T>().AsQueryable<T>();
        }/// <summary>
        /// 添加實體
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>返回更新后的實體</returns>
        public virtual T AddEntity(T entity)
        {
            objectContext.Set<T>().Add(entity);
            objectContext.SaveChanges();
            return entity;
        }

        /// <summary>
        /// 更新實體
        /// </summary>
        /// <param name="entity"></param>
        /// <returns>返回更新后的實體</returns>
        public virtual T UpdateEntity(T entity)
        {
            objectContext.Set<T>().Attach(entity);
            objectContext.Entry<T>(entity).State = EntityState.Modified;//將附加的對象狀態更改為修改
            objectContext.SaveChanges();
            return entity;
        }

        /// <summary>
        /// 刪除實體
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual bool DelEntity(T entity)
        {
            objectContext.Set<T>().Attach(entity);
            objectContext.Entry<T>(entity).State = EntityState.Deleted;//將附加的實體狀態更改為刪除
            if (objectContext.SaveChanges() > 0)
            {
                return true;//刪除成功
            }
            else
            {
                return false;//刪除失敗
            }
        }

        /// <summary>
        /// 根據條件刪除對象
        /// </summary>
        /// <param name="whereLambda">條件</param>
        /// <returns></returns>
        public virtual bool DelEntityByWhere(Func<T, bool> whereLambda)
        {
            var tmp = objectContext.Set<T>().Where<T>(whereLambda);//根據條件從數據庫中獲取對象集合
            foreach (var entity in tmp)
            {
                objectContext.Entry<T>(entity).State = EntityState.Deleted;//標記對象為刪除狀態刪除
            }
            if (objectContext.SaveChanges() > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

使用T4模板生成所有實體對象的實現,生成所有的實體類繼承自BaseDao並實現各自的接口。創建一個模板文件DaoExt

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension=".cs" #>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"..\\DBModel\\Model.edmx";

EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBModel;
using EF.IDAO;


namespace EF.DAO
{
<#
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{#>    
    public partial class <#=entity.Name#>Dao:BaseDao<<#=entity.Name#>>,I<#=entity.Name#>Dao
    {
      
    }
<#};#>
}

創建文本模板DBSessionExt,來生成DBSession實現接口IDBSession

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"..\\DBModel\\Model.edmx";

EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);

#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EF.IDAO;
using System.Data.Entity;

namespace EF.DAO
{
  public partial class DBSession : IDBSession
  {
  private DbContext _efContext;

        //EF上下文 這段代碼是因為要實現IDBSession接口中的SaveChange方法而寫的,因為每次保存這個文本模板時,如果沒有這段代碼那么在生成的類中就會缺少SaveChange方法的實現,從而導致編譯不過
        public DbContext EfContext
        {
            get
            {
                if (_efContext == null)
                {
                    _efContext = ObjectContextFactory.GetCurrentObjectContext();
                }
                return _efContext;
            }
            set { _efContext = value; }
        }

        public int SaveChange()
        {
            return EfContext.SaveChanges();//調用SaveChanges()方法提交操作
        }
    <#foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
    {#>    
        private I<#=entity.Name#>Dao _<#=entity.Name#>Dao;
        public I<#=entity.Name#>Dao <#=entity.Name#>Dao
        {
            get
            {
                if (_<#=entity.Name#>Dao == null)
                {
                    _<#=entity.Name#>Dao = new <#=entity.Name#>Dao();
                }
                return _<#=entity.Name#>Dao;
            }
            set { _<#=entity.Name#>Dao = value; }
        }
    <#}#>
 }
}

添加一個類DBSessionFactory,實現接口IDBSessionFactory

namespace EF.DAO
{
    public class DBSessionFactory : IDBSessionFactory
    {
        public IDBSession GetCurrentDBSession()
        {
            IDBSession dbSession = CallContext.GetData(typeof(DBSessionFactory).FullName) as DBSession;
            if (dbSession == null)
            {
                dbSession = new DBSession();
                CallContext.SetData(typeof(DBSessionFactory).FullName, dbSession);
            }
            return dbSession;
        }
    }
}

至此,我們就已經完成了對數據訪問層的封裝當數據庫中的表或字段有更新時,我們只需要重新運行一下相應T4模版,就可以實現與數據庫保存一致。一共添加了如下文件

 

四、業務邏輯層的封裝

創建名為EF.IBLL的程序集,主要用於業務邏輯層接口定義

創建接口IBaseService,這個接口和IBaseDao一模一樣的,但是這個接口是業務邏輯層的,而IBaseDao是數據訪問層的。所以可以將IBaseDao直接拷過來換個名字就行了

創建名為IServiceExt的T4模版,用於自動生成所有實體對象的接口,並繼承自IBaseService接口

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"..\\DBModel\\Model.edmx";

EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DBModel;

namespace EF.IBLL
{
<#
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{#>    
    public interface I<#=entity.Name#>Service : IBaseService<<#=entity.Name#>>
    {
    }
<#};#>
}

至此,業務邏輯層定義完成

五、實現業務邏輯層,對數據訪問層的調用

創建項目EF.BLL,創建名為BaseService基類。該基類中實現了對數據訪問層的調用,也實現了增刪改查

步驟: 1、先將IDBSessionFactory封裝為屬性,用於獲取IDBSession

                     2、再將IDBSession封裝為屬性,用於獲取EF上下文對象

                     3、定義IBaseDao類型的CurrentDao屬性,用於屬性獲取具體的實體對象

                     4、定義抽象方法 SetCurrentDao(),用於子類設置實現,為CurrentDao屬性賦具體的實體對象

namespace EF.BLL
{
    public abstract class BaseService<T> where T : class, new()
    {
        //構造函數
        public BaseService()
        {
            //調用SetCurrentDao()方法,要求子類必須實現
            SetCurrentDao();
        }

        //獲取EF實體工廠
        IDBSessionFactory _dbSessionFactory;
        IDBSession _dbSession;

        public IDBSessionFactory DbSessionFactory
        {
            get
            {
                if (_dbSessionFactory == null)
                {
                    _dbSessionFactory = new DBSessionFactory();
                }
                return _dbSessionFactory;
            }
            set { _dbSessionFactory = value; }
        }


        public IDBSession DbSession
        {
            get
            {
                if (_dbSession == null)
                {
                    _dbSession = DbSessionFactory.GetCurrentDBSession();//通過數據訪問層提供的工廠獲取EF實體對象
                }
                return _dbSession;
            }
            set { _dbSession = value; }
        }
        //數據訪問層基接口類型可以接收數據訪問層的所有實體Dao
        public IBaseDao<T> CurrentDao { get; set; }

        //該方法用於子類實現,其作用是設置相應的實體Dao
        public abstract void SetCurrentDao();

    }
}

在實現增加和更新方法時,我們這時調用DBSessin中封裝的SaveChanges()方法進行提交,主要目的是實現業務層控制提交。由於EF具有延遲加載特性,因此我們利用此特性,實現批量操作時,一次提交數據庫,以提高程序性能,因此我們這時需要將BaseDao中的增加和更新方法中的SaveChange()方法注視掉。

基類BaseService剩下的代碼:

 //以下是CRUD實現

        public virtual IQueryable<T> LoadEntites()
        {
            return this.CurrentDao.LoadEntites();
        }

public virtual T AddEntity(T entity) { var tmp = this.CurrentDao.AddEntity(entity); this.DbSession.SaveChange(); return tmp; } public virtual T UpdateEntity(T entity) { var tmp = this.CurrentDao.UpdateEntity(entity); this.DbSession.SaveChange(); return tmp; } public virtual bool DelEntity(T entity) { return this.CurrentDao.DelEntity(entity); } public virtual bool DelEntityByWhere(Func<T, bool> whereLambda) { return this.DelEntityByWhere(whereLambda); }

至此,BaseService業務邏輯層基類就封裝完成,接下來使用T4模版自動生成所有實體。

創建名為ServiceExt的T4模版

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);

string inputFile = @"..\\DBModel\\Model.edmx";

EdmItemCollection ItemCollection = loader.CreateEdmItemCollection(inputFile);
string namespaceName = code.VsNamespaceSuggestion();

EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
#>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EF.IBLL;
using DBModel;

namespace EF.BLL
{
<#
foreach (EntityType entity in ItemCollection.GetItems<EntityType>().OrderBy(e => e.Name))
{#>    
    public partial class <#=entity.Name#>Service : BaseService<<#=entity.Name#>>, I<#=entity.Name#>Service
    {
       public override void SetCurrentDao()
       {
           this.CurrentDao = this.DbSession.<#=entity.Name#>Dao;
       }
    }
<#};#>
}

這時我們就對業務邏輯層封裝完成。

現在需要建一個運行項目,來測試是否真的可以訪問數據庫了。項目建好后,添加DBModel,IBLL,BLL的引用。將DBModel中app.config里面的字符串鏈接復制到運行項目中的web.config中

然后在要訪問數據庫的方法中通過下面的代碼來實現:

 EF.IBLL.IN_CommodityService ics = new EF.BLL.N_CommodityService();
           IList<DBModel.N_Commodity> lis = ics.LoadEntites().where(t => t.Id_bigint < 200).ToList();
           int s = lis.Count;
//如果是分頁查詢,如下:
IList<DBModel.N_Commodity> les = ics.LoadEntites().where(t => t.Id_bigint < 200).Take(30).Skip(20).ToList();
//還可以通過OrderBy,ThenByDescending,
ThenBy等方法來實現排序,這些都要在tolist方法之前調用

也就是說,所有對數據庫的訪問都是通過EF.IBLL中的實體接口來實現的。

總結:整個數據訪問的框架就分為業務邏輯層(EF.IBLL,EF.BLL),數據訪問層(EF.IDAO,EF.DAO)和實體(DBModel)三個部分。在創建實體的時候,要注意EF版本的選擇,目前來說最好是選擇5.0版本的,因為EntityFramework的版本為5.0,否則可能會出現意想不到的錯誤。還有就是在選擇數據連接的時候,對於是否在字符串中包括敏感數據,選擇是就行了,這也是為了避免出現不可預知的錯誤。每次更新了實體的時候,都要重新保存一次所有的文本模板文件,以便能自動生成實體類和接口。

以上介紹的一些對數據庫的訪問的方法都是linq方式的,有時候我們需要通過sql語句來實現對數據庫的訪問,這種方式在另一篇隨筆再詳細的介紹。

 

ThenBy


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM