前言:
在我们日常开发中,EF或者EfCore通过导航属性实现的自动ORM已经很方便了,其主要围绕实体为中心的ORM,在复杂场景下可能还是通过sql执行方式比较方便,但是有时候我们需要返回的实体和表结构并不相似,所以基于该场景,我们尝试实现一个基础的单表ORM.
原理:
1. 通过DbCommand执行sql拿到返回结果DataTable.
2.通过反射自动将实体属性名和sql query result的列名匹配,赋值.
代码实现:
咱们通过给DataTable写一个泛型扩展方法来方便我们后续使用。
1.efcore中封装执行sql的方法.
public async Task<DataTable> ExecuteSqlWithAdo(string sql,List<Tuple<string,string>> parameters)
{
var dataTable = new DataTable();
try
{
await using var command = _context.Database.GetDbConnection().CreateCommand();
command.CommandText = sql;
//添加查询参数,参数化查询可以避免sql注入
if (parameters.Any())
{
foreach (var parameter in parameters)
{
var sqlParameter = new SqlParameter(parameter.Item1, parameter.Item2);
command.Parameters.Add(sqlParameter);
}
}
await _context.Database.OpenConnectionAsync();
await using var dbDataReader = await command.ExecuteReaderAsync();
dataTable.Load(dbDataReader);
return dataTable;
}
catch (Exception e)
{
throw;
}
}
2. DataTable扩展方法,ToList<T>.
public static List<T> ToList<T>(this DataTable table) where T : class, new()
{
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
//大写
var propDict = properties.ToDictionary(p => p.Name.ToUpper(), p => p);
var list = new List<T>();
//遍历一行数据
foreach (DataRow row in table.Rows)
{
var obj = new T();
//大写
foreach (DataColumn column in table.Columns)
{
if (propDict.TryGetValue(column.ColumnName.ToUpper(), out var prop))
{
if (prop.CanWrite)
{
var value = row[column.ColumnName];
//null的时候也可以考虑给其设置默认值
prop.SetValue(obj, value == DBNull.Value ? null : value);
}
}
}
list.Add(obj);
}
return list;
}
使用:
string sql =
"select p.projectName,s.serviceName from [Project] p join [service] s on s.projectId=p.ProjectId where p.ProjectId=@ProjectId ";
var dataTable = await _wrapper.ProjectRepository.ExecuteSqlWithAdo(sql, [new("@ProjectId", "1")]);
dataTable.ToList<Project>();
通过以上步骤就可以简单实现单表的自动ORM了,避免我们手动进行row, column 重复代码映射了。
此外本博主仍然实现了带层级的ORM,但是由于并不是全自动的,对sql书写有比较严格的要求,如果大家有什么更好的实现带层级的想法,欢迎交流沟通。
以上就是此篇文章的全部。