一个简单得不能再简单的“ORM”了
本文适合初学者,老鸟请点赞即走,谢谢。
文字功底有限,表述不恰当的地方,请各位多多包涵。
一,核心
现在ORM已经很多了,功能也齐全了,大家说我这是干无聊的事,造的连车轮子都还不算,反正我就当学习。
还有就是,下面这个不算正在的ORM,离真正在ORM差的很远的。
主要思想

二,实例测试
1,基础数据准备
1.1 数据库表结构(sqlite数据库)
       
1.2 实体
public class Msg
{
public string Id { get; set; }
public string Content { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
}
2,开始插入数据
2.1 创建了一个控制台程序做测试
        string connStr = string.Format("Data Source={0};", System.AppDomain.CurrentDomain.BaseDirectory + "App_Data\\db.db");
            WangSql.ISqlExe sqlexe = new WangSql.SqlExe(WangSql.DbType.SQLLITE, connStr);
            string sql = "insert into Msg(Id,Content,Name,CreateTime) values(#Id#,#Content#,#Name#,#CreateTime#)";
            Msg model = new Msg()
            {
                Id = Guid.NewGuid().ToString("N"),
                Content = "这里是内容",
                Name = "姓名",
                CreateTime = DateTime.Now
            };
            sqlexe.NonQuery(sql, model);
查看下数据呢,
    
至此,测试成功,再来测试下呢。
2.2 开8个线程,每个线程循环插入100条数据试试看。
测试结果:好尴尬sqlite多线程容易锁库,以后操作这个库,还是队列吧,楼主本着不放弃不抛弃的精神,再来了一次。
    
这次没被锁,数据库数据呢。
    
数据也没少,OK,测试完成。
三,源码讲解(准确的是代码讲解)
3.1 生成SQL
大家有没有发现,我在执行时传入sql的格式
insert into Msg(Id,Content,Name,CreateTime) values(#Id#,#Content#,#Name#,#CreateTime#)//有没有很熟悉呢,没错,就是借鉴(山寨)的ibatis的,哈哈
就是这个样子的,
表:Msg(Id,Content,Name,CreateTime)
DbDataParameter:values(#Id#,#Content#,#Name#,#CreateTime#)
DbDataParameter,这里支持值类型,string,object,object[],以及Hashtable和用户自定义类。
下面就是生成SQL语句的代码、
public string SqlInit(string sql, out List<string> paraName)
{
string sqlTag = sql;
List<string> sqlParaName = new List<string>();
Regex regex = new Regex("(#(.[^#]+?)#)");
var ms = regex.Matches(sql);
foreach (Match item in ms)
{
var p1 = item.Groups[];
var p2 = item.Groups[];
sqlTag = sqlTag.Replace(p1.Value, prefix + p2.Value);
if (!sqlParaName.Contains(p2.Value))
sqlParaName.Add(p2.Value);
}
paraName = sqlParaName;
return sqlTag;
}
这个就会生成sql,并且还会把Parameter的key以集合的方式抛出去。
3.2 实体转成DbDataParameter
当插入数据,参数传入的是个用户自定义类的话,需要做一次转换。先将实体转成Hashtable,然后再根据3.1生成sql抛出来的Parameter的key来生成Parameter集合。
public static Hashtable ModelToHashtable(object model)
{
Hashtable ht = new Hashtable();
BindingFlags flag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo[] propertys = model.GetType().GetProperties(flag);
foreach (PropertyInfo pi in propertys)
{
string name = pi.Name;
if (!pi.CanRead) continue;
object value = pi.GetValue(model, null);
ht.Add(name, value);
}
return ht;
}
3.3 完整NonQuery执行代码
public int NonQuery(string sql, object para)
{
if (IsModel(para))//Model入参
{
Hashtable ht = DataMapHelper.ModelToHashtable(para);
List<string> paraName = new List<string>();
string sqlTag = SqlInit(sql, out paraName);
IDbDataParameter[] sqlParas = DBFactory.CreateDbDataParameters(type, paraName.Count);
for (int i = ; i < paraName.Count; i++)
{
string key = paraName[i];
object value = ht[key];
sqlParas[i] = DBFactory.CreateDbDataParameter(type, prefix + key, value);
}
var result = helper.ExecuteNonQuery(sqlTag, sqlParas);
return result;
}
else if (para.GetType() == typeof(Hashtable))//Hashtanle入参
{
Hashtable ht = (Hashtable)para;
List<string> paraName = new List<string>();
string sqlTag = SqlInit(sql, out paraName);
IDbDataParameter[] sqlParas = DBFactory.CreateDbDataParameters(type, paraName.Count);
for (int i = ; i < paraName.Count; i++)
{
string key = paraName[i];
object value = ht[key];
sqlParas[i] = DBFactory.CreateDbDataParameter(type, prefix + key, value);
}
var result = helper.ExecuteNonQuery(sqlTag, sqlParas);
return result;
}
else if (para.GetType() == typeof(object[]))
{
List<string> paraName = new List<string>();
string sqlTag = SqlInit(sql, out paraName);
object[] ht = (object[])para;
IDbDataParameter[] sqlParas = DBFactory.CreateDbDataParameters(type, paraName.Count);
for (int i = ; i < paraName.Count; i++)
{
string key = paraName[i];
object value = ht[i];
sqlParas[i] = DBFactory.CreateDbDataParameter(type, prefix + key, value);
}
var result = helper.ExecuteNonQuery(sqlTag, sqlParas);
return result;
}
else//一个参数入参
{
List<string> paraName = new List<string>();
string sqlTag = SqlInit(sql, out paraName);
IDbDataParameter[] sqlParas = DBFactory.CreateDbDataParameters(type, paraName.Count);
for (int i = ; i < paraName.Count; i++)
{
string key = paraName[i];
object value = para;
sqlParas[i] = DBFactory.CreateDbDataParameter(type, prefix + key, value);
}
var result = helper.ExecuteNonQuery(sqlTag, sqlParas);
return result;
}
}
3.4 查询语句
如果是查询的话,执行完SQL返回一个DataTable,操作DataTable也太麻烦了吧,所以利用反射做了个实体转换器。
DataTable转实体Model,DataRow转实体Model,DataTable转泛型T,DataRow转泛型T (之前的文章)
public static T DataRowToModel<T>(DataRow row)
{
T model;
Type type = typeof(T);
ModelType modelType = GetModelType(type);
switch (modelType)
{
case ModelType.Struct://值类型
{
model = default(T);
if (row[] != null)
model = (T)row[];
}
break;
case ModelType.Enum://值类型
{
model = default(T);
if (row[] != null)
{
Type fiType = row[].GetType();
if (fiType == typeof(int))
{
model = (T)row[];
}
else if (fiType == typeof(string))
{
var value = Enum.Parse(typeof(T), row[].ToString());
if (value != null)
model = (T)value;
}
}
}
break;
case ModelType.String://引用类型 c#对string也当做值类型处理
{
model = default(T);
if (row[] != null)
model = (T)row[];
}
break;
case ModelType.Object://引用类型 直接返回第一行第一列的值
{
model = default(T);
if (row[] != null)
model = (T)row[];
}
break;
case ModelType.Else://引用类型
{
model = System.Activator.CreateInstance<T>();//引用类型 必须对泛型实例化
#region MyRegion
//获取model中的属性
BindingFlags flag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
PropertyInfo[] modelPropertyInfos = type.GetProperties(flag);
//遍历model每一个属性并赋值DataRow对应的列
foreach (PropertyInfo pi in modelPropertyInfos)
{
if (!pi.CanWrite) continue;
//获取属性名称
string tempName = GetFieldName(pi);
String name = string.IsNullOrEmpty(tempName) ? pi.Name : tempName;
if (row.Table.Columns.Contains(name) && row[name] != null)
{
ModelType piType = GetModelType(pi.PropertyType);
switch (piType)
{
case ModelType.Struct:
{
var value = Convert.ChangeType(row[name], pi.PropertyType);
pi.SetValue(model, value, null);
}
break;
case ModelType.Enum:
{
Type fiType = row[name].GetType();
if (fiType == typeof(int))
{
pi.SetValue(model, row[name], null);
}
else if (fiType == typeof(string))
{
var value = Enum.Parse(typeof(T), row[name].ToString());
if (value != null)
pi.SetValue(model, (T)value, null);
}
}
break;
case ModelType.String:
{
var value = Convert.ChangeType(row[name], pi.PropertyType);
pi.SetValue(model, value, null);
}
break;
case ModelType.Object:
{
pi.SetValue(model, row[name], null);
}
break;
case ModelType.Else:
//throw new Exception("不支持该类型转换");
break;
default:
throw new Exception("未知类型");
}
}
}
#endregion
}
break;
default:
model = default(T);
break;
} return model;
}
好了,差不多了吧,还有些多谢没讲,可以看我另外两篇博客。
DataTable转实体Model,DataRow转实体Model,DataTable转泛型T,DataRow转泛型T
最后项目截图吧,稍后源码附上。

源码下载:
一个简单得不能再简单的“ORM”了的更多相关文章
- Python入门一:简单得不能再简单了##
		从python的语法上看,简单得不能再简单了. 想学它,请移步廖雪峰python2.7教程以及python3.这实在是最好的入门教程.参考资料太多: 外国的教程 Python 入门指南 Python ... 
- C# 实现IP视频监控(摄像头)画面推送(简单的不能再简单的DEMO)
		最近继续在家休息,在完成上一个Python抓取某音乐网站爬虫后,琢磨着实现一个基于HTTP推送的 IP视频监控,比如外出的时候,在家里 开启一个监控端(摄像头+服务端),可以看到实时画面,如果再加上自 ... 
- grunt配置太复杂?发布一个前端构建工具,简单高效,自动跳过未更新的文件
		做前端项目,如果没有一个自动化构建工具,手动处理那简直就是坑爹O(∩_∩)O.于是上网了解了下,grunt用的人不少,功能也挺强大.看了一下grunt的配置(包括gulp),感觉稍显复杂.当时项目结构 ... 
- 写一个ajax程序就是如此简单
		写一个ajax程序就是如此简单 ajax介绍: 1:AJAX全称为Asynchronous JavaScript and XML(异步JavaScript和XML),指一种创建交互式网页应用的网页开发 ... 
- Android图表库MPAndroidChart(七)—饼状图可以再简单一点
		Android图表库MPAndroidChart(七)-饼状图可以再简单一点 接上文,今天实现的是用的很多的,作用在统计上的饼状图,我们看下今天的效果 这个效果,我们实现,和之前一样的套路,我先来说下 ... 
- 手撸一个SpringBoot的Starter,简单易上手
		前言:今天介绍一SpringBoot的Starter,并手写一个自己的Starter,在SpringBoot项目中,有各种的Starter提供给开发者使用,Starter则提供各种API,这样使开发S ... 
- Bitter ORM NETCORE ORM 全网最粗暴简单易用高性能的 NETCore ORM 开源了
		开源的来了,懒人程序员的福音,.NET 生态闭环太缺开源精神了, 拥抱开源! 前言: 本人不是不喜欢现有ORM的轮子,而是发现现有的ORM 的都不太符合开发人员的一些习惯.现有的ORM 要么功能太冗余 ... 
- VB.net数据库编程(03):一个SQLserver连接查询的简单样例
		这个样例,因为在ADO.net入门已经专门学了,再次进行复习 一下. 主要掌握连接字串的情况. 过程就是: 1.引用System.Data.SqlClient.而Access中引用 的是System. ... 
- 【转】C#类的分类(静态类、实例类、嵌套类、结构、简单的抽象类、简单的密封类)
		静态类 -------------------------------------------------------------------------------- 静态类就是在class关键字前 ... 
随机推荐
- 在Android上使用qemu-user运行可执行文件
			在Android上使用qemu-user运行可执行文件 作者:寻禹@阿里聚安全 前言 QEMU简要介绍: QEMU可以解释执行可执行程序.既然QEMU可以解释执行可执行程序,那么QEMU就能够知道执行 ... 
- Javascript设置对象属性为"只读"
			有时为了保护某些属性,让其无法被更改,我们会把他们设置为常量. 在某些语言里面,也许会用const来实现这样的功能.本文讲述如何在Javascript中实现这样的功能. 方法一: var myObje ... 
- 每周一书-《鸟哥的Linux私房菜基础学习篇(第四版)》台湾原版,你想要吗?
			首先说明,本周活动有效时间为2016年10月19日到2016年10月31日. 目在介绍这本书之前,首先要感谢QQ号为:1084830483(路在远方),来自哈尔滨工程大学的同学赠送给玄魂工作室的 ... 
- iTextSharp 116秒处理6G的文件
			前言: 有一家印刷企业专为米兰新娘,微微新娘,金夫人这样的影楼印刷婚纱相册.通过一个B2B销售终端软件,把影楼的相片上传到印刷公司的服务器,服务器对这些图片进行处理. 比如: 1)为每个图片生成订单条 ... 
- ASP.NET MVC 路由(五)
			ASP.NET MVC 路由(五) 前言 前面的篇幅讲解了MVC中的路由系统,只是大概的一个实现流程,让大家更清晰路由系统在MVC中所做的以及所在的位置,通过模糊的概念描述.思维导图没法让您看到路由的 ... 
- .NET 基础 一步步 一幕幕 [注释、命名规则、访问修饰符、数据类型、常量、变量]
			注释.命名规则.访问修饰符.数据类型.常量.变量 话说一个不会写注释的程序猿的不是一个好吃货,我们本篇就从注释开始说起好了. 在C#中有三种注释: 第一种:单行注释 以//开头,后面的就是注释内容 ... 
- 启动 Apache2.2 的问题
			启动 Apache2.2 的问题: windows不能在本地计算机启动 Apache2.2.有关更多信息,查阅系统事件日志.如果这是非Microsoft服务,请与服务厂商联系,并参考特定服务错误代码1 ... 
- Linux查找命令:grep,awk,sed
			grep grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具 ... 
- Spring<bean>标签是反射来实现的
- iOS中多线程知识总结(一)
			这一段开发中一直在处理iOS多线程的问题,但是感觉知识太散了,所以就把iOS中多线程的知识点总结了一下. 1.基本概念 1)什么是进程?进程的特性是什么? 进程是指在系统中正在运行的一个应用程序. ... 
