【手撸一个ORM】第八步、查询工具类
一、实体查询
using MyOrm.Commons;
using MyOrm.DbParameters;
using MyOrm.Expressions;
using MyOrm.Mappers;
using MyOrm.Reflections;
using MyOrm.SqlBuilder;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text; namespace MyOrm.Queryable
{
public class MyQueryable<T> where T : class , new ()
{
private readonly string _connectionString; // 要查询的导航属性
private readonly Dictionary<string, string[]> _includeProperties = new Dictionary<string, string[]>(); // Where子句中包含导航属性
private List<string> _whereProperties = new List<string>(); // 导航属性的缓存
private readonly List<MyEntity> _entityCache = new List<MyEntity>(); // Select子句
private readonly List<SelectResolveResult> _selectProperties = new List<SelectResolveResult>(); // 主表信息
private readonly MyEntity _masterEntity; // 查询需要的参数
private readonly MyDbParameters _parameters = new MyDbParameters(); // 是否已经调用过Where方法
private bool _hasInitWhere; // 拼接好的where子句
private string _where; // 拼接好的order by子句
private string _orderBy; // 构造方法
public MyQueryable(string connectionString)
{
_masterEntity = MyEntityContainer.Get(typeof(T));
_connectionString = connectionString;
} #region Include
public MyQueryable<T> Include<TProperty>(Expression<Func<T, TProperty>> expression) where TProperty : IEntity
{
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
var memberExpr = (MemberExpression)expression.Body;
if (memberExpr.Expression != null &&
memberExpr.Expression.NodeType == ExpressionType.Parameter &&
memberExpr.Member.GetType().IsClass)
{
_includeProperties.TryAdd(memberExpr.Member.Name, new string[]{});
}
} return this;
} public MyQueryable<T> Include<TProperty>(
Expression<Func<T, TProperty>> property,
Expression<Func<TProperty, object>> fields) where TProperty : IEntity
{
if (property.Body.NodeType == ExpressionType.MemberAccess)
{
var visitor = new ObjectMemberVisitor();
visitor.Visit(property);
var member = visitor.GetPropertyList().First(); visitor.Clear();
visitor.Visit(fields);
var fieldList = visitor.GetPropertyList(); _includeProperties.TryAdd(member, fieldList.ToArray());
} return this;
} public MyQueryable<T> Include(string navPropertyName)
{
var property = _masterEntity.Properties.Single(p => p.Name == navPropertyName);
if (property != null)
{
if (property.JoinAble)
{
_includeProperties.Add(property.Name, new string[]{});
}
}
return this;
} public MyQueryable<T> Include(string navPropertyName, string[] fields)
{
var property = _masterEntity.Properties.Single(p => p.Name == navPropertyName);
if (property != null)
{
if (property.JoinAble)
{
_includeProperties.Add(property.Name, fields);
}
}
return this;
}
#endregion #region Where
public MyQueryable<T> Where(Expression<Func<T, bool>> expr)
{
if (_hasInitWhere)
{
throw new ArgumentException("每个查询只能调用一次Where方法");
}
_hasInitWhere = true; var condition = new QueryConditionResolver<T>(_masterEntity);
var result = condition.Resolve(expr.Body);
_where = result.Condition;
_parameters.AddParameters(result.Parameters);
_entityCache.AddRange(result.NavPropertyList);
_whereProperties = result.NavPropertyList.Select(p => p.Name).ToList(); return this;
}
#endregion #region OrderBy,ThenOrderBy
public MyQueryable<T> OrderBy<TProperty>(Expression<Func<T, TProperty>> expression,
MyDbOrderBy orderBy = MyDbOrderBy.Asc)
{
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
_orderBy = GetOrderByString((MemberExpression)expression.Body);
if (orderBy == MyDbOrderBy.Desc)
{
_orderBy += " DESC";
}
} return this;
} public MyQueryable<T> ThenOrderBy<TProperty>(Expression<Func<T, TProperty>> expression,
MyDbOrderBy orderBy = MyDbOrderBy.Asc)
{
if (string.IsNullOrWhiteSpace(_orderBy))
{
throw new ArgumentNullException(nameof(_orderBy), "排序字段为空,必须先调用OrderBy或OrderByDesc才能调用此方法");
}
if (expression.Body.NodeType == ExpressionType.MemberAccess)
{
_orderBy += "," + GetOrderByString((MemberExpression)expression.Body);
if (orderBy == MyDbOrderBy.Desc)
{
_orderBy += " DESC";
}
} return this;
}
#endregion #region Select public MySelect<TTarget> Select<TTarget>(Expression<Func<T, object>> expression)
{
var visitor = new SelectExpressionResolver();
visitor.Visit(expression);
_selectProperties.AddRange(visitor.GetPropertyList());
return new MySelect<TTarget>(_connectionString, GetFields(), GetFrom(), _where, _parameters, _orderBy);
} #endregion #region 输出
public List<T> ToList()
{
var fields = GetFields();
var from = GetFrom(); var sqlBuilder = new SqlServerBuilder();
var sql = sqlBuilder.Select(from, fields, _where, _orderBy); var visitor = new SqlDataReaderConverter<T>();
List<T> result;
using (var conn = new SqlConnection(_connectionString))
{
var command = new SqlCommand(sql, conn);
command.Parameters.AddRange(_parameters.Parameters);
conn.Open();
using (var sdr = command.ExecuteReader())
{
result = visitor.ConvertToEntityList(sdr);
}
} return result;
} public List<T> ToPageList(int pageIndex, int pageSize, out int recordCount)
{
var fields = GetFields();
var from = GetFrom();
recordCount = ; var sqlBuilder = new SqlServerBuilder();
var sql = sqlBuilder.PagingSelect(from, fields, _where, _orderBy, pageIndex, pageSize); var command = new SqlCommand(sql);
command.Parameters.AddRange(_parameters.Parameters);
var param = new SqlParameter("@RecordCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
command.Parameters.Add(param); List<T> result; using (var conn = new SqlConnection(_connectionString))
{
conn.Open();
command.Connection = conn;
using (var sdr = command.ExecuteReader())
{
var handler = new SqlDataReaderConverter<T>(_includeProperties.Select(p => p.Key).ToArray());
result = handler.ConvertToEntityList(sdr);
}
} recordCount = (int)param.Value;
return result;
} public T FirstOrDefault()
{
var fields = GetFields();
var from = GetFrom(); var sqlBuilder = new SqlServerBuilder();
var sql = sqlBuilder.Select(from, fields, _where, _orderBy, ); using (var conn = new SqlConnection(_connectionString))
{
conn.Open();
var command = new SqlCommand(sql, conn);
command.Parameters.AddRange(_parameters.Parameters);
var sdr = command.ExecuteReader(); var handler = new SqlDataReaderConverter<T>(_includeProperties.Select(p => p.Key).ToArray());
return handler.ConvertToEntity2(sdr);
}
}
#endregion #region 辅助方法 /// 把要用到的导航属性的MyEntity缓存到一个List里,不需要每次都要到字典中获取
private MyEntity GetIncludePropertyEntityInfo(Type type)
{
var entity = _entityCache.FirstOrDefault(e => e.Name == type.FullName); if (entity != null) return entity; entity = MyEntityContainer.Get(type);
_entityCache.Add(entity);
return entity;
} // 获取Select子句
public string GetFields()
{
if (_selectProperties.Count == )
{
var masterFields = string.Join(
",",
_masterEntity
.Properties
.Where(p => p.IsMap)
.Select(p => $"[{_masterEntity.TableName}].[{p.FieldName}] AS [{p.Name}]")
); if (_includeProperties.Count > )
{
var sb = new StringBuilder(masterFields);
sb.Append(",");
var includeProperties = _includeProperties.OrderBy(i => i); foreach (var property in includeProperties)
{
var prop = _masterEntity.Properties.Single(p => p.Name == property.Key);
var propEntity = GetIncludePropertyEntityInfo(prop.PropertyInfo.PropertyType);
if (property.Value.Length == )
{
sb.Append(
string.Join(",",
propEntity.Properties.Where(p => p.IsMap).Select(p =>
$"[{propEntity.TableName}].[{p.FieldName}] AS [{property.Key}_{p.Name}]"))
);
}
else
{
sb.Append(
string.Join(",",
propEntity.Properties.Where(p =>
p.IsMap && property.Value.Contains(p.Name))
.Select(p =>
$"[{propEntity.TableName}].[{p.FieldName}] AS [{property.Key}_{p.Name}]"))
);
}
} return sb.ToString();
} return masterFields;
}
else
{
_includeProperties.Clear();
var sb = new StringBuilder();
foreach (var property in _selectProperties)
{
if (string.IsNullOrWhiteSpace(property.FieldName))
{
var prop = _masterEntity.Properties.Single(p => p.Name == property.PropertyName);
if (prop != null)
{
sb.Append($",[{_masterEntity.TableName}].[{prop.FieldName}] AS [{property.MemberName}]");
}
}
else
{
if (_masterEntity.Properties.Any(p => p.Name == property.PropertyName))
{
_includeProperties.Add(property.PropertyName, new string[] {});
var prop = _masterEntity.Properties.Single(p => p.Name == property.PropertyName);
var propEntity = GetIncludePropertyEntityInfo(prop.PropertyInfo.PropertyType); var field = propEntity.Properties.Single(p => p.Name == property.FieldName);
if (field != null)
{
sb.Append(
$",[{property.PropertyName}].[{field.FieldName}] AS [{property.MemberName}]");
}
}
}
} return sb.Remove(, ).ToString();
}
} // 获取From子句
public string GetFrom()
{
var masterTable = $"[{_masterEntity.TableName}]";
var allJoinProperties = _includeProperties.Select(p => p.Key).Concat(_whereProperties).Distinct().ToList(); if (allJoinProperties.Any())
{
var sb = new StringBuilder(masterTable);
foreach (var property in allJoinProperties)
{
var prop = _masterEntity.Properties.Single(p => p.Name == property);
if (prop != null)
{
var propEntity = GetIncludePropertyEntityInfo(prop.PropertyInfo.PropertyType);
sb.Append($" LEFT JOIN [{propEntity.TableName}] AS [{property}] ON [{_masterEntity.TableName}].[{prop.ForeignKey}]=[{propEntity.TableName}].[{propEntity.KeyColumn}]");
}
} return sb.ToString();
} return masterTable;
} // 获取OrderBy子句
private string GetOrderByString(MemberExpression expression)
{
expression.GetRootType(out var stack);
if (stack.Count == )
{
var propName = stack.Pop();
var prop = _masterEntity.Properties.Single(p => p.Name == propName);
return $"[{_masterEntity.TableName}].[{prop.FieldName}]";
} if (stack.Count == )
{
var slavePropName = stack.Pop();
var propertyName = stack.Pop(); var masterProp = _masterEntity.Properties.Single(p => p.Name == propertyName);
var slaveEntity = GetIncludePropertyEntityInfo(masterProp.PropertyInfo.PropertyType);
var slaveProperty = slaveEntity.Properties.Single(p => p.Name == slavePropName); return $"[{masterProp.Name}].[{slaveProperty.FieldName}]";
} return string.Empty;
} #endregion
}
}
二、按需查询 Select<T>()
using MyOrm.DbParameters;
using MyOrm.Mappers;
using MyOrm.SqlBuilder;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient; namespace MyOrm.Queryable
{
public class MySelect<T>
{
private readonly string _connectionString;
private readonly string _fields;
private readonly string _table;
private readonly string _where;
private readonly string _orderBy;
private readonly MyDbParameters _parameters; public MySelect(string connectionString, string fields, string table, string where, MyDbParameters dbParameters, string orderBy)
{
_fields = fields;
_table = table;
_where = where;
_parameters = dbParameters;
_orderBy = orderBy;
_connectionString = connectionString;
} public List<T> ToList()
{ var sqlBuilder = new SqlServerBuilder();
var sql = sqlBuilder.Select(_table, _fields, _where, _orderBy); var visitor = new SqlDataReaderMapper();
List<T> result;
using (var conn = new SqlConnection(_connectionString))
{
var command = new SqlCommand(sql, conn);
command.Parameters.AddRange(_parameters.Parameters);
conn.Open();
using (var sdr = command.ExecuteReader())
{
result = visitor.ConvertToList<T>(sdr);
}
} return result;
} //public List<dynamic> DynamicList()
//{
// var sqlBuilder = new SqlServerBuilder();
// var sql = sqlBuilder.Select(_table, _fields, _where, _orderBy); // var visitor = new SqlDataReaderMapper();
// List<dynamic> result;
// using (var conn = new SqlConnection(_connectionString))
// {
// var command = new SqlCommand(sql, conn);
// command.Parameters.AddRange(_parameters.Parameters);
// conn.Open();
// using (var sdr = command.ExecuteReader())
// {
// result = visitor.ConvertToList(sdr);
// }
// } // return result;
//} public List<T> ToPageList(int pageIndex, int pageSize, out int recordCount)
{
recordCount = ; var sqlBuilder = new SqlServerBuilder();
var sql = sqlBuilder.PagingSelect2008(_table, _fields, _where, _orderBy, pageIndex, pageSize); var command = new SqlCommand(sql);
command.Parameters.AddRange(_parameters.Parameters);
var param = new SqlParameter("@RecordCount", SqlDbType.Int) { Direction = ParameterDirection.Output };
command.Parameters.Add(param); List<T> result; using (var conn = new SqlConnection(_connectionString))
{
conn.Open();
command.Connection = conn;
using (var sdr = command.ExecuteReader())
{
var handler = new SqlDataReaderMapper();
result = handler.ConvertToList<T>(sdr);
}
} recordCount = (int)param.Value;
return result;
} //public List<dynamic> ToPageListDynamic(int pageIndex, int pageSize, out int recordCount)
//{
// recordCount = 0; // var sqlBuilder = new SqlServerBuilder();
// var sql = sqlBuilder.PagingSelect2008(_table, _fields, _where, _orderBy, pageIndex, pageSize); // var command = new SqlCommand(sql);
// command.Parameters.AddRange(_parameters.Parameters);
// var param = new SqlParameter("@RecordCount", SqlDbType.Int) {Direction = ParameterDirection.Output};
// command.Parameters.Add(param); // List<dynamic> result; // using (var conn = new SqlConnection(_connectionString))
// {
// conn.Open();
// command.Connection = conn;
// using (var sdr = command.ExecuteReader())
// {
// var handler = new SqlDataReaderMapper();
// result = handler.ConvertToList(sdr);
// }
// } // recordCount = (int) param.Value;
// return result;
//} public T FirstOrDefault()
{
var sqlBuilder = new SqlServerBuilder();
var sql = sqlBuilder.Select(_table, _fields, _where, _orderBy, ); using (var conn = new SqlConnection(_connectionString))
{
conn.Open();
var command = new SqlCommand(sql, conn);
command.Parameters.AddRange(_parameters.Parameters);
var sdr = command.ExecuteReader(); var handler = new SqlDataReaderMapper();
return handler.ConvertToEntity<T>(sdr);
}
} //public dynamic FirstOrDefaultDynamic()
//{
// var sqlBuilder = new SqlServerBuilder();
// var sql = sqlBuilder.Select(_table, _fields, _where, _orderBy, 1); // using (var conn = new SqlConnection(_connectionString))
// {
// conn.Open();
// var command = new SqlCommand(sql, conn);
// command.Parameters.AddRange(_parameters.Parameters);
// var sdr = command.ExecuteReader(); // var handler = new SqlDataReaderMapper();
// return handler.ConvertToEntity(sdr);
// }
//}
}
}
【手撸一个ORM】第八步、查询工具类的更多相关文章
- 【手撸一个ORM】第一步、实体约定和描述
一.约定 数据实体必须实现 IEntity 接口,该接口定义了一个int类型的Id属性,既每个实体必须有一个名称为Id的自增主键. 若数据表的主键列名称不是Id,可以通过 [MyKey("主 ...
- 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程
这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...
- 【手撸一个ORM】MyOrm的使用说明
[手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...
- 【手撸一个ORM】第六步、对象表达式解析和Select表达式解析
说明 一个Orm自然不仅仅包含条件表达式,还会有如下的场景: OrderBy(s => s.StudentName) Select<StudentDto>(s => new S ...
- 【手撸一个ORM】第七步、SqlDataReader转实体
说明 使用Expression(表达式目录树)转Entity的文章在园子里有很多,思路也大致也一样,我在前面有篇文章对解决思路有些说明,有兴趣的小伙伴可以看下 (传送门),刚接触表达式目录树时写的,不 ...
- 【手撸一个ORM】第十步、数据操作工具类 MyDb
说明 其实就是数据库操作的一些封装,很久不用SqlCommand操作数据库了,看了点园子里的文章就直接上手写了,功能上没问题,但写法上是否完美高效无法保证,建议有需要的朋友自己重写,当然如果能把最佳实 ...
- 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装
既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...
- 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句
说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...
- 【手撸一个ORM】第四步、Expression(表达式目录树)扩展
到这里,Orm的基架已经搭起来了,接下来就是激动人心的部分,表达式目录树转Sql语句,SqlDataReader转数据实体等等,但是在这之前,我们需要扩展下表达式目录树的方法,以方便后面的相关操作. ...
随机推荐
- 每天一个linux命令(3):cd命令
版权声明 更新:2017-04-27博主:LuckyAlan联系:liuwenvip163@163.com声明:吃水不忘挖井人,转载请注明出处! 1 文章介绍 本文介绍了Linux下命令cd. 2 开 ...
- bzoj 3527: [Zjoi2014]力 快速傅里叶变换 FFT
题目大意: 给出n个数\(q_i\)定义 \[f_i = \sum_{i<j}{\frac{q_iq_j}{(i-j)^2}} - \sum_{i>j}\frac{q_iq_j}{(i-j ...
- 【转】Pro Android学习笔记(七):了解Content Provider(下上)
我们通过一个Content Provider小例子进行详细说明.数据源是一个SQLite数据库,名字为books.db,该数据库只含有一个表格,名字为books.表格中含有name,isbn,auth ...
- MYSQL学习二 关于左连接
工作中有如下的SQL, 针对A.ID ='abcdefg', left join B和C两个表,来查找其他信息.就算是B和C中没有任何满足条件的记录,最后结果也肯定不是空.因为A.ID ='abc ...
- Balloon Comes! hdu(小数位数处理)
Balloon Comes! Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Submission(s): A ...
- 转载:SharePoint 2010 自定义 字段 类型--------省市区联动
最近有几个朋友问到了有关自定义字段类型的问题,为了让更多的人了解自定义字段类型的方法,特写一篇博客与大家分享,首先看一下解决方案目录 创建自定义类型分以下几个步骤: 第一步:添加SharePoint映 ...
- 用expressjs写RESTful API
http://blog.csdn.net/kiwi_coder/article/details/36424671 用expressjs写RESTful API http://blog.csdn ...
- idea救命篇--误删文件恢复
删除.覆盖文件恢复:右键文件--Local History 查到被删的代码,idea自动保存的. 即使文件目录文件被删了,在同地方新建一个同名空文件,也可以通过Local History找回来代码.
- MultiBinding的StringFormat参数问题
在wpf的绑定中,我们会用到多值绑定,如下: <MultiBinding Mode="OneWay" StringFormat="{3}({0}/{1}):{2}& ...
- freemaker宏的用法
freemaker宏 定义:定义一个标签,标签体中可以包含参数,开始标签和结束标签可以包含内容,内容中可以通过${}方式引用标签体中定义的参数 用法:页面引入标签,通过标签可以直接输出标签的内容 He ...