一、实体查询

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. eclipse的maven工程Dynamic Web Module 2.3 修改为3.0 解决办法

    1. 创建Maven Web工程 2. 项目只有src/main/resources >Java Build Path导入Tomcat运行环境 3. 删除以图片红框中的文件 4. Propert ...

  2. 勤于思考:Objective-C特性的扩展

    赋值 assign:直接赋值.默认 @interface Car : NSObject { NSString *_name; } @property (assign,nonatomic) NSStri ...

  3. linux命令学习笔记(52):ifconfig命令

    许多windows非常熟悉ipconfig命令行工具,它被用来获取网络接口配置信息并对此进行修改.Linux系统拥有 一个类似的工具,也就是ifconfig (interfaces config).通 ...

  4. homebrew cask安装launch rocket【转】

    简介 brew cask是一个用命令行管理Mac下应用的工具,它是基于homebrew的一个增强工具. homebrew可以管理Mac下的命令行工具,例如imagemagick, nodejs,如下所 ...

  5. 如何运行jnlp文件

    运行DOS命令: C:\Users\thinkpad>javaws D:\***.jnlp 如提示“应用程序已被java安全阻止”则进入控制面板->Java  打开java控制面板,在安全 ...

  6. ACM学习历程—HDU4717 The Moving Points(模拟退火 || 三分法)

    Description There are N points in total. Every point moves in certain direction and certain speed. W ...

  7. 百度地图API的第一次接触——地图事件

    0.初始化地图 var map = new BMap.Map("container"); var point = new BMap.Point(116.404, 39.915); ...

  8. 2012年浙大:Hello World for U

    题目描述: Given any string of N (>=5) characters, you are asked to form the characters into the shape ...

  9. tp框架下,数据库和编辑器都是utf-8, 输出中文却还是乱码

    输出: array(2) { [0]=> array(4) { ["id"]=> string(1) "1" ["user"]= ...

  10. SSO跨域 CodeProject

    http://www.codeproject.com/Articles/114484/Single-Sign-On-SSO-for-cross-domain-ASP-NET-appl 翻译: http ...