一个简易的ORM框架的实现(二)
框架目标
什么是框架,框架能做到什么?
把一个方向的技术研发做封装,具备通用性,让使用框架的开发者用起来很轻松。
属性:
- 通用性
- 健壮性
- 稳定性
- 扩展性
- 高性能
- 组件化
- 跨平台
从零开始-搭建框架
- 建立项目
- 主键查询功能开发
- 绑定实体
一步一步的给大家推导:
一边写一边测试
从零开始--搭建框架
1. 创建项目
首先,创建两个类库一个名为Models保存我们的模型,一个名为DbProxy的类库保存我们对数据库的核心操作。
先进行我们查询功能的编写,暂时不考虑通用性。
public class DbProxyCore
{
public Commodity GetCommodity(int id)
{
string connectionString = "Data Source=10.10.32.242;Initial Catalog=AdvancedCustomerDB;Persist Security Info=True;User ID=sa;Password=*******";
Commodity commodity = new Commodity();
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = @"SELECT [Id]
,[ProductId]
,[CategoryId]
,[Title]
,[Price]
,[Url]
,[ImageUrl]
FROM [dbo].[Commodity] where Id="+id;
SqlCommand sqlCommand= connection.CreateCommand();
sqlCommand.CommandText = sql;
SqlDataReader reader= sqlCommand.ExecuteReader();//数据集的读取器
if (reader.Read())
{
commodity.Id = Convert.ToInt32(reader["Id"]);
commodity.ProductId = Convert.ToInt64(reader["ProductId"]);
commodity.CategoryId = Convert.ToInt32(reader["CategoryId"]);
commodity.Title = reader["Title"].ToString();
commodity.Price = Convert.ToDecimal(reader["Price"]);
commodity.Url = reader["Url"].ToString();
commodity.ImageUrl = reader["ImageUrl"].ToString();
}
}
return commodity;
}
}
当我们又创建一个其他的model对象的时候,就遇到一个问题,难道我们需要每次都进行不同对象的独有的方法的创建吗?
并不是,这里就可以通过泛型来完成它们独有的方法
暂时的改造
public T Find<T>(int id) where T : new()
{
string connectionString = """
Data Source=10.10.32.242;Initial Catalog=AdvancedCustomerDB;Persist Security Info=True;User ID=sa;Password=*********;
""";
T obj = new T();
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = @"SELECT [Id]
,[ProductId]
,[CategoryId]
,[Title]
,[Price]
,[Url]
,[ImageUrl]
FROM [dbo].[Commodity] where Id=" + id;
SqlCommand sqlCommand = connection.CreateCommand();
sqlCommand.CommandText = sql;
SqlDataReader reader = sqlCommand.ExecuteReader();//数据集的读取器
if (reader.Read())
{
//commodity.Id = Convert.ToInt32(reader["Id"]);
//commodity.ProductId = Convert.ToInt64(reader["ProductId"]);
//commodity.CategoryId = Convert.ToInt32(reader["CategoryId"]);
//commodity.Title = reader["Title"].ToString();
//commodity.Price = Convert.ToDecimal(reader["Price"]);
//commodity.Url = reader["Url"].ToString();
//commodity.ImageUrl = reader["ImageUrl"].ToString();
}
}
return obj;
}
尝试运行,可以正确的运行,并不报错。
我们要给对象的属性赋值,不能通过new一个对象,直接调用对象的属性赋值;
这里就可以使用到我们的反射技术。
public T Find<T>(int id) where T : new()
{
string connectionString = """
Data Source=10.10.32.242;Initial Catalog=AdvancedCustomerDB;Persist Security Info=True;User ID=sa;Password=7ujm&UJM;
""";
//T obj = new T();
Type type = typeof(T);
object? oResult = Activator.CreateInstance(type);
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = @"SELECT [Id]
,[ProductId]
,[CategoryId]
,[Title]
,[Price]
,[Url]
,[ImageUrl]
FROM [dbo].[Commodity] where Id=" + id;
SqlCommand sqlCommand = connection.CreateCommand();
sqlCommand.CommandText = sql;
SqlDataReader reader = sqlCommand.ExecuteReader();//数据集的读取器
if (reader.Read())
{
//commodity.Id = Convert.ToInt32(reader["Id"]);
//commodity.ProductId = Convert.ToInt64(reader["ProductId"]);
//commodity.CategoryId = Convert.ToInt32(reader["CategoryId"]);
//commodity.Title = reader["Title"].ToString();
//commodity.Price = Convert.ToDecimal(reader["Price"]);
//commodity.Url = reader["Url"].ToString();
//commodity.ImageUrl = reader["ImageUrl"].ToString();
foreach (var prop in type.GetProperties())
{
prop.SetValue(oResult, reader[prop.Name]);
}
}
}
return (T)oResult;
}
还有就是sql语句的问题,如何通过T来生成不同的sql语句。
sql语句应该依赖于我们的泛型T,也通过T来动态生成不同的SQl的语句。
public T Find<T>(int id) where T : new()
{
string connectionString = """
Data Source=10.10.32.242;Initial Catalog=AdvancedCustomerDB;Persist Security Info=True;User ID=sa;Password=********;
""";
//T obj = new T();
Type type = typeof(T);
object? oResult = Activator.CreateInstance(type);
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
List<string> propNameList = type.GetProperties().Select(c => c.Name).ToList();
string strProps = string.Join(",", propNameList);
string sql = $"SELECT {strProps} FROM {type.Name} where Id=" + id;
//以逗号分割的数据库表的字段名称。
SqlCommand sqlCommand = connection.CreateCommand();
sqlCommand.CommandText = sql;
SqlDataReader reader = sqlCommand.ExecuteReader();//数据集的读取器
if (reader.Read())
{
foreach (var prop in type.GetProperties())
{
prop.SetValue(oResult, reader[prop.Name]);
}
}
}
return (T)oResult;
}
处理DBNULL的问题
prop.SetValue(oResult, reader[prop.Name] is DBNull ? null : reader[prop.Name]) ;
这里还需要考虑如何避免传入如何的实体,导致报错的问题。
使用基类约束就能避免这个问题了。