当前位置:Gxlcms > 数据库问题 > EF查询数据库框架的搭建

EF查询数据库框架的搭建

时间:2021-07-01 10:21:17 帮助过:11人阅读

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(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(Func<T, bool> whereLambda)
        {
            return objectContext.Set<T>().Where<T>(whereLambda).AsQueryable<T>();
        }

        /// <summary>
        /// 分页加载数据
        /// </summary>
        /// <param name="whereLambda">过滤条件</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页大小</param>
        /// <param name="totalCount">总记录数</param>
        /// <returns></returns>
        public virtual IQueryable<T> LoadEntites(Func<T, bool> whereLambda, int pageIndex, int pageSize, out int totalCount)
        {
            var tmp = objectContext.Set<T>().Where<T>(whereLambda);
            totalCount = tmp.Count();

            return tmp.Skip<T>(pageSize * (pageIndex - 1))//跳过行数,最终生成的sql语句是Top(n)
                      .Take<T>(pageSize) //返回指定数量的行
                      .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(Func<T, bool> whereLambda)
        {
            return this.CurrentDao.LoadEntites(whereLambda);
        }


        public virtual IQueryable<T> LoadEntites(Func<T, bool> whereLambda, int pageIndex, int pageSize, out int totalCount)
        {
            return this.CurrentDao.LoadEntites(whereLambda, pageIndex, pageSize, out totalCount);
        }


        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(t => t.Id_bigint < 200).ToList();
           int s = lis.Count;

也就是说,所有对数据库的访问都是通过EF.IBLL中的实体接口来实现的。

总结:整个数据访问的框架就分为业务逻辑层(EF.IBLL,EF.BLL),数据访问层(EF.IDAO,EF.DAO)和实体(DBModel)三个部分。在创建实体的时候,要注意EF版本的选择,目前来说最好是选择5.0版本的,因为EntityFramework的版本为5.0,否则可能会出现意想不到的错误。还有就是在选择数据连接的时候,对于是否在字符串中包括敏感数据,选择是就行了,这也是为了避免出现不可预知的错误。每次更新了实体的时候,都要重新保存一次所有的文本模板文件,以便能自动生成实体类和接口。

以上介绍的一些对数据库的访问的方法都是linq方式的,有时候我们需要通过sql语句来实现对数据库的访问,这种方式在另一篇随笔再详细的介绍。

 

EF查询数据库框架的搭建

标签:efi   framework   gif   one   system   time   put   work   pac   

人气教程排行