zoukankan      html  css  js  c++  java
  • 七色花权限管理系统(7)- 实现数据仓储和利用T4自动生成实体仓储

    基于EntityFramework的数据访问层,我计划细分成数据核心、数据接口和数据实现。

    其中数据核心已经在前几个章节中创建,那么在这篇日志里,将演示数据仓储(接口和实现)的实现及封装架构的整个过程。

    仓储的作用

    仓储的概念请自行搜索了解,我认为它最大的作用就是解耦。没有仓储,就只能直接使用EF数据库上下文对象来操作数据库,而为了“能使用EF数据库上下文对象来操作数据库(各实体库)”,就必须把实体关联给EF数据库上下文。例如MasterEntityContext中的DbSet属性:

      1 /// <summary>
      2 /// 用户
      3 /// </summary>
      4 public DbSet<SysUser> Users { get; set; }
      5 
      6 /// <summary>
      7 /// 角色
      8 /// </summary>
      9 public DbSet<SysRole> Roles { get; set; }

    只有这样,我们才能在初始化EF数据库上下文对象之后,通过对象来访问相应的实体库,比如登录判定中那句代码:

      1 var db = new MasterEntityContext("matrixkey");
      2 var entity = db.Users.Where(w => w.UserName == model.UserName).FirstOrDefault();

    上述代码中的db.Users,其中Users对象,就是EF数据库上下文中定义的属性。

    在WebUI层直接使用数据访问层的核心对象,这无疑是非常不正确的。在后面的日志中,我们将增加业务逻辑层,作为数据访问层和WebUI层的桥梁。

    但是若没有仓储,即使有了业务逻辑层,仍然只能通过直接使用EF数据库上下文对象的方式来操作数据库,这就需要:

    1、在业务层中暴露EF数据库上下文对象

    2、在业务层中引用EntityFramework

    这依旧很糟糕,有了EF数据库上下文对象,就等于有了一切。不能让业务层拥有一切,也不能让业务层引用EntityFramework。

    于是就需要仓储作为“EF数据库上下文和业务层之间的桥梁”。对于业务层而言,仓储才是真正的数据访问层,业务层根本不知道EF数据库上下文的存在,它不关心数据访问层中究竟是谁提供了数据库访问的能力,无论是EF或是dapper或是源生的ADO.NET,它只关心数据访问层是否执行了它要操作的动作。

    总之,业务层是无关数据驱动的,不能把EF暴露给业务层。仓储的实现,解耦了数据核心层和业务层,并且在下一章节中,还将解耦数据实体层和数据核心层。可见仓储最大的作用就是解耦。

    用户仓储的简易实现

    在解决方案下新建类库项目,名称S.Framework.DataAchieve。再创建相应的文件夹进行区分,结构如下图:

    image

    在Master下创建用户仓储类,名称SysUserRepository。上面说过,仓储是桥梁,是为其他需要获取数据的项目层提供代理服务的,那么在仓储类中,需要定义EF数据库上下文对象,以及通过该上下文对象对数据库的各种操作的方法的封装。

    毫无疑问,数据实现层需要引用“数据实体层、数据核心层”。

    这里以“用户登录功能”作为范例,一步一步演示用户仓储中的代码的演变:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 using System.Data.Entity;
      7 using System.Data.Entity.Infrastructure;
      8 
      9 using S.Framework.Entity.Master;
     10 using S.Framework.DataCore.EntityFramework.EntityContexts;
     11 
     12 namespace S.Framework.DataAchieve.EntityFramework.Repositories.Master
     13 {
     14     /// <summary>
     15     /// 用户仓储
     16     /// </summary>
     17     public class SysUserRepository
     18     {
     19 
     20     }
     21 }
     22 
    用户仓储类-0

    定义数据库上下文对象:

      1 /// <summary>
      2 /// 数据库上下文
      3 /// </summary>
      4 private MasterEntityContext Db
      5 {
      6     get;
      7     set;
      8 }
    数据库上下文对象

    数据库上下文对象作为最核心的内容,只允许在当前类中被设置和获取,因此该属性的访问修饰符设置为private。

    定义用户仓储的构造方法,并在方法中初始化数据库上下文对象:

      1 public SysUserRepository()
      2 {
      3     this.Db = new MasterEntityContext("matrixkey");
      4 }

    “用户登录功能”中需要根据用户名获取用户实体,可以在仓储中定义该方法:

      1 /// <summary>
      2 /// 根据用户名获取用户实体
      3 /// </summary>
      4 /// <param name="userName">用户名</param>
      5 /// <returns>用户实体</returns>
      6 public SysUser GetByUserName(string userName)
      7 {
      8     return this.Db.Users.Where(w => w.UserName == userName).FirstOrDefault();
      9 }

    理论上来说,用户仓储已经完成。

    用户仓储功能检验结果

    先在WebUI项目中增加对数据实现层(S.Framework.DataAchieve)的引用(对数据核心层的引用继续留着,因为数据库初始化策略的设置要用到核心层,等会会把数据库初始化设置从核心层移出去,那时再移除WebUI层对数据核心层的引用)。

    把“用户登录功能”的代码调整为对用户仓储的调用,将

      1 var db = new MasterEntityContext("matrixkey");
      2 var entity = db.Users.Where(w => w.UserName == model.UserName).FirstOrDefault();

    修改为:

      1 var rep = new SysUserRepository();
      2 var entity = rep.GetByUserName(model.UserName);

    编译运行,用admin和123456进行登录,成功跳转至首页。

    解耦数据核心层与WebUI层

    上面说到,由于数据库初始化策略及其设置,都定义在数据核心层中,所以WebUI层需要依赖数据核心层。不能忍,必须解耦,方法是将数据库初始化策略及其设置类,移动到数据实现层中,如下图:

    image

    此时可以把数据核心层中EntityFramework文件夹下的Migrations、Initializes子文件夹删除。然后调整WebUI层中Global的命名空间即可。

    编译,可能会有“找不到命名空间”的报错,原因是旧命名空间不存在了,删除这条using即可。

    编译生成无误,然后移除WebUI层对数据核心层的引用吧。

    由于数据库初始化类调整过,因此需要删除数据库让EF重新创建一次。删除数据库,重新去登录页面尝试登录。

    仓储的完善

    对实体的常用操作,可以简单地归纳为“特定查询、添加、更新、删除”,这几类操作是每一个实体仓储都需要包含的,因此可以通过封装来精简代码量。

    建立一个仓储基本类,名称BaseRepository,将通用的部分在基本类中实现,如下:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 using System.Linq.Expressions;
      7 
      8 using S.Framework.Entity;
      9 using S.Framework.DataCore.EntityFramework.EntityContexts;
     10 
     11 namespace S.Framework.DataAchieve.EntityFramework
     12 {
     13     public abstract class BaseRepository<TEntity> where TEntity : class, new()
     14     {
     15         private System.Data.Entity.DbContext Db { get; set; }
     16 
     17         private System.Data.Entity.DbSet<TEntity> DbSet { get { return this.Db.Set<TEntity>(); } }
     18 
     19         public BaseRepository()
     20         {
     21             this.Db = new MasterEntityContext("matrixkey");
     22         }
     23 
     24         /// <summary>
     25         /// 主键查询
     26         /// </summary>
     27         /// <param name="keyValues">键值</param>
     28         /// <returns>实体</returns>
     29         public virtual TEntity Find(IEnumerable<object> keyValues)
     30         {
     31             if (keyValues == null || keyValues.Count() == 0)
     32             {
     33                 throw new ArgumentException("参数有误。");
     34             }
     35             keyValues = keyValues.Where(keyValue => keyValue != null);
     36             return this.DbSet.Find(keyValues);
     37         }
     38 
     39         /// <summary>
     40         /// 主键查询
     41         /// </summary>
     42         /// <param name="keyValues">键值</param>
     43         /// <returns>实体</returns>
     44         public virtual TEntity Find(params object[] keyValues)
     45         {
     46             if (keyValues == null || keyValues.Count() == 0)
     47             {
     48                 throw new ArgumentException("参数有误。");
     49             }
     50 
     51             return this.DbSet.Find(keyValues);
     52         }
     53 
     54         /// <summary>
     55         /// 获取 <see cref="TEntity"/> 的Linq查询器
     56         /// </summary>
     57         /// <returns></returns>
     58         protected IQueryable<TEntity> Query()
     59         {
     60             return this.DbSet.AsQueryable();
     61         }
     62 
     63         /// <summary>
     64         /// 获取 <see cref="TEntity"/> 的Linq查询器
     65         /// </summary>
     66         /// <param name="predicate">查询条件</param>
     67         /// <returns>数据查询器</returns>
     68         protected IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate)
     69         {
     70             return this.DbSet.Where(predicate);
     71         }
     72 
     73         /// <summary>
     74         /// 添加实体
     75         /// </summary>
     76         /// <param name="entity">实体</param>
     77         public void Add(TEntity entity)
     78         {
     79             if (entity == null)
     80             { return; }
     81 
     82             this.DbSet.Add(entity);
     83         }
     84 
     85         /// <summary>
     86         /// 批量添加实体
     87         /// </summary>
     88         /// <param name="entities">实体集合</param>
     89         public void AddRange(IEnumerable<TEntity> entities)
     90         {
     91             if (entities == null || entities.Count() == 0)
     92             { return; }
     93 
     94             System.Data.Entity.DbSet<TEntity> set = this.DbSet;
     95             bool autoDetectChangesEnabled = this.Db.Configuration.AutoDetectChangesEnabled;
     96             this.Db.Configuration.AutoDetectChangesEnabled = false;
     97 
     98             foreach (TEntity entity in entities)
     99             {
    100                 if (entity == null)
    101                 { continue; }
    102 
    103                 set.Add(entity);
    104             }
    105 
    106             this.Db.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
    107         }
    108 
    109         /// <summary>
    110         /// 更改实体
    111         /// </summary>
    112         /// <param name="entity">实体对象</param>
    113         public void Update(TEntity entity)
    114         {
    115             if (entity == null)
    116             { return; }
    117 
    118             System.Data.Entity.Infrastructure.DbEntityEntry<TEntity> entry = this.Db.Entry(entity);
    119             if (entry.State == System.Data.Entity.EntityState.Detached)
    120             {
    121                 this.DbSet.Attach(entity);
    122             }
    123             entry.State = System.Data.Entity.EntityState.Modified;
    124         }
    125 
    126         /// <summary>
    127         /// 批量更改实体
    128         /// </summary>
    129         /// <param name="entities">实体集合</param>
    130         public void UpdateRange(IEnumerable<TEntity> entities)
    131         {
    132             if (entities == null || entities.Count() == 0)
    133             { return; }
    134 
    135             var set = this.DbSet;
    136             bool autoDetectChangesEnabled = this.Db.Configuration.AutoDetectChangesEnabled;
    137             this.Db.Configuration.AutoDetectChangesEnabled = false;
    138 
    139             foreach (TEntity entity in entities)
    140             {
    141                 if (entity == null)
    142                 { continue; }
    143 
    144                 System.Data.Entity.Infrastructure.DbEntityEntry<TEntity> entry = this.Db.Entry(entity);
    145                 if (entry.State == System.Data.Entity.EntityState.Detached)
    146                 {
    147                     set.Attach(entity);
    148                 }
    149                 entry.State = System.Data.Entity.EntityState.Modified;
    150             }
    151 
    152             this.Db.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
    153         }
    154 
    155         /// <summary>
    156         /// 主键删除实体
    157         /// </summary>
    158         /// <param name="key">键值</param>
    159         public void Delete(object key)
    160         {
    161             if (key == null || string.IsNullOrWhiteSpace(key.ToString()))
    162             { return; }
    163 
    164             TEntity entity = this.Find(key);
    165 
    166             this.Delete(entity);
    167         }
    168 
    169         /// <summary>
    170         /// 删除实体
    171         /// </summary>
    172         /// <param name="entity">实体</param>
    173         public void Delete(TEntity entity)
    174         {
    175             if (entity == null)
    176             { return; }
    177 
    178             System.Data.Entity.DbSet<TEntity> set = this.DbSet;
    179             System.Data.Entity.Infrastructure.DbEntityEntry<TEntity> entry = this.Db.Entry(entity);
    180             if (entry.State == System.Data.Entity.EntityState.Detached)
    181             {
    182                 set.Attach(entity); set.Remove(entity);
    183             }
    184             else
    185             { entry.State = System.Data.Entity.EntityState.Deleted; }
    186         }
    187 
    188         /// <summary>
    189         /// 批量删除实体
    190         /// </summary>
    191         /// <param name="entities">实体集合</param>
    192         public void DeleteRange(IEnumerable<TEntity> entities)
    193         {
    194             if (entities == null || entities.Count() == 0)
    195             { return; }
    196             var set = this.DbSet;
    197 
    198             bool autoDetectChangesEnabled = this.Db.Configuration.AutoDetectChangesEnabled;
    199             this.Db.Configuration.AutoDetectChangesEnabled = false;
    200 
    201             foreach (TEntity entity in entities)
    202             {
    203                 if (entity == null)
    204                 { continue; }
    205 
    206                 System.Data.Entity.Infrastructure.DbEntityEntry<TEntity> entry = this.Db.Entry(entity);
    207                 if (entry.State == System.Data.Entity.EntityState.Detached)
    208                 {
    209                     set.Attach(entity);
    210                 }
    211                 else
    212                 {
    213                     entry.State = System.Data.Entity.EntityState.Deleted;
    214                 }
    215             }
    216 
    217             this.Db.Configuration.AutoDetectChangesEnabled = autoDetectChangesEnabled;
    218         }
    219     }
    220 }
    221 
    仓储基本类

    注意仓储基本类的修饰符以及类中各属性、方法等成员的访问修饰符,要充分理解abstract、private、protected、public的使用原因。不了解泛型的读者请自行查阅相关资料。

    现在让用户仓储类继承仓储基本类,并删除“在父类中已经实现的成员几相关代码”,用户仓储类将变得非常简单:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 using S.Framework.Entity.Master;
      8 
      9 namespace S.Framework.DataAchieve.EntityFramework.Repositories.Master
     10 {
     11     /// <summary>
     12     /// 用户仓储
     13     /// </summary>
     14     public class SysUserRepository : BaseRepository<SysUser>
     15     {
     16         /// <summary>
     17         /// 根据用户名获取用户实体
     18         /// </summary>
     19         /// <param name="userName">用户名</param>
     20         /// <returns>用户实体</returns>
     21         public SysUser GetByUserName(string userName)
     22         {
     23             return this.Query(w => w.UserName == userName).FirstOrDefault();
     24         }
     25     }
     26 }
     27 
    新的仓储基本类

    编译运行,再次进行登录,成功跳转至首页。

    现在为角色实体创建仓储类试试,简单吧。

    但同时也引出新问题:手动为每个实体创建实体仓储是个重复又机械的体力活,是否可以像“实体配置类”那样也用T4模板来自动生成?

    当然可以。过程和“利用T4自动生成实体配置类”一样,模板不同而已,这里一笔带过,直接贴出“仓储模板”和“执行器”的相关代码:

      1 <#+
      2 // <copyright file="Repository.tt" company="">
      3 //  Copyright © . All Rights Reserved.
      4 // </copyright>
      5 
      6 public class Repository : CSharpTemplate
      7 {
      8     private string _modelName;
      9     private string _prefixName;
     10 
     11     public Repository(string modelName, string prefixName)
     12     {
     13         this._modelName = modelName;
     14         this._prefixName = prefixName;
     15     }
     16 	public override string TransformText()
     17 	{
     18 		base.TransformText();
     19 #>
     20 using System;
     21 using System.Collections.Generic;
     22 using System.Linq;
     23 using System.Text;
     24 using System.Threading.Tasks;
     25 
     26 using S.Framework.Entity.<#= _prefixName #>;
     27 
     28 namespace S.Framework.DataAchieve.EntityFramework.Repositories.<#= _prefixName #>
     29 {
     30 	/// <summary>
     31     /// 实体仓储
     32     /// </summary>
     33     public partial class <#= _modelName #>Repository : BaseRepository<<#= _modelName #>>
     34 	{
     35 
     36 	}
     37 }
     38 <#+
     39         return this.GenerationEnvironment.ToString();
     40 	}
     41 }
     42 #>
     43 
    实体仓储模板
      1 <#@ template language="C#" debug="True" #>
      2 <#@ assembly name="System.Core" #>
      3 <#@ output extension="cs" #>
      4 <#@ import namespace="System.IO" #>
      5 <#@ import namespace="System.Text" #>
      6 <#@ import namespace="System.Reflection" #>
      7 <#@ import namespace="System.Linq" #>
      8 <#@ import namespace="System.Collections.Generic" #>
      9 <#@ include file="T4Toolbox.tt" #>
     10 <#@ include file="Repository.tt" #>
     11 <#
     12 
     13     string coreName = "S.Framework", projectName = coreName + ".DataAchieve", entityProjectName = coreName + ".Entity";
     14     string entityBaseModelName = entityProjectName + ".EntityBaseModel";
     15     string entityBaseModelNameForReflection = entityProjectName + ".EntityModelBaseForReflection";
     16     //当前完整路径
     17     string currentPath = Path.GetDirectoryName(Host.TemplateFile);
     18     //T4文件夹的父级文件夹路径
     19     string projectPath = currentPath.Substring(0, currentPath.IndexOf(@"T4"));
     20     //解决方案路径
     21     string solutionFolderPath = currentPath.Substring(0, currentPath.IndexOf(@"" + projectName));
     22 
     23     //加载数据实体.dll
     24     string entityFilePath = string.Concat(solutionFolderPath, ("\"+ entityProjectName +"\bin\Debug\" + entityProjectName + ".dll"));
     25     byte[] fileData = File.ReadAllBytes(entityFilePath);
     26     Assembly assembly = Assembly.Load(fileData);
     27     //反射出实体类,不知道为啥此处不能成功判定“是否继承EntityModelBaseForReflection类”
     28     //因此只能通过名称比较的方式来判定
     29     IEnumerable<Type> modelTypes = assembly.GetTypes().Where(m => m.IsClass && !m.IsAbstract && (m.BaseType.FullName.Equals(entityBaseModelName) || m.BaseType.FullName.Equals(entityBaseModelNameForReflection)));
     30 
     31     //循环实体类
     32     List<string> prefixNames = new List<string>();
     33     foreach (Type item in modelTypes)
     34     {
     35         //找 实体文件夹 名称
     36         string tempNamespace= item.Namespace, nameSpaceWithoutProjectName = tempNamespace.Substring(entityProjectName.Length);
     37         if(nameSpaceWithoutProjectName.IndexOf(".") != 0 || nameSpaceWithoutProjectName.LastIndexOf(".") > 0)
     38         { continue; }
     39 
     40         //是否直接继承实体基本类
     41         bool purity = item.BaseType.FullName.Equals(entityBaseModelNameForReflection);
     42         //实体所在的数据库标识名称
     43         string targetName = nameSpaceWithoutProjectName.Substring(1);
     44         if(!prefixNames.Any(a => a == targetName)){ prefixNames.Add(targetName); }
     45         //目标文件的路径和名称(嵌套Generate文件夹是为了标识T4生成的类文件)
     46         string fileName= targetName + @"Generate" + item.Name + "Repository.cs";
     47 
     48         //仓储文件
     49         string folderName= @"Repositories";
     50         Repository repository = new Repository(item.Name, targetName);
     51         repository.Output.Encoding = Encoding.UTF8;
     52         string path = projectPath + folderName + fileName;
     53         repository.RenderToFile(path);
     54     }
     55 #>
     56 
    模板执行器文件

    通过T4模板生成的实体仓储文件,结构如下图:

    image

    下一篇日志,将演示数据实体层和数据核心层的解耦。

    截止本章节,项目源码下载:点击下载(存在百度云盘中)

  • 相关阅读:
    SharePoint 2013 商务智能报表发布
    sharepoint designer web 服务器似乎没有安装microsoft sharepoint foundation
    SharePoint 2013 Designer系列之数据视图
    SharePoint 2013 Designer系列之数据视图筛选
    SharePoint 2013 Designer系列之自定义列表表单
    SharePoint 2013 入门教程之创建及修改母版页
    SharePoint 2013 入门教程之创建页面布局及页面
    SharePoint 2010 级联下拉列表 (Cascading DropDownList)
    使用SharePoint Designer定制开发专家库系统实例!
    PL/SQL Developer 建立远程连接数据库的配置 和安装包+汉化包+注册机
  • 原文地址:https://www.cnblogs.com/matrixkey/p/5569228.html
Copyright © 2011-2022 走看看