【手擼一個ORM】第八步、實體查詢和按需查詢
阿新 • • 發佈:2019-04-07
index desc cond ldb 主表 open 導航 sql handler
一、實體查詢
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 = newList<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 = 0; 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, 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 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 == 0) { var masterFields = string.Join( ",", _masterEntity .Properties .Where(p => p.IsMap) .Select(p => $"[{_masterEntity.TableName}].[{p.FieldName}] AS [{p.Name}]") ); if (_includeProperties.Count > 0) { 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 == 0) { 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(0, 1).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 == 1) { var propName = stack.Pop(); var prop = _masterEntity.Properties.Single(p => p.Name == propName); return $"[{_masterEntity.TableName}].[{prop.FieldName}]"; } if (stack.Count == 2) { 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>()。這裏的T不能支持object和dynamic等,嘗試了一些方法,總是不成功,再留個坑。
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 = 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<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, 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<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】第八步、實體查詢和按需查詢