一、实体查询

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】第八步、查询工具类的更多相关文章

  1. 【手撸一个ORM】第一步、实体约定和描述

    一.约定 数据实体必须实现 IEntity 接口,该接口定义了一个int类型的Id属性,既每个实体必须有一个名称为Id的自增主键. 若数据表的主键列名称不是Id,可以通过 [MyKey("主 ...

  2. 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程

    这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...

  3. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  4. 【手撸一个ORM】第六步、对象表达式解析和Select表达式解析

    说明 一个Orm自然不仅仅包含条件表达式,还会有如下的场景: OrderBy(s => s.StudentName) Select<StudentDto>(s => new S ...

  5. 【手撸一个ORM】第七步、SqlDataReader转实体

    说明 使用Expression(表达式目录树)转Entity的文章在园子里有很多,思路也大致也一样,我在前面有篇文章对解决思路有些说明,有兴趣的小伙伴可以看下 (传送门),刚接触表达式目录树时写的,不 ...

  6. 【手撸一个ORM】第十步、数据操作工具类 MyDb

    说明 其实就是数据库操作的一些封装,很久不用SqlCommand操作数据库了,看了点园子里的文章就直接上手写了,功能上没问题,但写法上是否完美高效无法保证,建议有需要的朋友自己重写,当然如果能把最佳实 ...

  7. 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装

    既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...

  8. 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句

    说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...

  9. 【手撸一个ORM】第四步、Expression(表达式目录树)扩展

    到这里,Orm的基架已经搭起来了,接下来就是激动人心的部分,表达式目录树转Sql语句,SqlDataReader转数据实体等等,但是在这之前,我们需要扩展下表达式目录树的方法,以方便后面的相关操作. ...

随机推荐

  1. checkbox怎么判断是否选中

    下面这种可以使用 if($("#checkbox1").is(':checked')) { alert("1"); } else { alert("0 ...

  2. leetcode 304. Range Sum Query 2D - Immutable(递推)

    Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper lef ...

  3. POJ1195Mobile phones (从二维树状数组到cdq分治)

    Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows ...

  4. 解决 sublime text3 运行python文件无法input的问题

    怎么输入都没有用,原来需要配置可交互环境来运行 首先,Ctrl+Shift+p快捷键,弹出框框输入 install Package,回车后又弹出一个框,输入SublimeREPL(要安装的插件名字), ...

  5. Educational Codeforces Round 17 颓废记

    又被虐了... (记一次惨痛的Codeforces) 好不容易登上去了Codeforces,22:35准时开打 第一题,一看:这不SB题嘛?直接枚举因数上啊.9min才过掉了pretest 第二题.. ...

  6. Poj_1005_I Think I Need A HouseBoat

    一.Description Fred Mapper is considering purchasing some land in Louisiana to build his house on. In ...

  7. maven学习九 关于maven一些參數

    一 maven profile:      不同的运行环境,比如开发环境.测试环境.生产环境,而我们的软件在不同的环境中,有的配置可能会不一样,比如数据源配置.日志文件配置.以及一些软件运行过程中的基 ...

  8. CS231n 2016 通关 第六章 Training NN Part2

    本章节讲解 参数更新 dropout ================================================================================= ...

  9. linux正则表达式基础

    linux中awk,sed,grep等 命令使用区别正则表达式基础 在最简单的情况下,一个正则表达式看上去就是一个普通的查找串.例如,正则表达式"testing"中没有包含任何元字 ...

  10. plsql&nbsp;分页

     select * from (select rownum rn,ps.* from (select * from user_t) ps ) where rn>=1 and rn<=10 ...