C# 数据操作系列 - 4. 自己实现一个ORM
0. 前言
在之前的几篇内容中,我们了解了如何通过ADO.NET 访问数据库,如何修改、新增数据。如何通过DataSet和DataAdapter获取数据,我们将在这一篇试试自己实现一个简单的ORM框架或者说ORM工具类。
涉及到的知识点:
- 反射(初级)
- ADO.NET 已有知识
1. ORM
那么,问题来了,什么是ORM?ORM全称 Object Relational Mapping,翻译过来就是对象关系映射。是一种通过描述对象与数据库之间映射关系的数据,将对象保存到数据库中的技术。
在C#中,曾经Entity Framework光芒万丈,遮盖了其他ORM框架的光辉(甚至如今都是如此)。
后来慢慢涌现除了其他的一些ORM框架,进一步丰富了市场。所以现有比较流行的大概有以下几种:
- Dapper 一个轻量的ORM框架
- Entity Framework/Entity Framework Core 功能完备的框架
- Nhibernate Java平台上著名的Hibernate的.net版
- 等等
嗯,这是我最近找到的创作组还在更新的几个框架,当然还有其他的很多有趣好用的ORM框架。欢迎各位补充哈。
这一篇的主要目的不是介绍这些框架(这是以后的内容),而是通过我们自己实现一个类ORM框架来了解底层核心。
2. 设计
我们先分析一下,如果我们设计一个实体对象与数据库之间转换的工具类应该具有哪些功能?
- 一个属性与数据库字段的映射关系
- 增删改查的SQL模板
- 查询结果与对象的转换
3. 实现
首先,声明一个类,因为不能仅支持一种类型,所以这个类的所有与数据库有关的方法都是泛型方法,或者这个类是泛型类,所以定义为泛型类:
public class OrmUtil<T>
{
}
我们事先约定类名即表名,属性名即表的列名,所以我们可以快速得到以下内容:
/// <summary>
/// T的类型实例
/// </summary>
private Type dType;
/// <summary>
/// T的属性表
/// </summary>
private PropertyInfo[] properties;
public OrmUtil()
{
dType = typeof(T);
properties = dType.GetProperties();
}
声明一个数据库连接:
public SqlConnection Connection { get; set; }
创建一个私有方法,检查连接是否可用:
/// <summary>
/// 检查连接是否可用
/// </summary>
/// <returns></returns>
private bool CheckConnection()
{
return Connection?.State == ConnectionState.Open;
}
准备工作完成,然后开始编写具体的业务方法:
Insert:
public int Insert(T entity)
{
if (!CheckConnection()) return -1;// 检查状态
var insert = $"insert into {dType.Name}({string.Join(",", properties.Select(t => t.Name))})";
var values = properties.Select(p => p.GetValue(entity));
var commandText = $"{insert} values('{string.Join("','", values)}')";
var command = Connection.CreateCommand();
command.CommandText = commandText;
var result = command.ExecuteNonQuery();
return result;
}
首先按照属性名与列名之间的映射拼接 SQL,然后执行SQL命令。
Update:
public int Update(T entity,string keyName,object keyValue)
{
if (!CheckConnection()) return -1;
var setValues = properties.ToDictionary(p => p.Name, p => $"'{p.GetValue(entity)}'");
var setSql = string.Join(",", setValues.Select(pair=>$"{pair.Key}='{pair.Value}'"));
var sql = $"update {dType.Name} set {setSql} where {keyName} = '{keyValue}'";
var command = Connection.CreateCommand();
command.CommandText = sql;
return command.ExecuteNonQuery();
}
Update需要注意的就是如何正确拼接赋值sql。
Delete:
删除满足条件的对象:
public int Delete(T entity)
{
if (!CheckConnection()) return -1;
var querySet = properties.Select(p => $"{p.Name} = '{p.GetValue(entity)}'");
var sql = $"delete from {dType.Name} where {string.Join(" and ", querySet)}";
var command = Connection.CreateCommand();
command.CommandText = sql;
return command.ExecuteNonQuery();
}
这里写法有时候根据实际业务不同,大多数情况下删除主键对应的元素,或者满足某一个条件的所有元素。这里只是做了个演示,小伙伴们可以试试自己改造一下。
Search:
先创建一个从DataTable转成对象的工具方法:
private List<T> Convert(DataTable table)
{
var list = new List<T>(table.Rows.Count);//事先声明一下容量
foreach(DataRow row in table.AsEnumerable())
{
T entity = Activator.CreateInstance<T>();
foreach(var p in properties)
{
if (!table.Columns.Contains(p.Name)) continue;// 如果属性名不在表格中,则忽略
p.SetValue(entity, row[p.Name]);
}
list.Add(entity);
}
return list;
}
好,我们写一个查询方法:
public List<T> SearchAll()
{
var adapter = new SqlDataAdapter($"select * from {dType.Name}", Connection);
var set = new DataSet();
adapter.Fill(set);
return Convert(set.Tables[0]);
}
这样一个简单的ORM框架就这样形成雏形了,当然实际上的ORM底层比这复杂,因为需要支持不同的数据库,所以Connection 就不能简简单单的是一个SqlConnection了,或者底层不是像我们一样取巧使用DataTable了。
实际上的DataTable到类对象的转换要比我写的复杂一点,因为还要判断这个属性是否是可读、可写的。
4. 总结
在这里我做了个抛砖引玉,带领小伙伴们一起构思了一个简陋的ORM框架,也让大伙对此有了一定的印象。嗯,今天就到这了。同时ADO.NET 也告一段落了,接下来就是上Entity Framework了。当然,DataSet、DataAdapter这两个类并没有讲完。这部分内容可能会在后续的番外篇内补全。
更多内容烦请关注我的博客《高先生小屋》

