C# 数据操做系列 - 4. 本身实现一个ORM

0. 前言

在以前的几篇内容中,咱们了解了如何经过ADO.NET 访问数据库,如何修改、新增数据。如何经过DataSet和DataAdapter获取数据,咱们将在这一篇试试本身实现一个简单的ORM框架或者说ORM工具类。sql

涉及到的知识点:数据库

  • 反射(初级)
  • ADO.NET 已有知识

1. ORM

那么,问题来了,什么是ORM?ORM全称 Object Relational Mapping,翻译过来就是对象关系映射。是一种经过描述对象与数据库之间映射关系的数据,将对象保存到数据库中的技术。c#

在C#中,曾经Entity Framework光芒万丈,遮盖了其余ORM框架的光辉(甚至现在都是如此)。app

后来慢慢涌现除了其余的一些ORM框架,进一步丰富了市场。因此现有比较流行的大概有如下几种:框架

  • Dapper 一个轻量的ORM框架
  • Entity Framework/Entity Framework Core 功能完备的框架
  • Nhibernate Java平台上著名的Hibernate的.net版
  • 等等

嗯,这是我最近找到的创做组还在更新的几个框架,固然还有其余的不少有趣好用的ORM框架。欢迎各位补充哈。工具

这一篇的主要目的不是介绍这些框架(这是之后的内容),而是经过咱们本身实现一个类ORM框架来了解底层核心。.net

2. 设计

咱们先分析一下,若是咱们设计一个实体对象与数据库之间转换的工具类应该具备哪些功能?hibernate

  • 一个属性与数据库字段的映射关系
  • 增删改查的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这两个类并无讲完。这部份内容可能会在后续的番外篇内补全。

更多内容烦请关注个人博客《高先生小屋》

file

相关文章
相关标签/搜索