【手撸一个ORM】第二步、封装实体描述和实体属性描述
一、实体属性描述 [MyProperty.cs]
- Name,属性名称
- PropertyInfo,反射获取的属性信息,后面很多地方需要通过该属性获取对应的实体类型,或调用SetValue进行赋值
- FieldName,对应的数据表列名
- IsKey,是否主键
- IsMap,查询时是否映射该属性,若属性非值类型或string,则默认为false,其他默认为true;若需手动设置为false,如计算属性,需要在MyColumnAttribute中配置 IsMap=false
- InsertIgnore,插入时忽略
- UpdateIgnore,更新时忽略
- JoinAble,是否可以通过Join进行查询,等同于导航属性
- ForignKey,如果可以通过Join查询,对应的外键名
- MasterKey,如果可以通过Join查询,对应的主表Id,默认为Id,若主表主键列名不是Id,需手动在MyForeignKey中配置 MasterKey="StudentId"
using System.Reflection; namespace MyOrm.Reflections
{
public class MyProperty
{
public string Name { get; set; } public PropertyInfo PropertyInfo { get; set; }public string FieldName { get; set; } public bool IsKey { get; set; } public bool IsMap { get; set; } = true; public bool InsertIgnore { get; set; } public bool UpdateIgnore { get; set; } public bool JoinAble { get; set; } public string ForeignKey { get; set; } public string MasterKey { get; set; } public MyProperty(PropertyInfo property)
{
Name = property.Name;
TypeName = property.PropertyType.Name;
PropertyInfo = property; if (property.IsMapAble())
{
// 判断是否主键
var keyAttribute = property.GetKeyAttribute();
if (keyAttribute != null)
{
// 有
IsKey = true;
FieldName = string.IsNullOrWhiteSpace(keyAttribute.FieldName) ? Name : keyAttribute.FieldName;
if (keyAttribute.IsIncrement)
{
// 如果是自增列,不能插入和修改
InsertIgnore = true;
UpdateIgnore = true;
}
else
{
// 如果不是自增列,可插入但不能修改
InsertIgnore = true;
}
}
else if (Name == "Id")
{
FieldName = "Id";
IsKey = true;
InsertIgnore = true;
UpdateIgnore = true;
}
else
{
// 可映射的属性
var columnAttribute = property.GetMyColumnAttribute(); if (columnAttribute != null)
{
FieldName = string.IsNullOrWhiteSpace(columnAttribute.ColumnName)
? Name
: columnAttribute.ColumnName;
InsertIgnore = columnAttribute.Ignore || columnAttribute.InsertIgnore;
UpdateIgnore = columnAttribute.Ignore || columnAttribute.UpdateIgnore;
}
else
{
FieldName = Name;
}
}
}
else if (property.IsJoinAble())
{
// 可关联查询的属性
IsMap = false;
JoinAble = true;
UpdateIgnore = true;
InsertIgnore = true;
var foreignAttribute = property.GetForeignKeyAttribute();
if (foreignAttribute == null)
{
ForeignKey = Name + "Id";
MasterKey = "Id";
}
else
{
ForeignKey = string.IsNullOrWhiteSpace(foreignAttribute.ForeignKey)
? Name + "Id"
: foreignAttribute.ForeignKey;
MasterKey = string.IsNullOrWhiteSpace(foreignAttribute.MasterKey)
? "Id"
: foreignAttribute.MasterKey;
}
}
else
{
// 其他属性
IsMap = false;
UpdateIgnore = true;
InsertIgnore = true;
}
}
}
}
二、数据实体描述 [MyEntity.cs]
- KeyColumn,数据表中主键列名称
- Name,实体名称
- TableName,实体对应的数据表名称
- IsSoftDelete,是否软删除(继承ISoftDelete),后面会有说明
- IsCreateAudit,是否创建审计(保存创建人、创建时间)
- IsUpdateAudit,是否更新审计(保存修改人、修改时间)
- Properties,封装过的属性信息列表-考虑过这里用字典保存,但是因为后面需要大量的遍历操作,个人感觉还是List用起来方便,所以最终选择了List类型
using MyOrm.Attributes;
using MyOrm.Commons;
using System;
using System.Collections.Generic; namespace MyOrm.Reflections
{
public class MyEntity
{
public string KeyColumn { get; set; } public string Name { get; set; } public string TableName { get; set; } public bool IsSoftDelete { get; set; } public bool IsCreateAudit { get; set; } public bool IsUpdateAudit { get; set; } public List<MyProperty> Properties { get; set; } public MyEntity(Type type)
{
Name = type.Name;
IsSoftDelete = type.IsInstanceOfType(typeof(ISoftDelete));
IsCreateAudit = type.IsInstanceOfType(typeof(ICreateAudit));
IsUpdateAudit = type.IsInstanceOfType(typeof(IUpdateAudit)); var tableAttr = type.GetCustomAttributes(typeof(MyTableAttribute), false);
if (tableAttr.Length > )
{
var tableName = ((MyTableAttribute)tableAttr[]).TableName;
TableName = string.IsNullOrWhiteSpace(tableName) ? type.Name.Replace("Entity", "") : tableName;
}
else
{
TableName = Name;
} Properties = new List<MyProperty>(); foreach (var propertyInfo in type.GetProperties())
{
var property = new MyProperty(propertyInfo);
if (property.IsKey)
{
KeyColumn = property.FieldName;
}
Properties.Add(property);
}
}
}
}
三、实体容器
上面对实体及其属性进行了封装,但是如果每次都需要反射获取,那性能损耗会非常厉害,因此将生成的内容缓存在起来便十分必要了。这里使用的是线程安全的静态字典作为缓存容器。
using System;
using System.Collections.Concurrent; namespace MyOrm.Reflections
{
public class MyEntityContainer
{
private static readonly ConcurrentDictionary<string, MyEntity> Dict =
new ConcurrentDictionary<string, MyEntity>(); public static MyEntity Get(Type type)
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (Dict.TryGetValue(type.FullName ?? throw new InvalidOperationException(), out var result))
{
return result;
}
else
{
var entity = new MyEntity(type);
Dict.TryAdd(type.FullName, entity);
return entity;
}
}
}
}
【手撸一个ORM】第二步、封装实体描述和实体属性描述的更多相关文章
- 【手撸一个ORM】MyOrm的使用说明
[手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...
- 【手撸一个ORM】第一步、实体约定和描述
一.约定 数据实体必须实现 IEntity 接口,该接口定义了一个int类型的Id属性,既每个实体必须有一个名称为Id的自增主键. 若数据表的主键列名称不是Id,可以通过 [MyKey("主 ...
- 【手撸一个ORM】第六步、对象表达式解析和Select表达式解析
说明 一个Orm自然不仅仅包含条件表达式,还会有如下的场景: OrderBy(s => s.StudentName) Select<StudentDto>(s => new S ...
- 【手撸一个ORM】第七步、SqlDataReader转实体
说明 使用Expression(表达式目录树)转Entity的文章在园子里有很多,思路也大致也一样,我在前面有篇文章对解决思路有些说明,有兴趣的小伙伴可以看下 (传送门),刚接触表达式目录树时写的,不 ...
- 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装
既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...
- 【手撸一个ORM】第十步、数据操作工具类 MyDb
说明 其实就是数据库操作的一些封装,很久不用SqlCommand操作数据库了,看了点园子里的文章就直接上手写了,功能上没问题,但写法上是否完美高效无法保证,建议有需要的朋友自己重写,当然如果能把最佳实 ...
- 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句
说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...
- 【手撸一个ORM】第四步、Expression(表达式目录树)扩展
到这里,Orm的基架已经搭起来了,接下来就是激动人心的部分,表达式目录树转Sql语句,SqlDataReader转数据实体等等,但是在这之前,我们需要扩展下表达式目录树的方法,以方便后面的相关操作. ...
- 【手撸一个ORM】第八步、查询工具类
一.实体查询 using MyOrm.Commons; using MyOrm.DbParameters; using MyOrm.Expressions; using MyOrm.Mappers; ...
随机推荐
- hdu-5816 Hearthstone(状压dp+概率期望)
题目链接: Hearthstone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Other ...
- leetcode 191 Number of 1 Bits(位运算)
Write a function that takes an unsigned integer and returns the number of ’1' bits it has (also know ...
- bzoj 2159 Crash 的文明世界 & hdu 4625 JZPTREE —— 第二类斯特林数+树形DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2159 使用公式:\( n^{k} = \sum\limits_{i=0}^{k} S(k,i ...
- bzoj 3501 PA2008 Cliquers Strike Back —— 贝尔数
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3501 用贝尔三角预处理贝尔数,拆模数并在 \( p \) 进制下使用公式,因为这样每次角标增 ...
- 洛谷P1144——最短路计数
题目:https://www.luogu.org/problemnew/show/P1144 spfa跑最短路的同时记录cnt数组表示到达方案数. 代码如下: #include<iostream ...
- POJ 3258 最小值最大化 二分搜索
题意:牛要到河对岸,在与河岸垂直的一条线上,河中有N块石头,给定河岸宽度L,以及每一块石头离牛所在河岸的距离, 现在去掉M块石头,要求去掉M块石头后,剩下的石头之间以及石头与河岸的最小距离的最大值. ...
- PPI协议详解 ppi通讯协议 ppi通信协议 vb与ppi协议通讯
转自:http://blog.csdn.net/vbvcde/article/details/7660497 我们提供 PPI协议的官方文档,协议更新时间为2005年,下面是我们根据文档解析的PPI读 ...
- Swiper 滑动切换图片(可用于PC端,移动端)
作为一名后端的普通程序猿, 你让我搞这种前端不是跟我玩命吗,所以用插件来搞,省事又简单,而且Swiper使用又简单是吧: 头皮发麻,不喜欢说废话,我更喜欢直接看到效果: 按Swiper官方文档来说, ...
- Linux命令总结_查看主机磁盘使用
1.dh -h 查看各个挂载点的使用量 2.du -sh *(星号表示当前所有文件夹)可以查看当前目录下各个文件夹的大小,-s表示只显示当前文件夹(不加-s你可以看到所有文件夹下的子文件夹的大小,太多 ...
- 转载TCP-IP协议解释
本文转载自 http://www.ruanyifeng.com/blog/2009/03/tcp-ip_model.html TCP/IP模型是互联网的基础, 理解 TCP/IP对理解互联网至关重要 ...