标签:
上文讲述了数据库配置及其它配置文件的使用,搭建好数据库的链接方式了,接下来就是数据库中的表与实体类的关联了(映射)。
在V1.x中,新增了视图、存储过程的映射。在以往的解决方案中,视图是当成”表“来操作的。
但是我们的视图通常只是为了方便查询而建的。而表支持写操作。会显的很不入调(当然MSSQL的视图也是支持写的,但是这里的视图映射是为了只读视图而来的)
其次,存储过程也支持实体类映射。这是这个版本的一个小创新点。以往我们调用存储过程时,只能在逻辑段里手动写参数名称、存储过程名称。
这给我们带来了维护灾难,同时最重要的是不支持硬编码方式也是很不优雅的。在新版中实现了这一支持。
Context上下文
在前面,我们讲到有表、视图、存储过程的映射。那么他们分别对应着:TableContext、ViewContext、ProcContext(类似于EntityFramework中的DbContext),同时他们均继承了BaseContext基类。
表的容器,在实体类中必须继承它,与此同时它的各个表由TableSet封装。
1 namespace Demo.PO 2 { 3 [Context()] 4 public class Table : TableContext<Table> 5 { 6 [Set(Name = "Members_User")] 7 public TableSet<UserVO> User { get; set; } 8 9 [Set(Name = "Members_Role")] 10 public TableSet<UserRoleVO> UserRole { get; set; } 11 } 12 }
TableSet表操作类:针对数据库表提供的一系列CURD的操作。被TableSet包含的就是表字段了。
视图的容器,在实体类中必须继承它,与此同时它的各个视图由ViewSet封装。
1 namespace Demo.PO 2 { 3 [Context(0)] 4 public class View : ViewContext<View> 5 { 6 public View() : base(0) { } 7 8 [Set(Name = "View_Account")] 9 public ViewSet<AccountVO> Account { get; set; } 10 } 11 }
ViewSet视图操作类:针对数据库视图提供的一系列查询的操作。被ViewSet包含的就是视图字段了。
存储过程的容器,在实体类中必须继承它,与此同时它的各个存储过程由ProcSet封装。
1 namespace Demo.PO 2 { 3 [Context(0)] 4 public class Proc : ProcContext<Proc> 5 { 6 [Set(Name = "sp_Info_User")] 7 public ProcSet<InfoUserVO> InfoUser { get; set; } 8 9 [Set(Name = "sp_Insert_User")] 10 public ProcSet<InsertUserVO> InsertUser { get; set; } 11 12 [Set(Name = "sp_List_User")] 13 public ProcSet<ListUserVO> ListUser { get; set; } 14 15 [Set(Name = "sp_Value_User")] 16 public ProcSet<ValueUserVO> ValueUser { get; set; } 17 } 18 }
ProcSet存储过程操作类:针对数据库表提供的一系列执行的操作。被ProcSet包含的就是存储过程字段了。这里有点特殊的是,除了是查询的结果集的映射外,包含了参数传入的映射,并且区分in/out,后面会讲到。
4、表实体类:
1 public class UserVO : IEntity<int?> 2 { 3 /// <summary> 用户ID </summary> 4 [Field(IsPrimaryKey = true)] 5 public int? ID { get; set; } 6 /// <summary> 用户名 </summary> 7 [Field()] 8 public string UserName { get; set; } 9 /// <summary> 密码 </summary> 10 public string PassWord { get; set; } 11 /// <summary> 会员类型 </summary> 12 public eumGenderType? GenderType { get; set; } 13 /// <summary> 登陆次数 </summary> 14 public int? LoginCount { get; set; } 15 /// <summary> 登陆IP </summary> 16 public string LoginIP { get; set; } 17 /// <summary> 登陆IP </summary> 18 [Field(Name = "getdate()")] 19 public DateTime? GetDate { get; set; } 20 /// <summary> 创建时间 </summary> 21 public DateTime? CreateAt { get; set; } 22 }
可以看到,我们具体的实体,其实是一个POCO的实体。
没错,在V1.x中,框架的一个改变也是为了减轻实体的”负担“,也让框架更加轻量级,而不是侵入式的。
这里重点说下:[Field(Name = "getdate()")]
在后面我们会讲到Field.Name 是显示的告诉框架这个类属性绑定数据库中对应的表字段名称。
熟悉MSSQL朋友知道,getdate()并不是字段名称,则是MSSQL提供的函数。框架对此也是支持的。
举个例子,表有两个字段,A、B 都是int类型,
我们用SQL对A、B的值相加后进行排序是很容易实现的。
而通过框架,我们可以定义一个类属性,然后Field.Name = "A+B" ,这样我们在排序时,就按照普通的字段进行Desc(o=>o.变量)就可以了。
另外这里继承了IEntity,如前面文章说到的,是为了支持提供的扩展。我们看看IEntity的接口代码吧:
1 namespace FS.Core.Infrastructure 2 { 3 /// <summary> 通过实体类的继承后,后续Set、扩展方法提供针对主键的Where条件 </summary> 4 /// <typeparam name="T"></typeparam> 5 public interface IEntity<T> 6 { 7 /// <summary> 主键ID </summary> 8 T ID { get; set; } 9 } 10 11 /// <summary> 通过实体类的继承后,后续Set、扩展方法提供针对主键的Where条件(默认为int?) </summary> 12 public interface IEntity : IEntity<int?> { } 13 }
接口只有一个属性:ID,大部份,我们的表都有一个主键。这个主键是自动标识的。
继承它后,框架提供了一Where条件自动转换的支持:
1 /// <summary> 2 /// 对List进行分页 3 /// </summary> 4 /// <typeparam name="TEntity">实体类</typeparam> 5 /// <param name="lst">List列表</param> 6 /// <param name="IDs">条件,等同于:o=> IDs.Contains(o.ID) 的操作</param> 7 /// <param name="rpt">Repeater</param> 8 public static List<TEntity> ToList<TEntity>(this IEnumerable<TEntity> lst, List<int> IDs, Repeater rpt) where TEntity : IEntity 9 10 /// <summary> 11 /// 通用的分页方法(多条件) 12 /// </summary> 13 /// <param name="ts">TableSet</param> 14 /// <param name="rpt">Repeater带分页控件</param> 15 /// <typeparam name="TEntity">实体类</typeparam> 16 public static List<TEntity> ToList<TEntity>(this TableSet<TEntity> ts, Repeater rpt) where TEntity : class, IEntity, new() 17 18 /// <summary> 19 /// 获取下一条记录 20 /// </summary> 21 /// <param name="ID">当前ID</param> 22 /// <param name="ts">TableSet</param> 23 /// <typeparam name="TEntity">实体类</typeparam> 24 public static TEntity ToNextEntity<TEntity>(this TableSet<TEntity> ts, int? ID) where TEntity : class, Core.Infrastructure.IEntity, new() 25 { 26 return ts.Where(o => o.ID > ID).Asc(o => o.ID).ToEntity(); 27 } 28 29 /// <summary> 30 /// 获取上一条记录 31 /// </summary> 32 /// <param name="ID">当前ID</param> 33 /// <param name="ts">TableSet</param> 34 /// <typeparam name="TEntity">实体类</typeparam> 35 public static TEntity ToPreviousEntity<TEntity>(this TableSet<TEntity> ts, int? ID) where TEntity : class, Core.Infrastructure.IEntity, new() 36 { 37 return ts.Where(o => o.ID < ID).Desc(o => o.ID).ToEntity(); 38 } 39 /// <summary> 40 /// 获取下一条记录 41 /// </summary> 42 /// <param name="ID">当前ID</param> 43 /// <param name="ts">TableSet</param> 44 /// <typeparam name="TEntity">实体类</typeparam> 45 public static TEntity ToNextEntity<TEntity>(this ViewSet<TEntity> ts, int? ID) where TEntity : class, Core.Infrastructure.IEntity, new() 46 { 47 return ts.Where(o => o.ID > ID).Asc(o => o.ID).ToEntity(); 48 } 49 50 /// <summary> 51 /// 获取上一条记录 52 /// </summary> 53 /// <param name="ID">当前ID</param> 54 /// <param name="ts">TableSet</param> 55 /// <typeparam name="TEntity">实体类</typeparam> 56 public static TEntity ToPreviousEntity<TEntity>(this ViewSet<TEntity> ts, int? ID) where TEntity : class, Core.Infrastructure.IEntity, new() 57 { 58 return ts.Where(o => o.ID < ID).Desc(o => o.ID).ToEntity(); 59 }
当然这里没办法贴出所有的扩展方法来,但让大家知道它的目的就是让我们经常操作的:o.ID == x 、o=> IDs.Contains(o.ID) 这些操作变的更加方便,不需要手动敲代码完成。
5、视图实体类:
1 public class AccountVO 2 { 3 /// <summary> 用户ID </summary> 4 public int? ID { get; set; } 5 /// <summary> 用户名 </summary> 6 public string Name { get; set; } 7 /// <summary> 密码 </summary> 8 public string Pwd { get; set; } 9 /// <summary> 登陆IP </summary> 10 [Field(Name = "getdate()")] 11 public string GetDate { get; set; } 12 }
这里与表是相同的,就不重复的细述了。
6、存储过程实体类:
1 public class InsertUserVO 2 { 3 /// <summary> 用户ID </summary> 4 [Field(IsOutParam = true)] 5 public int? ID { get; set; } 6 /// <summary> 用户名 </summary> 7 [Field(IsInParam = true)] 8 public string UserName { get; set; } 9 /// <summary> 密码 </summary> 10 [Field(IsInParam = true)] 11 public string PassWord { get; set; } 12 }
存储过程与表、视图的区别比较大。在上面代码中,看到使用了IsOutParam和IsInParam的特性。标记后,在执行存储过程时,会根据这些标记生成SQL参数化。传入到存储过程中。
与此同时,如果该条存储过程执行后,会返回数据列表时,也会对应的返回当前这个实体列表。就根据我们对表进行查询时一样。
通过存储过程的映射,我们不需要每次调用时手动指定他们了。让我们存储过程也能ORM了。
以上代码在Demo中。大家可以直接在源代码里面去查找
7、我们先来介绍第一个特性:字段的映射
[FieldAttribute]特性目的是告诉框架,当前的这个类属性与数据库的表字段是如何关联在一起的。比如显式的指定表字段的名称。
我们先来看下面的表格:
序号 | 变量 | 注释 | 类型 | 适用范围 | 说明 |
1 | Name | 数据库字段名称 | string | 全部 |
指定表字段、视图字段名称、存储过程参数的名称、数据库函数 未显示指定时,默认使用当前的类属性名称作为缺省名称 |
2 | IsPrimaryKey | 是否为数据库主键 | bool | TableContext |
默认:false 设为:true 并且该变量赋了值是:Insert操作,在MSSQL里,会启用SET IDENTITY_INSERT设置,在Update时,会转换成条件,并去掉赋值。 比如:Table.Data.User.Update(new User{ ID=1,Name="xxx")); 会被转换成:Table.Data.User.Where(o=>o.ID == 1).Update(new User{ Name="xxx")); |
3 | InsertStatusType | 插入时字段状态 |
StatusType 枚举类型 |
TableContext |
默认:CanWrite 设为:ReadOnly时,在对应的插入或者更新时,该赋值会被过滤掉(忽略) 设为:ReadCondition时,在对应的插入或者更新时,该赋值会被转换成 == 条件。同时过滤掉赋值(忽略) 这个特性运用的好,对我们编写代码时,可以省去很多Where(o=>o.ID == 1)这种写法,以及安全保护。(禁止被更新,即时赋了值) |
4 | UpdateStatusType | 修改时字段状态 | TableContext | ||
5 | IsMap | 是否映射到数据库字段中 | bool | 全部 |
默认:true 设为:false 将不映射数据库 |
6 | IsOutParam | 指示字段是否为存储过程中输出的参数 | bool | ProcContext |
默认:false 设为:true 将作为SQL参数值传入,并配合Name声明参数名称,与InParam区别是Out在数据库存储过程中也要声明为该变量是out 即在存储过程中改变时,会将值返回来 |
7 | IsInParam | 指示字段是否为存储过程中输入的参数 | bool | ProcContext |
默认:false 设为:true 将作为SQL参数值传入,并配合Name声明参数名称 |
目前只有一个Name属性。
序号 | 变量 | 注释 | 类型 | 适用范围 | 说明 |
1 | Name | 表名称、视图名称、存储过程名称 | string | 全部 |
指定表、视图、存储过程的名称 未显示指定时,默认使用当前的类名称作为缺省名称 |
目前只有一个Name属性。
序号 | 变量 | 注释 | 类型 | 适用范围 | 说明 |
1 | ConnStr | 设置数据库连接字符串 | string | 全部 |
这里跟前面说到的数据库配置里的元素很多相同,就不重复在细述了。 当ConnStr未赋值时,框架默认为数据库配置索引项第一项,即如果不是想要动态的情况下,不需要指定这个特性。 可能有时候我们不需要在配置项里保存数据库的信息。那可以在直接在这里进行显示设置。(也可以使用索引的哦) |
1 | DataType | 设置数据库类型 | DataBaseType | 全部 | |
1 | DataVer | 设置数据库版本 | string | 全部 | |
1 | CommandTimeout | 设置数据库执行T-SQL时间,单位秒默认是30秒 | int | 全部 |
在这里值得要说的是,除了用这里的数据库配置环境外,还可以通过BaseContext的构造函数来指定:
public abstract class BaseContext : IDisposable { /// <summary> /// 使用DB特性设置数据库信息 /// </summary> protected BaseContext() { ContextMap = CacheManger.GetContextMap(this.GetType()); DataBase = new DbExecutor(ContextMap.ContextProperty.ConnStr, ContextMap.ContextProperty.DataType, ContextMap.ContextProperty.CommandTimeout); } /// <summary> /// 通过数据库配置,连接数据库 /// </summary> /// <param name="dbIndex">数据库选项</param> protected BaseContext(int dbIndex) : this(CacheManger.CreateConnString(dbIndex), DbConfigs.ConfigEntity.DbList[dbIndex].DataType, DbConfigs.ConfigEntity.DbList[dbIndex].CommandTimeout) { } /// <summary> /// 通过自定义数据链接符,连接数据库 /// </summary> /// <param name="connectionString">数据库连接字符串</param> /// <param name="dbType">数据库类型</param> /// <param name="commandTimeout">SQL执行超时时间</param> protected BaseContext(string connectionString, DataBaseType dbType = DataBaseType.SqlServer, int commandTimeout = 30) { DataBase = new DbExecutor(connectionString, dbType, commandTimeout); ContextMap = CacheManger.GetContextMap(this.GetType()); } }
下面看下如何调用的:
1 [Context()] 2 public class Table : TableContext<Table> 3 { 4 /// <summary> 5 /// 指定数据库配置项 6 /// </summary> 7 public Table() : base(0) { } 8 9 [Set(Name = "Members_User")] 10 public TableSet<UserVO> User { get; set; } 11 12 [Set(Name = "Members_Role", IsCache = true)] 13 public TableSet<UserRoleVO> UserRole { get; set; } 14 }
我们通过显示的指定索引即可。但这通常是用在动态配置情况下才会用到。一般的,如果你要显示的时候,通常是通过特性来实现的,特性也支持数据库配置索引:
1 /// <summary> 2 /// 指定数据库配置项 3 /// </summary> 4 [Context(0)] 5 public class Table : TableContext<Table> 6 { 7 [Set(Name = "Members_User")] 8 public TableSet<UserVO> User { get; set; } 9 10 [Set(Name = "Members_Role", IsCache = true)] 11 public TableSet<UserRoleVO> UserRole { get; set; } 12 }
也可以这样来定义,特性传入的值必须是常量,意味着我们无法”动态“改变。
相信通过上文的说明,大家知道如何进行数据库的映射了。
今天这篇与昨天的,都是环境搭建过程而已。
到了下一篇,就开始讲解实质性的数据库操作了。希望给大家带来惊喜!
QQ群:116228666 (Farseer.net开源框架交流) 请注明:Farseer.Net
Farseer.Net是一款ORM框架 + 常用工具 + 扩展集合。
Farseer 寓意:先知、预言家 通常在某些场合时,提供计谋、策略。也希望该框架能给大家提供最大化的便捷。
ORM:其英文全称是:Object(对象) Relational(关系) Mapping(映射)
Farseer.Net的目标是:快速上手、快速开发、简单方便。
1 Table.Data.User.Where(o=>o.ID == 1).ToEntity(); 2 Table.Data.User.Where(o=>o.ID > 1).ToList(); 3 Table.Data.User.Where(o=>o.ID != 0).Delete(); 4 Table.Data.User.Where(o=>o.ID != 0).AddUp(o=>o.LoginCount, 1); 5 Table.Data.User.Where(o=>o.ID == 1).Update(new User{ UserName = "newName" }); 6 Table.Data.User.Insert(new User{ UserName = "newName" });
Farseer.net轻量级开源框架 V1.x 入门篇:表实体类映射
标签:
原文地址:http://www.cnblogs.com/steden/p/4555262.html