|
@@ -87,17 +87,24 @@ namespace Vitorm.Sql
|
|
|
protected virtual string dbGroupName => "SqlDbSet_" + dbConnectionProvider.dbHashCode;
|
|
|
public virtual string databaseName => dbConnectionProvider.databaseName;
|
|
|
|
|
|
- /// <summary>
|
|
|
- ///
|
|
|
- /// </summary>
|
|
|
- /// <param name="sqlTranslateService"></param>
|
|
|
- /// <param name="dbConnectionProvider"></param>
|
|
|
- /// <param name="sqlExecutor"></param>
|
|
|
- public virtual void Init(ISqlTranslateService sqlTranslateService, DbConnectionProvider dbConnectionProvider, SqlExecutor sqlExecutor = null)
|
|
|
+
|
|
|
+ public virtual void Init(ISqlTranslateService sqlTranslateService, DbConnectionProvider dbConnectionProvider, SqlExecutor sqlExecutor = null, Dictionary<string, object> extraConfig = null)
|
|
|
{
|
|
|
this.sqlTranslateService = sqlTranslateService;
|
|
|
this.dbConnectionProvider = dbConnectionProvider;
|
|
|
this.sqlExecutor = sqlExecutor ?? SqlExecutor.Instance;
|
|
|
+
|
|
|
+ extraConfig?.ForEach(kv =>
|
|
|
+ {
|
|
|
+ switch (kv.Key)
|
|
|
+ {
|
|
|
+ case nameof(query_ToListAndTotalCount_InvokeInOneExecute):
|
|
|
+ {
|
|
|
+ if (kv.Value is bool invokeInOneExecute) query_ToListAndTotalCount_InvokeInOneExecute = invokeInOneExecute;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
|
|
@@ -276,21 +283,26 @@ namespace Vitorm.Sql
|
|
|
|
|
|
}
|
|
|
|
|
|
+ public override IQueryable<Entity> Query<Entity>()
|
|
|
+ {
|
|
|
+ return QueryableBuilder.Build<Entity>(QueryExecutor, dbGroupName);
|
|
|
+ }
|
|
|
+
|
|
|
protected bool QueryIsFromSameDb(object query, Type elementType)
|
|
|
{
|
|
|
return dbGroupName == QueryableBuilder.GetQueryConfig(query as IQueryable) as string;
|
|
|
}
|
|
|
public Action<SqlDbContext, Expression, Type, object> AfterQuery;
|
|
|
- protected object QueryExecutor(Expression expression, Type type)
|
|
|
+ protected object QueryExecutor(Expression expression, Type expressionResultType)
|
|
|
{
|
|
|
object result = null;
|
|
|
try
|
|
|
{
|
|
|
- return result = ExecuteQuery(expression, type);
|
|
|
+ return result = ExecuteQuery(expression, expressionResultType);
|
|
|
}
|
|
|
finally
|
|
|
{
|
|
|
- AfterQuery?.Invoke(this, expression, type, result);
|
|
|
+ AfterQuery?.Invoke(this, expression, expressionResultType, result);
|
|
|
}
|
|
|
}
|
|
|
public virtual SqlDbContext AutoDisposeAfterQuery()
|
|
@@ -299,7 +311,7 @@ namespace Vitorm.Sql
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- protected virtual object ExecuteQuery(Expression expression, Type type)
|
|
|
+ protected virtual object ExecuteQuery(Expression expression, Type expressionResultType)
|
|
|
{
|
|
|
// #1 convert to ExpressionNode
|
|
|
ExpressionNode node = convertService.ConvertToLambdaData(expression, autoReduce: true, isArgument: QueryIsFromSameDb);
|
|
@@ -341,46 +353,20 @@ namespace Vitorm.Sql
|
|
|
(string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
|
|
|
return sql;
|
|
|
}
|
|
|
- case "Count":
|
|
|
- {
|
|
|
- // Count
|
|
|
-
|
|
|
- // deal with skip and take , no need to pass to PrepareCountQuery
|
|
|
- combinedStream.orders = null;
|
|
|
- (int skip, int? take) range = (combinedStream.skip ?? 0, combinedStream.take);
|
|
|
- combinedStream.take = null;
|
|
|
- combinedStream.skip = null;
|
|
|
-
|
|
|
- // get arg
|
|
|
- var arg = new QueryTranslateArgument(this, null);
|
|
|
-
|
|
|
- (string sql, Dictionary<string, object> sqlParam) = sqlTranslateService.PrepareCountQuery(arg, combinedStream);
|
|
|
-
|
|
|
- var count = ExecuteScalar(sql: sql, param: sqlParam, useReadOnly: true);
|
|
|
- var rowCount = Convert.ToInt32(count);
|
|
|
- if (rowCount > 0)
|
|
|
- {
|
|
|
- if (range.skip > 0) rowCount = Math.Max(rowCount - range.skip, 0);
|
|
|
-
|
|
|
- if (combinedStream.take.HasValue)
|
|
|
- rowCount = Math.Min(rowCount, range.take.Value);
|
|
|
- }
|
|
|
- return rowCount;
|
|
|
- }
|
|
|
case nameof(Orm_Extensions.ExecuteDelete):
|
|
|
{
|
|
|
// ExecuteDelete
|
|
|
|
|
|
// get arg
|
|
|
- var resultEntityType = (combinedStream.source as SourceStream)?.GetEntityType();
|
|
|
- var arg = new QueryTranslateArgument(this, resultEntityType);
|
|
|
+ var entityType = (combinedStream.source as SourceStream)?.GetEntityType();
|
|
|
+ var arg = new QueryTranslateArgument(this, entityType);
|
|
|
|
|
|
(string sql, Dictionary<string, object> sqlParam) = sqlTranslateService.PrepareExecuteDelete(arg, combinedStream);
|
|
|
|
|
|
var count = Execute(sql: sql, param: sqlParam);
|
|
|
return count;
|
|
|
}
|
|
|
- case "FirstOrDefault" or "First" or "LastOrDefault" or "Last":
|
|
|
+ case nameof(Queryable.FirstOrDefault) or nameof(Queryable.First) or nameof(Queryable.LastOrDefault) or nameof(Queryable.Last):
|
|
|
{
|
|
|
// get arg
|
|
|
var resultEntityType = expression.Type;
|
|
@@ -391,28 +377,119 @@ namespace Vitorm.Sql
|
|
|
using var reader = ExecuteReader(sql: sql, param: sqlParam, useReadOnly: true);
|
|
|
return dataReader.ReadData(reader);
|
|
|
}
|
|
|
- case "ToList":
|
|
|
+ case nameof(Queryable.Count) or nameof(Queryable_Extensions.TotalCount):
|
|
|
+ {
|
|
|
+ return ExecuteQuery_Count(combinedStream);
|
|
|
+ }
|
|
|
+ case nameof(Queryable_Extensions.ToListAndTotalCount):
|
|
|
+ {
|
|
|
+ var resultEntityType = expression.Type.GetGenericArguments()?.FirstOrDefault()?.GetGenericArguments()?.FirstOrDefault();
|
|
|
+ return ExecuteQuery_ToListAndTotalCount(combinedStream, resultEntityType);
|
|
|
+ }
|
|
|
+ case nameof(Enumerable.ToList):
|
|
|
case "":
|
|
|
case null:
|
|
|
{
|
|
|
// ToList
|
|
|
-
|
|
|
- // get arg
|
|
|
var resultEntityType = expression.Type.GetGenericArguments()?.FirstOrDefault();
|
|
|
- var arg = new QueryTranslateArgument(this, resultEntityType);
|
|
|
-
|
|
|
- (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
|
|
|
-
|
|
|
- using var reader = ExecuteReader(sql: sql, param: sqlParam, useReadOnly: true);
|
|
|
- return dataReader.ReadData(reader);
|
|
|
+ return ExecuteQuery_ToList(combinedStream, resultEntityType);
|
|
|
}
|
|
|
}
|
|
|
throw new NotSupportedException("not supported query type: " + combinedStream.method);
|
|
|
}
|
|
|
|
|
|
- public override IQueryable<Entity> Query<Entity>()
|
|
|
+ protected bool query_ToListAndTotalCount_InvokeInOneExecute = true;
|
|
|
+ protected virtual object ExecuteQuery_ToListAndTotalCount(CombinedStream combinedStream, Type resultEntityType)
|
|
|
{
|
|
|
- return QueryableBuilder.Build<Entity>(QueryExecutor, dbGroupName);
|
|
|
+ object list; int totalCount;
|
|
|
+
|
|
|
+ if (query_ToListAndTotalCount_InvokeInOneExecute)
|
|
|
+ {
|
|
|
+ // get arg
|
|
|
+ var arg = new QueryTranslateArgument(this, resultEntityType);
|
|
|
+
|
|
|
+ string sqlToList, sqlCount;
|
|
|
+ IDbDataReader dataReader; Dictionary<string, object> sqlParam;
|
|
|
+ // #1 ToList
|
|
|
+ {
|
|
|
+ combinedStream.method = nameof(Enumerable.ToList);
|
|
|
+ (sqlToList, sqlParam, dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
|
|
|
+ }
|
|
|
+
|
|
|
+ // #2 TotalCount
|
|
|
+ {
|
|
|
+ combinedStream.method = nameof(Enumerable.Count);
|
|
|
+ (combinedStream.orders, combinedStream.skip, combinedStream.take) = (null, null, null);
|
|
|
+
|
|
|
+ (sqlCount, sqlParam) = sqlTranslateService.PrepareCountQuery(arg, combinedStream);
|
|
|
+ }
|
|
|
+
|
|
|
+ // #3 read data
|
|
|
+ {
|
|
|
+ var sql = sqlCount + " ;\r\n" + sqlToList;
|
|
|
+ using var reader = ExecuteReader(sql: sql, param: sqlParam, useReadOnly: true);
|
|
|
+ reader.Read();
|
|
|
+ totalCount = Convert.ToInt32(reader[0]);
|
|
|
+ reader.NextResult();
|
|
|
+ list = dataReader.ReadData(reader);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ combinedStream.method = nameof(Enumerable.ToList);
|
|
|
+ list = ExecuteQuery_ToList(combinedStream, resultEntityType);
|
|
|
+
|
|
|
+ combinedStream.method = nameof(Queryable_Extensions.TotalCount);
|
|
|
+ totalCount = ExecuteQuery_Count(combinedStream);
|
|
|
+ }
|
|
|
+
|
|
|
+ //combinedStream.method = nameof(Queryable_Extensions.ToListAndTotalCount);
|
|
|
+
|
|
|
+ return new Func<object, int, (object, int)>(ValueTuple.Create<object, int>)
|
|
|
+ .Method.GetGenericMethodDefinition()
|
|
|
+ .MakeGenericMethod(list.GetType(), typeof(int))
|
|
|
+ .Invoke(null, new[] { list, totalCount });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Queryable.Count or Queryable_Extensions.TotalCount
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="combinedStream"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ protected virtual int ExecuteQuery_Count(CombinedStream combinedStream)
|
|
|
+ {
|
|
|
+ // deal with skip and take , no need to pass to PrepareCountQuery
|
|
|
+ var queryArg = (combinedStream.orders,combinedStream.skip , combinedStream.take);
|
|
|
+ (combinedStream.orders, combinedStream.skip, combinedStream.take) = (null,null,null);
|
|
|
+
|
|
|
+ // get arg
|
|
|
+ var arg = new QueryTranslateArgument(this, null);
|
|
|
+
|
|
|
+ (string sql, Dictionary<string, object> sqlParam) = sqlTranslateService.PrepareCountQuery(arg, combinedStream);
|
|
|
+
|
|
|
+ var countValue = ExecuteScalar(sql: sql, param: sqlParam, useReadOnly: true);
|
|
|
+ var count = Convert.ToInt32(countValue);
|
|
|
+ if (count > 0 && combinedStream.method == nameof(Queryable.Count))
|
|
|
+ {
|
|
|
+ if (queryArg.skip > 0) count = Math.Max(count - queryArg.skip.Value, 0);
|
|
|
+
|
|
|
+ if (queryArg.take.HasValue)
|
|
|
+ count = Math.Min(count, queryArg.take.Value);
|
|
|
+ }
|
|
|
+
|
|
|
+ (combinedStream.orders, combinedStream.skip, combinedStream.take) = queryArg;
|
|
|
+ return count;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected virtual object ExecuteQuery_ToList(CombinedStream combinedStream, Type resultEntityType)
|
|
|
+ {
|
|
|
+ var arg = new QueryTranslateArgument(this, resultEntityType);
|
|
|
+
|
|
|
+ (string sql, Dictionary<string, object> sqlParam, IDbDataReader dataReader) = sqlTranslateService.PrepareQuery(arg, combinedStream);
|
|
|
+
|
|
|
+ using var reader = ExecuteReader(sql: sql, param: sqlParam, useReadOnly: true);
|
|
|
+ return dataReader.ReadData(reader);
|
|
|
}
|
|
|
|
|
|
#endregion
|