C# 数据操作系列 - 4. 自己实现一个ORM的更多相关文章
- C# 数据操作系列 - 6 EF Core 配置映射关系
0. 前言 在<C# 数据操作系列 - 5. EF Core 入门>篇中,我们简单的通过两个类演示了一下EF增删改查等功能.细心的小伙伴可能看了生成的DDL SQL 语句,在里面发现了些端 ...
- C# 数据操作系列 - 8. EF Core的增删改查
0.前言 到目前为止,我们看了一下如何声明EF Core的初步使用,也整体的看了下EF Core的映射关系配置以及导航属性的配置. 这一篇,我带大家分享一下,我在工作中需要的EF Core的用法. 1 ...
- C# 数据操作系列 - 12 NHibernate的增删改查
0. 前言 上一篇<C# 数据操作系列 - 11 NHibernate 配置和结构介绍> 介绍了Nhibernate里的配置内容.这一篇将带领大家了解一下如何使用NHIbernate.之前 ...
- C# 数据操作系列 - 15 SqlSugar 增删改查详解
0. 前言 继上一篇,以及上上篇,我们对SqlSugar有了一个大概的认识,但是这并不完美,因为那些都是理论知识,无法描述我们工程开发中实际情况.而这一篇,将带领小伙伴们一起试着写一个能在工程中使用的 ...
- C# 数据操作系列 - 16 SqlSugar 完结篇
0. 前言 前一篇我们详细的介绍了SqlSugar的增删改查,那些已经满足我们在日常工程开发中的使用了.但是还有一点点在开发中并不常用,但是却非常有用的方法.接下来让我们一起来看看还有哪些有意思的内容 ...
- C# 数据操作系列 - 19 FreeSql 入坑介绍
0. 前言 前几天FreeSql的作者向我推荐了FreeSql框架,想让我帮忙写个文章介绍一下.嗯,想不到我也能带个货了.哈哈,开个玩笑-看了下觉得设计的挺有意思的,所以就谢了这篇文章. 简单介绍一下 ...
- C# 数据操作系列 - 1. SQL基础操作
0.前言 前篇介绍了一些数据库的基本概念和以及一些常见的数据库,让我们对数据库有了一个初步的认识.这一篇我们将继续为C#数据操作的基础填上一个空白-SQL语句. SQL(Structured Quer ...
- C# 数据操作系列 - 0. 序言
0. 前言 在上一个系列中,我们初步浏览了一下C#的基础知识.这句话的意思就是C#基础知识系列完结了,撒花.当然,并不是因为C#已经讲完了.正是因为我们轻轻地叩开了那扇门,才能看到门后面那瑰丽的世界. ...
- C# 数据操作系列 - 2. ADO.NET操作
0.前言 在上一篇中初略的介绍了一下SQL的基本写法,这一篇开始我们正式步入C#操作数据库的范围.通过这一系列的内容,我想大家能对于数据库交互有了一定的认识和基础.闲话不多说,先给大家介绍一个C#操作 ...
随机推荐
- K - Two Contests
题目连接:https://atcoder.jp/contests/agc040/tasks/agc040_b 大佬题解:https://blog.csdn.net/duanghaha/article/ ...
- 如何防止CDN防护被绕过
当攻击者发现目标站点存在CDN防护的时候,会尝试通过查找站点的真实IP,从而绕过CDN防护.我们来看一个比较常见的基于公有云的高可用架构,如下:CDN(入口层)->WAF(应用层防护)-> ...
- [转]ThinkCMF框架任意内容包含漏洞分析复现
0x00 简介 ThinkCMF是一款基于PHP+MYSQL开发的中文内容管理框架,底层采用ThinkPHP3.2.3构建.ThinkCMF提出灵活的应用机制,框架自身提供基础的管理功能,而开发者可以 ...
- Python 类学习的一些Tips
这里不详细介绍类,只总结一些小萌新在学习python 类时会有的一些疑点. 类的私有性 在python中,属性和方法的访问权限只有两种,公开的,和私有的.在给属性命名时用两个“__”下划线作为开头,就 ...
- SpringCloud(二)笔记之Eureka
Eureka包含两个组件:Eureka Server和Eureka Client Eureka Server:提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册 Eureka ...
- tensorflow1.0 构建lstm做图片分类
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #this is data mni ...
- LeetCode466. Count The Repetitions
题目链接 传送门 题意 定义一个特殊的串, 现在给出串S1和S2的参数, 问: S2最多可以多少个连接起来扔是S1的子序列, 求出这个最大值 解题思路 注意s1与S1的区别, 可以去看题目描述, 预处 ...
- 二、Go语言开发环境安装与编写第一个Hello World
本系列文章均为学习过程中记录的笔记,欢迎和我一起来学习Go语言. 全文使用环境如下: Go语言版本:1.13 操作系统:deepin 使用工具:Goland开发工具 Go语言追溯历史 Go语言2009 ...
- SSL/TLS 漏洞“受戒礼”,RC4算法关闭
SSL/TLS 漏洞"受戒礼" 一.漏洞分析 事件起因 2015年3月26日,国外数据安全公司Imperva的研究员Itsik Mantin在BLACK HAT ASIA 2015 ...
- Java 后台设置session成功,获取为空
cookie secure当服务器使用https时,容易出现漏洞SSL cookie without secure flag set,敏感cookie这时就需要打开cookie secure,服务器端 ...