From ba715af0ce9a2f1eb51e9e610936bd6dfb6ebd2c Mon Sep 17 00:00:00 2001 From: wjf <1448376744@qq.com> Date: Sat, 9 May 2020 17:26:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=BF=BD=E7=95=A5=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AutoEntitys.tt | 116 ++++ .../Attributes/ColumnAttribute.cs | 21 + .../Attributes/ComplexTypeAttribute.cs | 14 + .../Attributes/ConcurrencyCheckAttribute.cs | 14 + .../Attributes/DefaultAttribute.cs | 13 + .../Attributes/FunctionAttribute.cs | 13 + .../Attributes/IdentityAttribute.cs | 13 + .../Attributes/NotMappedAttribute.cs | 13 + .../Attributes/PrimaryKeyAttribute.cs | 13 + .../Attributes/TableAttribute.cs | 17 + src/Dapper.Common/Dapper.Common.csproj | 32 + src/Dapper.Common/DbContexts/DbContext.cs | 493 +++++++++++++++ .../DbContexts/DbContextBuilder.cs | 12 + .../DbContexts/DbContextState.cs | 14 + src/Dapper.Common/DbContexts/DbContextType.cs | 15 + src/Dapper.Common/DbContexts/EmitConvert.cs | 292 +++++++++ src/Dapper.Common/DbContexts/EntityMapper.cs | 585 ++++++++++++++++++ src/Dapper.Common/DbContexts/MultiResult.cs | 147 +++++ .../DbUpdateConcurrencyException.cs | 18 + .../Expressions/BooleanExpressionResovle.cs | 228 +++++++ .../Expressions/ExpressionActivator.cs | 237 +++++++ .../Expressions/ExpressionResovle.cs | 82 +++ .../Expressions/FunctionExpressionResovle.cs | 77 +++ .../Expressions/GroupExpressionResovle.cs | 40 ++ .../Expressions/OrderExpressionResovle.cs | 45 ++ .../Expressions/SelectExpressionResovle.cs | 83 +++ .../Queryables/DbMetaInfoCache.cs | 130 ++++ src/Dapper.Common/Queryables/DbQuery.cs | 433 +++++++++++++ src/Dapper.Common/Queryables/DbQueryAsync.cs | 166 +++++ src/Dapper.Common/Queryables/DbQuerySync.cs | 275 ++++++++ src/Dapper.Common/Queryables/IDbQuery.cs | 354 +++++++++++ src/Dapper.Common/Queryables/Operator.cs | 201 ++++++ .../XmlResovles/Nodes/CommandNode.cs | 100 +++ src/Dapper.Common/XmlResovles/Nodes/INode.cs | 11 + src/Dapper.Common/XmlResovles/Nodes/IfNode.cs | 13 + .../XmlResovles/Nodes/TextNode.cs | 13 + .../XmlResovles/Nodes/WhereNode.cs | 11 + src/Dapper.Common/XmlResovles/XmlQuery.cs | 168 +++++ src/Dapper.Common/XmlResovles/XmlResovle.cs | 240 +++++++ src/Dapper.Linq.NUnitTest/AutoEntitys.cs | 42 ++ src/Dapper.Linq.NUnitTest/AutoEntitys.tt | 116 ++++ .../Dapper.Linq.NUnitTest.csproj | 39 ++ src/Dapper.Linq.NUnitTest/UnitTest1.cs | 55 ++ src/Dapper.Linq.NUnitTest/student.xml | 35 ++ src/Dapper.Linq.sln | 37 ++ src/Dapper.Linq/Attributes/ColumnAttribute.cs | 21 + .../Attributes/ComplexTypeAttribute.cs | 14 + .../Attributes/ConcurrencyCheckAttribute.cs | 14 + .../Attributes/DefaultAttribute.cs | 13 + .../Attributes/FunctionAttribute.cs | 13 + .../Attributes/IdentityAttribute.cs | 13 + .../Attributes/NotMappedAttribute.cs | 13 + .../Attributes/PrimaryKeyAttribute.cs | 13 + src/Dapper.Linq/Attributes/TableAttribute.cs | 17 + src/Dapper.Linq/Dapper.Linq.csproj | 33 + src/Dapper.Linq/DbContexts/DbContext.cs | 493 +++++++++++++++ .../DbContexts/DbContextBuilder.cs | 12 + src/Dapper.Linq/DbContexts/DbContextState.cs | 14 + src/Dapper.Linq/DbContexts/DbContextType.cs | 15 + src/Dapper.Linq/DbContexts/EmitConvert.cs | 292 +++++++++ src/Dapper.Linq/DbContexts/EntityMapper.cs | 585 ++++++++++++++++++ src/Dapper.Linq/DbContexts/MultiResult.cs | 147 +++++ .../DbUpdateConcurrencyException.cs | 18 + .../Expressions/BooleanExpressionResovle.cs | 228 +++++++ .../Expressions/ExpressionActivator.cs | 237 +++++++ .../Expressions/ExpressionResovle.cs | 82 +++ .../Expressions/FunctionExpressionResovle.cs | 77 +++ .../Expressions/GroupExpressionResovle.cs | 40 ++ .../Expressions/OrderExpressionResovle.cs | 45 ++ .../Expressions/SelectExpressionResovle.cs | 83 +++ src/Dapper.Linq/Queryables/DbMetaInfoCache.cs | 130 ++++ src/Dapper.Linq/Queryables/DbQuery.cs | 433 +++++++++++++ src/Dapper.Linq/Queryables/DbQueryAsync.cs | 166 +++++ src/Dapper.Linq/Queryables/DbQuerySync.cs | 275 ++++++++ src/Dapper.Linq/Queryables/IDbQuery.cs | 354 +++++++++++ src/Dapper.Linq/Queryables/Operator.cs | 201 ++++++ .../XmlResovles/Nodes/CommandNode.cs | 100 +++ src/Dapper.Linq/XmlResovles/Nodes/INode.cs | 11 + src/Dapper.Linq/XmlResovles/Nodes/IfNode.cs | 13 + src/Dapper.Linq/XmlResovles/Nodes/TextNode.cs | 13 + .../XmlResovles/Nodes/WhereNode.cs | 11 + src/Dapper.Linq/XmlResovles/XmlQuery.cs | 168 +++++ src/Dapper.Linq/XmlResovles/XmlResovle.cs | 240 +++++++ src/References/Dapper.dll | Bin 0 -> 120320 bytes src/References/MySql.Data.dll | Bin 0 -> 431616 bytes 85 files changed, 9733 insertions(+) create mode 100644 src/AutoEntitys.tt create mode 100644 src/Dapper.Common/Attributes/ColumnAttribute.cs create mode 100644 src/Dapper.Common/Attributes/ComplexTypeAttribute.cs create mode 100644 src/Dapper.Common/Attributes/ConcurrencyCheckAttribute.cs create mode 100644 src/Dapper.Common/Attributes/DefaultAttribute.cs create mode 100644 src/Dapper.Common/Attributes/FunctionAttribute.cs create mode 100644 src/Dapper.Common/Attributes/IdentityAttribute.cs create mode 100644 src/Dapper.Common/Attributes/NotMappedAttribute.cs create mode 100644 src/Dapper.Common/Attributes/PrimaryKeyAttribute.cs create mode 100644 src/Dapper.Common/Attributes/TableAttribute.cs create mode 100644 src/Dapper.Common/Dapper.Common.csproj create mode 100644 src/Dapper.Common/DbContexts/DbContext.cs create mode 100644 src/Dapper.Common/DbContexts/DbContextBuilder.cs create mode 100644 src/Dapper.Common/DbContexts/DbContextState.cs create mode 100644 src/Dapper.Common/DbContexts/DbContextType.cs create mode 100644 src/Dapper.Common/DbContexts/EmitConvert.cs create mode 100644 src/Dapper.Common/DbContexts/EntityMapper.cs create mode 100644 src/Dapper.Common/DbContexts/MultiResult.cs create mode 100644 src/Dapper.Common/Exceptions/DbUpdateConcurrencyException.cs create mode 100644 src/Dapper.Common/Expressions/BooleanExpressionResovle.cs create mode 100644 src/Dapper.Common/Expressions/ExpressionActivator.cs create mode 100644 src/Dapper.Common/Expressions/ExpressionResovle.cs create mode 100644 src/Dapper.Common/Expressions/FunctionExpressionResovle.cs create mode 100644 src/Dapper.Common/Expressions/GroupExpressionResovle.cs create mode 100644 src/Dapper.Common/Expressions/OrderExpressionResovle.cs create mode 100644 src/Dapper.Common/Expressions/SelectExpressionResovle.cs create mode 100644 src/Dapper.Common/Queryables/DbMetaInfoCache.cs create mode 100644 src/Dapper.Common/Queryables/DbQuery.cs create mode 100644 src/Dapper.Common/Queryables/DbQueryAsync.cs create mode 100644 src/Dapper.Common/Queryables/DbQuerySync.cs create mode 100644 src/Dapper.Common/Queryables/IDbQuery.cs create mode 100644 src/Dapper.Common/Queryables/Operator.cs create mode 100644 src/Dapper.Common/XmlResovles/Nodes/CommandNode.cs create mode 100644 src/Dapper.Common/XmlResovles/Nodes/INode.cs create mode 100644 src/Dapper.Common/XmlResovles/Nodes/IfNode.cs create mode 100644 src/Dapper.Common/XmlResovles/Nodes/TextNode.cs create mode 100644 src/Dapper.Common/XmlResovles/Nodes/WhereNode.cs create mode 100644 src/Dapper.Common/XmlResovles/XmlQuery.cs create mode 100644 src/Dapper.Common/XmlResovles/XmlResovle.cs create mode 100644 src/Dapper.Linq.NUnitTest/AutoEntitys.cs create mode 100644 src/Dapper.Linq.NUnitTest/AutoEntitys.tt create mode 100644 src/Dapper.Linq.NUnitTest/Dapper.Linq.NUnitTest.csproj create mode 100644 src/Dapper.Linq.NUnitTest/UnitTest1.cs create mode 100644 src/Dapper.Linq.NUnitTest/student.xml create mode 100644 src/Dapper.Linq.sln create mode 100644 src/Dapper.Linq/Attributes/ColumnAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/ComplexTypeAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/ConcurrencyCheckAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/DefaultAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/FunctionAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/IdentityAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/NotMappedAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/PrimaryKeyAttribute.cs create mode 100644 src/Dapper.Linq/Attributes/TableAttribute.cs create mode 100644 src/Dapper.Linq/Dapper.Linq.csproj create mode 100644 src/Dapper.Linq/DbContexts/DbContext.cs create mode 100644 src/Dapper.Linq/DbContexts/DbContextBuilder.cs create mode 100644 src/Dapper.Linq/DbContexts/DbContextState.cs create mode 100644 src/Dapper.Linq/DbContexts/DbContextType.cs create mode 100644 src/Dapper.Linq/DbContexts/EmitConvert.cs create mode 100644 src/Dapper.Linq/DbContexts/EntityMapper.cs create mode 100644 src/Dapper.Linq/DbContexts/MultiResult.cs create mode 100644 src/Dapper.Linq/Exceptions/DbUpdateConcurrencyException.cs create mode 100644 src/Dapper.Linq/Expressions/BooleanExpressionResovle.cs create mode 100644 src/Dapper.Linq/Expressions/ExpressionActivator.cs create mode 100644 src/Dapper.Linq/Expressions/ExpressionResovle.cs create mode 100644 src/Dapper.Linq/Expressions/FunctionExpressionResovle.cs create mode 100644 src/Dapper.Linq/Expressions/GroupExpressionResovle.cs create mode 100644 src/Dapper.Linq/Expressions/OrderExpressionResovle.cs create mode 100644 src/Dapper.Linq/Expressions/SelectExpressionResovle.cs create mode 100644 src/Dapper.Linq/Queryables/DbMetaInfoCache.cs create mode 100644 src/Dapper.Linq/Queryables/DbQuery.cs create mode 100644 src/Dapper.Linq/Queryables/DbQueryAsync.cs create mode 100644 src/Dapper.Linq/Queryables/DbQuerySync.cs create mode 100644 src/Dapper.Linq/Queryables/IDbQuery.cs create mode 100644 src/Dapper.Linq/Queryables/Operator.cs create mode 100644 src/Dapper.Linq/XmlResovles/Nodes/CommandNode.cs create mode 100644 src/Dapper.Linq/XmlResovles/Nodes/INode.cs create mode 100644 src/Dapper.Linq/XmlResovles/Nodes/IfNode.cs create mode 100644 src/Dapper.Linq/XmlResovles/Nodes/TextNode.cs create mode 100644 src/Dapper.Linq/XmlResovles/Nodes/WhereNode.cs create mode 100644 src/Dapper.Linq/XmlResovles/XmlQuery.cs create mode 100644 src/Dapper.Linq/XmlResovles/XmlResovle.cs create mode 100644 src/References/Dapper.dll create mode 100644 src/References/MySql.Data.dll diff --git a/src/AutoEntitys.tt b/src/AutoEntitys.tt new file mode 100644 index 0000000..3459411 --- /dev/null +++ b/src/AutoEntitys.tt @@ -0,0 +1,116 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="System.Data" #> +<#@ assembly name="$(SolutionDir)\YC51.References\Dapper.dll" #> +<#@ assembly name="$(SolutionDir)\YC51.References\MySql.Data.dll" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="MySql.Data.MySqlClient" #> +<#@ import namespace="Dapper" #> +<#@ output extension=".cs" #> +<# + //数据库连接字符串 + var connectionString = "server=127.0.0.1;user id=root;password=1024;database=test;"; +#> +<# + var connection = new MySqlConnection(connectionString); + var tablesql = string.Format("SELECT TABLE_NAME as TableName,TABLE_TYPE as TableType,TABLE_COMMENT as TableComment from INFORmation_schema.TABLES WHERE TABLE_SCHEMA='{0}'", connection.Database); + var columnsql = string.Format("SELECT TABLE_NAME as TableName,COLUMN_NAME as ColumnName,COLUMN_COMMENT as ColumnComment,COLUMN_DEFAULT as ColumnDefault,IS_NULLABLE as IsNullable,DATA_TYPE as DataType,COLUMN_TYPE as ColumnType,COLUMN_KEY as ColumnKey,EXTRA as Extra FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='{0}' ORDER BY ORDINAL_POSITION", connection.Database); + var tables = connection.Query(tablesql).ToList(); + var columns = connection.Query(columnsql).ToList(); + connection.Close(); +#> +using System; +using YC51.SqlBatis.Attributes; + +namespace YC51.MiniProgram.Service +{ +<#foreach(var table in tables){#> + + /// + /// <#=table.TableComment#> + /// 更新时间:<#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> + /// + [Table("<#=table.TableName#>")] + public partial class <#=Utils.Pascal(table.TableName)#> + { + <#foreach(var column in columns.FindAll(f => f.TableName == table.TableName)){#> + /// + /// <#=column.ColumnComment#> + /// + [Column("<#= column.ColumnName #>")] + <#=column.ColumnKey == "PRI"?"[PrimaryKey]\r\n":""#><#=column.Extra == "auto_increment"?"[Identity]\r\n":"" #><#=column.ColumnDefault != null?"[Default]\r\n":"" #> + public <#= Utils.GetCSharpType(column.DataType) #> <#= Utils.Pascal(column.ColumnName) #> { get; set; } + <#}#>} +<#}#> +} + + + +<#+ + public class Table + { + public string TableName { get; set; } + public string TableType { get; set; } + public string TableComment { get; set; } + } + public class Column + { + public string TableName { get; set; } + public string ColumnName { get; set; } + public string ColumnComment { get; set; } + public string ColumnDefault { get; set; } + public string IsNullable { get; set; } + public string ColumnType { get; set; } + public string DataType { get; set; } + public string ColumnKey { get; set; } + public string Extra { get; set; } + } + public static class Utils + { + //字段类型映射 + public static string GetCSharpType(string columnType) + { + var type = "object"; + switch (columnType) + { + case "varchar": type = "string"; break; + case "text": type = "string"; break; + case "char": type = "string"; break; + case "bit": type = "bool?"; break; + case "tinyint": type = "int?"; break; + case "smallint": type = "int?"; break; + case "int": type = "int?"; break; + case "integer": type = "int?"; break; + case "bigint": type = "int?"; break; + case "mediumint": type = "int?"; break; + case "real": type = "float?"; break; + case "float": type = "float?"; break; + case "double": type = "double?"; break; + case "decimal": type = "decimal?"; break; + case "date": type = "DateTime?"; break; + case "datetime": type = "DateTime?"; break; + case "json": type = "System.Text.Json.JsonElement?"; break; + } + return type; + } + //Pacsl命名转换 + public static string Pascal(string name) + { + var list = new List(); + foreach (var item in name.Split('_')) + { + list.Add(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(item.ToLower())); + } + return string.Join("",list); + } + //Camel命名转换 + public static string Camel(string name) + { + name = Pascal(name); + return char.ToLower(name[0]) + name.Substring(1); + } + } +#> \ No newline at end of file diff --git a/src/Dapper.Common/Attributes/ColumnAttribute.cs b/src/Dapper.Common/Attributes/ColumnAttribute.cs new file mode 100644 index 0000000..4c631b2 --- /dev/null +++ b/src/Dapper.Common/Attributes/ColumnAttribute.cs @@ -0,0 +1,21 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 字段映射 + /// + [AttributeUsage(AttributeTargets.Property)] + public class ColumnAttribute : Attribute + { + internal string Name { get; set; } + /// + /// 属性字段映射 + /// + /// 数据库字段名 + public ColumnAttribute(string name = null) + { + Name = name; + } + } +} diff --git a/src/Dapper.Common/Attributes/ComplexTypeAttribute.cs b/src/Dapper.Common/Attributes/ComplexTypeAttribute.cs new file mode 100644 index 0000000..557443c --- /dev/null +++ b/src/Dapper.Common/Attributes/ComplexTypeAttribute.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.Attributes +{ + /// + /// 计算列,如果字段一个是计算列则新增和修改的时候不会处理 + /// + public class ComplexTypeAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/ConcurrencyCheckAttribute.cs b/src/Dapper.Common/Attributes/ConcurrencyCheckAttribute.cs new file mode 100644 index 0000000..7eab1a9 --- /dev/null +++ b/src/Dapper.Common/Attributes/ConcurrencyCheckAttribute.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.Attributes +{ + /// + /// 并发检查,如果字段属性是number类型则用时间戳,否则使用GUID + /// + public class ConcurrencyCheckAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/DefaultAttribute.cs b/src/Dapper.Common/Attributes/DefaultAttribute.cs new file mode 100644 index 0000000..d0f7e7c --- /dev/null +++ b/src/Dapper.Common/Attributes/DefaultAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 默认值约束 + /// + [AttributeUsage(AttributeTargets.Property)] + public class DefaultAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/FunctionAttribute.cs b/src/Dapper.Common/Attributes/FunctionAttribute.cs new file mode 100644 index 0000000..ef6ffb5 --- /dev/null +++ b/src/Dapper.Common/Attributes/FunctionAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 数据库函数标识 + /// + [AttributeUsage(AttributeTargets.Class)] + public class FunctionAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/IdentityAttribute.cs b/src/Dapper.Common/Attributes/IdentityAttribute.cs new file mode 100644 index 0000000..2605c87 --- /dev/null +++ b/src/Dapper.Common/Attributes/IdentityAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 自增列标识 + /// + [AttributeUsage(AttributeTargets.Property)] + public class IdentityAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/NotMappedAttribute.cs b/src/Dapper.Common/Attributes/NotMappedAttribute.cs new file mode 100644 index 0000000..c0a85b4 --- /dev/null +++ b/src/Dapper.Common/Attributes/NotMappedAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 忽略映射 + /// + [AttributeUsage(AttributeTargets.Property)] + public class NotMappedAttribute: Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/PrimaryKeyAttribute.cs b/src/Dapper.Common/Attributes/PrimaryKeyAttribute.cs new file mode 100644 index 0000000..f9ed31c --- /dev/null +++ b/src/Dapper.Common/Attributes/PrimaryKeyAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 主键约束 + /// + [AttributeUsage(AttributeTargets.Property)] + public class PrimaryKeyAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Common/Attributes/TableAttribute.cs b/src/Dapper.Common/Attributes/TableAttribute.cs new file mode 100644 index 0000000..8631d9b --- /dev/null +++ b/src/Dapper.Common/Attributes/TableAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 表名映射 + /// + [AttributeUsage(AttributeTargets.Class)] + public class TableAttribute : Attribute + { + internal string Name { get; set; } + public TableAttribute(string name = null) + { + Name = name; + } + } +} diff --git a/src/Dapper.Common/Dapper.Common.csproj b/src/Dapper.Common/Dapper.Common.csproj new file mode 100644 index 0000000..f705e93 --- /dev/null +++ b/src/Dapper.Common/Dapper.Common.csproj @@ -0,0 +1,32 @@ + + + + net45;netstandard2.0 + $(NoWarn);1591 + chaeyeon + Apache-2.0 + A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, etc.. + https://github.com/1448376744/Dapper.Linq + https://github.com/1448376744/Dapper.Linq + GIT + true + true + chaeyeon + chaeyeon + Dapper.Common + 3.0.0 + 3.0.0 + 3.0.0 + + + + + + + + + + + + + diff --git a/src/Dapper.Common/DbContexts/DbContext.cs b/src/Dapper.Common/DbContexts/DbContext.cs new file mode 100644 index 0000000..f043db3 --- /dev/null +++ b/src/Dapper.Common/DbContexts/DbContext.cs @@ -0,0 +1,493 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Dapper +{ + /// + /// 数据库上下文 + /// + public interface IDbContext : IDisposable + { + /// + /// 数据库连接 + /// + IDbConnection Connection { get; } + /// + /// 数据库上下文类型 + /// + DbContextType DbContextType { get; } + /// + /// 获取一个xml执行器 + /// + /// 参数类型 + /// 命令id + /// 参数 + /// + IXmlQuery From(string id, T parameter) where T : class; + /// + /// 获取一个xml执行器 + /// + /// 命令id + /// + IXmlQuery From(string id); + /// + /// 获取一个linq执行器 + /// + /// + /// + IDbQuery From(); + /// + /// 开启事务会话 + /// + void BeginTransaction(); + /// + /// 开启事务会话 + /// + /// 事务隔离级别 + void BeginTransaction(IsolationLevel level); + /// + /// 关闭连接和事务 + /// + void Close(); + /// + /// 提交当前事务会话 + /// + void CommitTransaction(); + /// + /// 执行多结果集查询,返回IMultiResult + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + IMultiResult ExecuteMultiQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回dynamic类型的结果集 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回dynamic类型的结果集 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回受影响的行数 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + int ExecuteNonQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回受影响的行数 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task ExecuteNonQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回指定类型的数据 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + T ExecuteScalar(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回指定类型的数据 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task ExecuteScalarAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 打开数据库连接 + /// + void Open(); + /// + /// 异步打开数据库连接 + /// + /// + Task OpenAsync(); + /// + /// 回滚当前事务会话 + /// + void RollbackTransaction(); + } + + public class DbContext : IDbContext + { + public DbContextState DbContextState = DbContextState.Closed; + + private readonly IXmlResovle _xmlResovle = null; + + private IDbTransaction _transaction = null; + + private readonly IEntityMapper _typeMapper = null; + + public IDbConnection Connection { get; } = null; + + public DbContextType DbContextType { get; } = DbContextType.Mysql; + + protected virtual void OnLogging(string message, IDataParameterCollection parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + + } + + protected virtual DbContextBuilder OnConfiguring(DbContextBuilder builder) + { + return builder; + } + + protected DbContext() + { + var builder = OnConfiguring(new DbContextBuilder()); + Connection = builder.Connection; + _xmlResovle = builder.XmlResovle; + _typeMapper = builder.TypeMapper ?? new EntityMapper(); + DbContextType = builder.DbContextType; + } + + public DbContext(DbContextBuilder builder) + { + Connection = builder.Connection; + _xmlResovle = builder.XmlResovle; + _typeMapper = builder.TypeMapper ?? new EntityMapper(); + DbContextType = builder.DbContextType; + } + public IXmlQuery From(string id, T parameter) where T : class + { + var sql = _xmlResovle.Resolve(id, parameter); + var deserializer = EmitConvert.GetDeserializer(typeof(T)); + var values = deserializer(parameter); + return new XmlQuery(this, sql, values); + } + public IXmlQuery From(string id) + { + var sql = _xmlResovle.Resolve(id); + return new XmlQuery(this, sql); + } + + public IDbQuery From() + { + return new DbQuery(this); + } + + public IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + var list = new List(); + using (var reader = cmd.ExecuteReader()) + { + var handler = EmitConvert.GetSerializer(); + while (reader.Read()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public async Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + using (var reader = await cmd.ExecuteReaderAsync()) + { + var list = new List(); + var handler = EmitConvert.GetSerializer(); + while (reader.Read()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public IMultiResult ExecuteMultiQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + var cmd = Connection.CreateCommand(); + Initialize(cmd, sql, parameter, commandTimeout, commandType); + return new MultiResult(cmd, _typeMapper); + } + + public IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + var list = new List(); + Initialize(cmd, sql, parameter, commandTimeout, commandType); + using (var reader = cmd.ExecuteReader()) + { + var handler = EmitConvert.GetSerializer(_typeMapper, reader); + while (reader.Read()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public async Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + using (var reader = await cmd.ExecuteReaderAsync()) + { + var list = new List(); + var handler = EmitConvert.GetSerializer(_typeMapper, reader); + while (await reader.ReadAsync()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public int ExecuteNonQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + return cmd.ExecuteNonQuery(); + } + } + + public async Task ExecuteNonQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + return await cmd.ExecuteNonQueryAsync(); + } + } + + public T ExecuteScalar(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + var result = cmd.ExecuteScalar(); + if (result is DBNull || result == null) + { + return default; + } + return (T)Convert.ChangeType(result, typeof(T)); + } + } + + public async Task ExecuteScalarAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + var result = await cmd.ExecuteScalarAsync(); + if (result is DBNull || result == null) + { + return default; + } + return (T)Convert.ChangeType(result, typeof(T)); + } + } + + public void BeginTransaction() + { + _transaction = Connection.BeginTransaction(); + OnLogging("Begin transaction"); + } + + public void BeginTransaction(IsolationLevel level) + { + _transaction = Connection.BeginTransaction(level); + OnLogging("Begin transaction isolationLevel = " + level); + } + + public void Close() + { + _transaction?.Dispose(); + Connection?.Dispose(); + DbContextState = DbContextState.Closed; + OnLogging("Colsed connection"); + } + + public void CommitTransaction() + { + _transaction?.Commit(); + DbContextState = DbContextState.Commit; + OnLogging("Commit transaction"); + } + + public void Open() + { + Connection?.Open(); + DbContextState = DbContextState.Open; + OnLogging("Open connection"); + } + + public async Task OpenAsync() + { + await (Connection as DbConnection).OpenAsync(); + DbContextState = DbContextState.Open; + OnLogging("Open connection"); + } + + public void RollbackTransaction() + { + _transaction?.Rollback(); + DbContextState = DbContextState.Rollback; + OnLogging("rollback"); + + } + + private void Initialize(IDbCommand cmd, string sql, object parameter, int? commandTimeout = null, CommandType? commandType = null) + { + var dbParameters = new List(); + cmd.Transaction = _transaction; + cmd.CommandText = sql; + if (commandTimeout.HasValue) + { + cmd.CommandTimeout = commandTimeout.Value; + } + if (commandType.HasValue) + { + cmd.CommandType = commandType.Value; + } + if (parameter is IDbDataParameter) + { + dbParameters.Add(parameter as IDbDataParameter); + } + else if (parameter is IEnumerable parameters) + { + dbParameters.AddRange(parameters); + } + else if (parameter is Dictionary keyValues) + { + foreach (var item in keyValues) + { + var param = CreateParameter(cmd, item.Key, item.Value); + dbParameters.Add(param); + } + } + else if (parameter != null) + { + var handler = EmitConvert.GetDeserializer(parameter.GetType()); + var values = handler(parameter); + foreach (var item in values) + { + var param = CreateParameter(cmd, item.Key, item.Value); + dbParameters.Add(param); + } + } + if (dbParameters.Count > 0) + { + foreach (IDataParameter item in dbParameters) + { + if (item.Value == null) + { + item.Value = DBNull.Value; + } + var pattern = $@"in\s+([\@,\:,\?]?{item.ParameterName})"; + var options = RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline; + if (cmd.CommandText.IndexOf("in", StringComparison.OrdinalIgnoreCase) != -1 && Regex.IsMatch(cmd.CommandText, pattern, options)) + { + var name = Regex.Match(cmd.CommandText, pattern, options).Groups[1].Value; + var list = new List(); + if (item.Value is IEnumerable || item.Value is Array) + { + list = (item.Value as IEnumerable).Cast().Where(a => a != null && a != DBNull.Value).ToList(); + } + else + { + list.Add(item.Value); + } + if (list.Count() > 0) + { + cmd.CommandText = Regex.Replace(cmd.CommandText, name, $"({string.Join(",", list.Select(s => $"{name}{list.IndexOf(s)}"))})"); + foreach (var iitem in list) + { + var key = $"{item.ParameterName}{list.IndexOf(iitem)}"; + var param = CreateParameter(cmd, key, iitem); + cmd.Parameters.Add(param); + } + } + else + { + cmd.CommandText = Regex.Replace(cmd.CommandText, name, $"(SELECT 1 WHERE 1 = 0)"); + } + } + else + { + cmd.Parameters.Add(item); + } + } + } + OnLogging(cmd.CommandText, cmd.Parameters, commandTimeout, commandType); + } + + private IDbDataParameter CreateParameter(IDbCommand command, string name, object value) + { + var parameter = command.CreateParameter(); + parameter.ParameterName = name; + parameter.Value = value; + return parameter; + } + + public void Dispose() + { + _transaction?.Dispose(); + Connection?.Dispose(); + } + } +} diff --git a/src/Dapper.Common/DbContexts/DbContextBuilder.cs b/src/Dapper.Common/DbContexts/DbContextBuilder.cs new file mode 100644 index 0000000..2490c52 --- /dev/null +++ b/src/Dapper.Common/DbContexts/DbContextBuilder.cs @@ -0,0 +1,12 @@ +using System.Data; + +namespace Dapper +{ + public class DbContextBuilder + { + public IXmlResovle XmlResovle { get; set; } + public IDbConnection Connection { get; set; } + public DbContextType DbContextType { get; set; } + public IEntityMapper TypeMapper { get; set; } + } +} diff --git a/src/Dapper.Common/DbContexts/DbContextState.cs b/src/Dapper.Common/DbContexts/DbContextState.cs new file mode 100644 index 0000000..f475352 --- /dev/null +++ b/src/Dapper.Common/DbContexts/DbContextState.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper +{ + public enum DbContextState + { + Closed = 0, + Open = 1, + Commit = 2, + Rollback = 3, + } +} diff --git a/src/Dapper.Common/DbContexts/DbContextType.cs b/src/Dapper.Common/DbContexts/DbContextType.cs new file mode 100644 index 0000000..7498409 --- /dev/null +++ b/src/Dapper.Common/DbContexts/DbContextType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper +{ + public enum DbContextType + { + Mysql, + SqlServer, + Postgresql, + Oracle, + Sqlite, + } +} diff --git a/src/Dapper.Common/DbContexts/EmitConvert.cs b/src/Dapper.Common/DbContexts/EmitConvert.cs new file mode 100644 index 0000000..1f13006 --- /dev/null +++ b/src/Dapper.Common/DbContexts/EmitConvert.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace Dapper +{ + /// + /// DbColumn Information + /// + public class DbDataInfo + { + public string TypeName { get; set; } + public Type DataType { get; set; } + public string DataName { get; set; } + public int Ordinal { get; set; } + public DbDataInfo(int ordinal, string typeName, Type dataType, string dataName) + { + Ordinal = ordinal; + TypeName = typeName; + DataType = dataType; + DataName = dataName; + } + } + /// + /// IL Convert + /// + public class EmitConvert + { + private readonly static ConcurrentDictionary _serializers + = new ConcurrentDictionary(); + private readonly static ConcurrentDictionary>> _deserializers + = new ConcurrentDictionary>>(); + + private struct SerializerKey : IEquatable + { + private string[] Names { get; set; } + private Type Type { get; set; } + public override bool Equals(object obj) + { + return obj is SerializerKey && Equals((SerializerKey)obj); + } + public bool Equals(SerializerKey other) + { + if (Type != other.Type) + { + return false; + } + else if (Names == other.Names) + { + return true; + } + else if (Names.Length != other.Names.Length) + { + return false; + } + else + { + for (int i = 0; i < Names.Length; i++) + { + if (Names[i] != other.Names[i]) + { + return false; + } + } + return true; + } + } + public override int GetHashCode() + { + return Type.GetHashCode(); + } + public SerializerKey(Type type, string[] names) + { + Type = type; + Names = names; + } + } + /// + /// IDataRecord Converted to T + /// + public static Func GetSerializer(IEntityMapper mapper, IDataRecord record) + { + string[] names = new string[record.FieldCount]; + for (int i = 0; i < record.FieldCount; i++) + { + names[i] = record.GetName(i); + } + var key = new SerializerKey(typeof(T), names.Length == 1 ? null : names); + var handler = _serializers.GetOrAdd(key, k => + { + return CreateTypeSerializerHandler(mapper, record); + }); + return handler as Func; + } + /// + /// IDataRecord Converted to dynamic + /// + public static Func GetSerializer() + { + return (reader) => + { + dynamic obj = new System.Dynamic.ExpandoObject(); + var row = (IDictionary)obj; + for (int i = 0; i < reader.FieldCount; i++) + { + var name = reader.GetName(i); + row.Add(name, reader.GetValue(i)); + } + return row; + }; + } + /// + /// IDataRecord Converted to T + /// + public static Func GetSerializer(IDataRecord record) + { + return GetSerializer(new EntityMapper(), record); + } + /// + /// Object To Dictionary<tstring, object> + /// + public static Func> GetDeserializer(Type type) + { + if (type == typeof(Dictionary)) + { + return (object param) => param as Dictionary; + } + var handler = _deserializers.GetOrAdd(type, t => + { + return CreateTypeDeserializerHandler(type) as Func>; + }); + return handler; + } + private static Func> CreateTypeDeserializerHandler(Type type) + { + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var methodName = $"{type.Name}Deserializer{Guid.NewGuid():N}"; + var dynamicMethod = new DynamicMethod(methodName, typeof(Dictionary), new Type[] { typeof(object) }, type, true); + var generator = dynamicMethod.GetILGenerator(); + LocalBuilder entityLocal1 = generator.DeclareLocal(typeof(Dictionary)); + LocalBuilder entityLocal2 = generator.DeclareLocal(type); + generator.Emit(OpCodes.Newobj, typeof(Dictionary).GetConstructor(Type.EmptyTypes)); + generator.Emit(OpCodes.Stloc, entityLocal1); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Castclass, type); + generator.Emit(OpCodes.Stloc, entityLocal2); + foreach (var item in properties) + { + generator.Emit(OpCodes.Ldloc, entityLocal1); + generator.Emit(OpCodes.Ldstr, item.Name); + generator.Emit(OpCodes.Ldloc, entityLocal2); + generator.Emit(OpCodes.Callvirt, item.GetGetMethod()); + if (item.PropertyType.IsValueType) + { + generator.Emit(OpCodes.Box, item.PropertyType); + } + var addMethod = typeof(Dictionary).GetMethod(nameof(Dictionary.Add), new Type[] { typeof(string), typeof(object) }); + generator.Emit(OpCodes.Callvirt, addMethod); + } + generator.Emit(OpCodes.Ldloc, entityLocal1); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func>)) as Func>; + } + + private static Func CreateTypeSerializerHandler(IEntityMapper mapper, IDataRecord record) + { + var type = typeof(T); + var methodName = $"{type.Name}Serializer{Guid.NewGuid():N}"; + var dynamicMethod = new DynamicMethod(methodName, type, new Type[] { typeof(IDataRecord) }, type, true); + var generator = dynamicMethod.GetILGenerator(); + LocalBuilder local = generator.DeclareLocal(type); + var dataInfos = new DbDataInfo[record.FieldCount]; + for (int i = 0; i < record.FieldCount; i++) + { + var dataname = record.GetName(i); + var datatype = record.GetFieldType(i); + var typename = record.GetDataTypeName(i); + dataInfos[i] = new DbDataInfo(i, typename, datatype, dataname); + } + /* + * T t; + * t = ConvertTo***(reader,i); + * return t; + */ + if (dataInfos.Length == 1 && (type.IsValueType || type == typeof(string) || type == typeof(object))) + { + var dataInfo = dataInfos.First(); + var convertMethod = mapper.FindConvertMethod(type, dataInfo.DataType); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, 0); + if (convertMethod.IsVirtual) + generator.Emit(OpCodes.Callvirt, convertMethod); + else + generator.Emit(OpCodes.Call, convertMethod); + if (type == typeof(object) && convertMethod.ReturnType.IsValueType) + { + generator.Emit(OpCodes.Box, convertMethod.ReturnType); + } + generator.Emit(OpCodes.Stloc, local); + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func)) as Func; + } + var constructor = mapper.FindConstructor(type); + /* + * T t; + * int a; + * int b; + * a = ConvertTo**(reader,i); + * b = ConvertTo***(reader,i); + * t = new T(a,b); + * return t; + */ + if (constructor.GetParameters().Length > 0) + { + var parameters = constructor.GetParameters(); + var locals = new LocalBuilder[parameters.Length]; + for (int i = 0; i < locals.Length; i++) + { + locals[i] = generator.DeclareLocal(parameters[i].ParameterType); + } + for (int i = 0; i < locals.Length; i++) + { + var item = mapper.FindConstructorParameter(dataInfos, parameters[i]); + if (item == null) + { + continue; + } + var convertMethod = mapper.FindConvertMethod(parameters[i].ParameterType, item.DataType); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, item.Ordinal); + if (convertMethod.IsVirtual) + generator.Emit(OpCodes.Callvirt, convertMethod); + else + generator.Emit(OpCodes.Call, convertMethod); + generator.Emit(OpCodes.Stloc, locals[i]); + } + for (int i = 0; i < locals.Length; i++) + { + generator.Emit(OpCodes.Ldloc, locals[i]); + } + generator.Emit(OpCodes.Newobj, constructor); + generator.Emit(OpCodes.Stloc, local); + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func)) as Func; + } + /* + * T t; + * t = new T(); + * t.A = ConvertTo**(reader,i); + * t.B = = ConvertTo***(reader,i); + * return t; + */ + else + { + var properties = type.GetProperties(); + generator.Emit(OpCodes.Newobj, constructor); + generator.Emit(OpCodes.Stloc, local); + foreach (var item in dataInfos) + { + var property = mapper.FindMember(properties, item) as PropertyInfo; + if (property == null) + { + continue; + } + var convertMethod = mapper.FindConvertMethod(property.PropertyType, item.DataType); + if (convertMethod == null) + { + continue; + } + int i = record.GetOrdinal(item.DataName); + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + if (convertMethod.IsVirtual) + generator.Emit(OpCodes.Callvirt, convertMethod); + else + generator.Emit(OpCodes.Call, convertMethod); + generator.Emit(OpCodes.Callvirt, property.GetSetMethod()); + } + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func)) as Func; + } + } + } +} diff --git a/src/Dapper.Common/DbContexts/EntityMapper.cs b/src/Dapper.Common/DbContexts/EntityMapper.cs new file mode 100644 index 0000000..2761cc4 --- /dev/null +++ b/src/Dapper.Common/DbContexts/EntityMapper.cs @@ -0,0 +1,585 @@ +using System; +using System.Data; +using System.Linq; +using System.Reflection; + +namespace Dapper +{ + /// + /// TypeMapper Interface + /// + public interface IEntityMapper + { + MemberInfo FindMember(MemberInfo[] properties, DbDataInfo dataInfo); + MethodInfo FindConvertMethod(Type csharpType, Type dbType); + DbDataInfo FindConstructorParameter(DbDataInfo[] dataInfos, ParameterInfo parameterInfo); + ConstructorInfo FindConstructor(Type csharpType); + } + + /// + /// 返回数据记录到Csharp类型的策略 + /// + public class EntityMapper : IEntityMapper + { + public bool MatchNamesWithUnderscores { get; set; } + + /// + /// Find parametric constructors. + /// If there is no default constructor, the constructor with the most parameters is returned. + /// + public ConstructorInfo FindConstructor(Type csharpType) + { + var constructor = csharpType.GetConstructor(Type.EmptyTypes); + if (constructor == null) + { + var constructors = csharpType.GetConstructors(); + constructor = constructors.Where(a => a.GetParameters().Length == constructors.Max(s => s.GetParameters().Length)).FirstOrDefault(); + } + return constructor; + } + + /// + /// Returns field information based on parameter information + /// + public DbDataInfo FindConstructorParameter(DbDataInfo[] dataInfos, ParameterInfo parameterInfo) + { + foreach (var item in dataInfos) + { + if (item.DataName.Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase)) + { + return item; + } + else if (MatchNamesWithUnderscores && item.DataName.Replace("_", "").Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase)) + { + return item; + } + } + return null; + } + + /// + /// Returns attribute information based on field information + /// + public MemberInfo FindMember(MemberInfo[] properties, DbDataInfo dataInfo) + { + foreach (var item in properties) + { + if (item.Name.Equals(dataInfo.DataName, StringComparison.OrdinalIgnoreCase)) + { + return item; + } + else if (MatchNamesWithUnderscores && item.Name.Equals(dataInfo.DataName.Replace("_", ""), StringComparison.OrdinalIgnoreCase)) + { + return item; + } + } + return null; + } + + /// + /// Return type conversion function. + /// + public MethodInfo FindConvertMethod(Type csharpType, Type dbType) + { + if (GetUnderlyingType(dbType) == typeof(bool) || GetUnderlyingType(csharpType) == typeof(bool)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToBooleanMethod : DataConvertMethod.ToBooleanNullableMethod; + } + if (GetUnderlyingType(csharpType).IsEnum) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToEnumMethod.MakeGenericMethod(csharpType) : DataConvertMethod.ToEnumNullableMethod.MakeGenericMethod(GetUnderlyingType(csharpType)); + } + if (GetUnderlyingType(dbType) == typeof(char) || GetUnderlyingType(csharpType) == typeof(char)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToCharMethod : DataConvertMethod.ToCharNullableMethod; + } + if (csharpType == typeof(string)) + { + return DataConvertMethod.ToStringMethod; + } + if (GetUnderlyingType(dbType) == typeof(Guid) || GetUnderlyingType(csharpType) == typeof(Guid)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToGuidMethod : DataConvertMethod.ToGuidNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(DateTime) || GetUnderlyingType(csharpType) == typeof(DateTime)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToDateTimeMethod : DataConvertMethod.ToDateTimeNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(byte) || GetUnderlyingType(dbType) == typeof(sbyte) || GetUnderlyingType(csharpType) == typeof(byte) || GetUnderlyingType(csharpType) == typeof(sbyte)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToByteMethod : DataConvertMethod.ToByteNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(short) || GetUnderlyingType(dbType) == typeof(ushort) || GetUnderlyingType(csharpType) == typeof(short) || GetUnderlyingType(csharpType) == typeof(ushort)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToIn16Method : DataConvertMethod.ToIn16NullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(int) || GetUnderlyingType(dbType) == typeof(uint) || GetUnderlyingType(csharpType) == typeof(int) || GetUnderlyingType(csharpType) == typeof(uint)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToIn32Method : DataConvertMethod.ToIn32NullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(long) || GetUnderlyingType(dbType) == typeof(long) || GetUnderlyingType(csharpType) == typeof(long) || GetUnderlyingType(csharpType) == typeof(ulong)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToIn64Method : DataConvertMethod.ToIn64NullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(float) || GetUnderlyingType(csharpType) == typeof(float)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToFloatMethod : DataConvertMethod.ToFloatNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(double) || GetUnderlyingType(csharpType) == typeof(double)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToDoubleMethod : DataConvertMethod.ToDoubleNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(decimal) || GetUnderlyingType(csharpType) == typeof(decimal)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToDecimalMethod : DataConvertMethod.ToDecimalNullableMethod; + } + return !IsNullableType(csharpType) ? DataConvertMethod.ToObjectMethod.MakeGenericMethod(csharpType) : DataConvertMethod.ToObjectNullableMethod.MakeGenericMethod(Nullable.GetUnderlyingType(GetUnderlyingType(csharpType))); + } + + private Type GetUnderlyingType(Type type) + { + var underlyingType = Nullable.GetUnderlyingType(type); + return underlyingType ?? type; + } + + private bool IsNullableType(Type type) + { + if (type.IsValueType && Nullable.GetUnderlyingType(type) == null) + { + return false; + } + return true; + } + + public EntityMapper(bool matchNamesWithUnderscores = false) + { + MatchNamesWithUnderscores = matchNamesWithUnderscores; + } + } + + /// + /// 数据库类型到Csharp类型转换器 + /// + static class DataConvertMethod + { + #region Method Field + public static MethodInfo ToObjectMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToObject)); + public static MethodInfo ToByteMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToByte)); + public static MethodInfo ToIn16Method = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt16)); + public static MethodInfo ToIn32Method = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt32)); + public static MethodInfo ToIn64Method = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt64)); + public static MethodInfo ToFloatMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToFloat)); + public static MethodInfo ToDoubleMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDouble)); + public static MethodInfo ToDecimalMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDecimal)); + public static MethodInfo ToBooleanMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToBoolean)); + public static MethodInfo ToCharMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToChar)); + public static MethodInfo ToStringMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToString)); + public static MethodInfo ToDateTimeMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDateTime)); + public static MethodInfo ToEnumMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToEnum)); + public static MethodInfo ToGuidMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToGuid)); + #endregion + + #region NullableMethod Field + public static MethodInfo ToObjectNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertObjectNullable)); + public static MethodInfo ToByteNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToByteNullable)); + public static MethodInfo ToIn16NullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt16Nullable)); + public static MethodInfo ToIn32NullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt32Nullable)); + public static MethodInfo ToIn64NullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt64Nullable)); + public static MethodInfo ToFloatNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToFloatNullable)); + public static MethodInfo ToDoubleNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDoubleNullable)); + public static MethodInfo ToBooleanNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToBooleanNullable)); + public static MethodInfo ToDecimalNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDecimalNullable)); + public static MethodInfo ToCharNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToCharNullable)); + public static MethodInfo ToDateTimeNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDateTimeNullable)); + public static MethodInfo ToEnumNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToEnumNullable)); + public static MethodInfo ToGuidNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToGuidNullable)); + #endregion + + #region Define Convert + public static T ConvertToObject(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var data = dr.GetValue(i); + return (T)Convert.ChangeType(data, typeof(T)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static byte ConvertToByte(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var result = dr.GetByte(i); + return result; + } + catch + { + throw ThrowException(dr, i); + } + } + + public static short ConvertToInt16(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(short)) + { + return dr.GetInt16(i); + } + return Convert.ToInt16(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static int ConvertToInt32(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(int)) + { + return dr.GetInt32(i); + } + return Convert.ToInt32(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static long ConvertToInt64(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(long)) + { + return dr.GetInt64(i); + } + return Convert.ToInt64(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static float ConvertToFloat(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(float)) + { + return dr.GetFloat(i); + } + return Convert.ToSingle(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static double ConvertToDouble(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(double)) + { + return dr.GetDouble(i); + } + return Convert.ToDouble(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static bool ConvertToBoolean(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + if (dr.GetFieldType(i) == typeof(bool)) + { + var result = dr.GetBoolean(i); + return result; + } + else if (int.TryParse(dr.GetValue(i).ToString(), out int value)) + { + return !(value == 0); + } + return Convert.ToBoolean(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static decimal ConvertToDecimal(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(decimal)) + { + return dr.GetDecimal(i); + } + return Convert.ToDecimal(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static char ConvertToChar(this IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var result = dr.GetChar(i); + return result; + } + catch + { + throw ThrowException(dr, i); + } + } + + public static string ConvertToString(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(string)) + { + return dr.GetString(i); + } + var result = dr.GetValue(i); + return Convert.ToString(result); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static DateTime ConvertToDateTime(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(DateTime)) + { + return dr.GetDateTime(i); + } + return DateTime.Parse(dr.GetValue(i).ToString()); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static T ConvertToEnum(IDataRecord dr, int i) where T : struct + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var value = dr.GetValue(i); + if (Enum.TryParse(value.ToString(), out T result)) return result; + return default; + } + catch + { + throw ThrowException(dr, i); + } + } + + public static Guid ConvertToGuid(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var result = dr.GetGuid(i); + return result; + } + catch + { + throw ThrowException(dr, i); + } + } + + private static Exception ThrowException(IDataRecord dr, int i) + { + var inner = new FormatException($"Column of {dr.GetName(i)} {dr.GetFieldType(i)} '{dr.GetValue(i)}' was not recognized as a valid {typeof(T).Name}."); + return new InvalidCastException($"Unable to cast object of type '{dr.GetFieldType(i).Name}' to type '{typeof(int).Name}'.", inner); + } + #endregion + + #region Define Nullable Convert + public static T ConvertObjectNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToObject(dr, i); + } + public static byte? ConvertToByteNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToByte(dr, i); + } + public static short? ConvertToInt16Nullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToInt16(dr, i); + } + public static int? ConvertToInt32Nullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToInt32(dr, i); + } + public static long? ConvertToInt64Nullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToInt64(dr, i); + } + public static float? ConvertToFloatNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToFloat(dr, i); + } + public static double? ConvertToDoubleNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToDouble(dr, i); + } + public static bool? ConvertToBooleanNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToBoolean(dr, i); + } + public static decimal? ConvertToDecimalNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToDecimal(dr, i); + } + public static char? ConvertToCharNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToChar(dr, i); + } + public static DateTime? ConvertToDateTimeNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToDateTime(dr, i); + } + public static T? ConvertToEnumNullable(IDataRecord dr, int i) where T : struct + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToEnum(dr, i); + } + public static Guid? ConvertToGuidNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToGuid(dr, i); + } + #endregion + } +} diff --git a/src/Dapper.Common/DbContexts/MultiResult.cs b/src/Dapper.Common/DbContexts/MultiResult.cs new file mode 100644 index 0000000..62e4425 --- /dev/null +++ b/src/Dapper.Common/DbContexts/MultiResult.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; + +namespace Dapper +{ + public interface IMultiResult : IDisposable + { + /// + /// 返回当前dynamic类型结果集 + /// + /// + List GetList(); + /// + /// 异步返回当前dynamic类型结果集 + /// + /// + Task> GetListAsync(); + /// + /// 返回当前T结果集 + /// + /// 结果集类型 + /// + List GetList(); + /// + /// 异步返回当前T类型结果集 + /// + /// + /// + Task> GetListAsync(); + /// + /// 返回当前dynamic类型结果 + /// + /// + object Get(); + /// + /// 异步返回当前dynamic类型结果 + /// + /// + Task GetAsync(); + /// + /// 返回当前T类型结果 + /// + /// 结果集类型 + /// + T Get(); + /// + /// 异步返回当前T类型结果 + /// + /// 结果集类型 + /// + Task GetAsync(); + } + + public class MultiResult : IMultiResult + { + private readonly IDataReader _reader = null; + + private readonly IDbCommand _command = null; + + private readonly IEntityMapper _mapper = null; + + internal MultiResult(IDbCommand command, IEntityMapper mapper) + { + _command = command; + _reader = command.ExecuteReader(); + _mapper = mapper; + } + + public void Dispose() + { + _reader?.Dispose(); + _command?.Dispose(); + } + + public T Get() + { + return GetList().FirstOrDefault(); + } + + public async Task GetAsync() + { + return (await GetListAsync()).FirstOrDefault(); + } + + public object Get() + { + return GetList().FirstOrDefault(); + } + + public async Task GetAsync() + { + return (await GetListAsync()).FirstOrDefault(); + } + + public async Task> GetListAsync() + { + var handler = EmitConvert.GetSerializer(); + var list = new List(); + while (await (_reader as DbDataReader).ReadAsync()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + + public List GetList() + { + var handler = EmitConvert.GetSerializer(); + var list = new List(); + while (_reader.Read()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + + public List GetList() + { + var handler = EmitConvert.GetSerializer(_mapper, _reader); + var list = new List(); + while (_reader.Read()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + + public async Task> GetListAsync() + { + var handler = EmitConvert.GetSerializer(_mapper, _reader); + var list = new List(); + while (await (_reader as DbDataReader).ReadAsync()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + } +} diff --git a/src/Dapper.Common/Exceptions/DbUpdateConcurrencyException.cs b/src/Dapper.Common/Exceptions/DbUpdateConcurrencyException.cs new file mode 100644 index 0000000..9ebd516 --- /dev/null +++ b/src/Dapper.Common/Exceptions/DbUpdateConcurrencyException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.Expressions +{ + /// + /// 修改数据时并发冲突 + /// + public class DbUpdateConcurrencyException : Exception + { + public DbUpdateConcurrencyException(string message) + : base(message) + { + + } + } +} diff --git a/src/Dapper.Common/Expressions/BooleanExpressionResovle.cs b/src/Dapper.Common/Expressions/BooleanExpressionResovle.cs new file mode 100644 index 0000000..c1f065f --- /dev/null +++ b/src/Dapper.Common/Expressions/BooleanExpressionResovle.cs @@ -0,0 +1,228 @@ +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Dapper.Attributes; + +namespace Dapper.Expressions +{ + public class BooleanExpressionResovle : ExpressionResovle + { + private readonly string _prefix = "@"; + + private bool _isNotExpression = false; + + private readonly Dictionary _parameters = new Dictionary(); + + public BooleanExpressionResovle(Expression expression) + : base(expression) + { + _parameters = new Dictionary(); + } + + public BooleanExpressionResovle(Expression expression, Dictionary parameters) + : base(expression) + { + _parameters = parameters; + } + + protected override Expression VisitMember(MemberExpression node) + { + if (IsParameterExpression(node)) + { + SetParameterName(node); + } + else + { + SetParameterValue(node); + } + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.DeclaringType == typeof(Operator)) + { + if (node.Arguments.Count == 2) + { + _textBuilder.Append("("); + SetParameterName(node.Arguments[0] as MemberExpression); + var type = Operator.ResovleExpressionType(node.Method.Name); + _textBuilder.Append($" {type} "); + var value = VisitConstantValue(node.Arguments[1]); + if (node.Method.Name == nameof(Operator.StartsWith) || node.Method.Name == nameof(Operator.NotStartsWith)) + { + SetParameterValue(Expression.Constant($"{value}%", typeof(string))); + } + else if (node.Method.Name == nameof(Operator.EndsWith) || node.Method.Name == nameof(Operator.NotEndsWith)) + { + SetParameterValue(Expression.Constant($"%{value}", typeof(string))); + } + else if (node.Method.Name == nameof(Operator.Contains) || node.Method.Name == nameof(Operator.NotContains)) + { + SetParameterValue(Expression.Constant($"%{value}%", typeof(string))); + } + else + { + SetParameterValue(Expression.Constant(value)); + } + _textBuilder.Append(")"); + } + } + else if (IsLikeExpression(node)) + { + _textBuilder.Append("("); + object value = null; + if (IsParameterExpression(node.Object)) + { + SetParameterName(node.Object as MemberExpression); + value = VisitConstantValue(node.Arguments[0]); + } + else + { + SetParameterName(node.Arguments[0] as MemberExpression); + value = VisitConstantValue(node.Object); + } + if (_isNotExpression) + { + _isNotExpression = false; + _textBuilder.Append(" NOT LIKE "); + } + else + { + _textBuilder.Append(" LIKE "); + } + if (node.Method.Name == nameof(string.Contains)) + { + SetParameterValue(Expression.Constant($"%{value}%")); + } + else if (node.Method.Name == nameof(string.StartsWith)) + { + SetParameterValue(Expression.Constant($"{value}%")); + } + else + { + SetParameterValue(Expression.Constant($"%{value}")); + } + _textBuilder.Append(")"); + } + else if (IsInExpression(node)) + { + _textBuilder.Append("("); + SetParameterName(node.Arguments[1] as MemberExpression); + if (_isNotExpression) + { + _isNotExpression = false; + _textBuilder.Append(" NOT IN "); + } + else + { + _textBuilder.Append(" IN "); + } + SetParameterValue(node.Arguments[0] as MemberExpression); + _textBuilder.Append(")"); + } + else if (node.Method.DeclaringType.GetCustomAttribute(typeof(FunctionAttribute), true) != null) + { + var function = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append(function); + } + else + { + SetParameterValue(node); + } + return node; + } + + protected override Expression VisitBinary(BinaryExpression node) + { + _textBuilder.Append("("); + Visit(node.Left); + if (node.Right is ConstantExpression right && right.Value == null && (node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual)) + { + _textBuilder.AppendFormat(" {0}", node.NodeType == ExpressionType.Equal ? "IS NULL" : "IS NOT NULL"); + } + else + { + _textBuilder.Append($" {Operator.ResovleExpressionType(node.NodeType)} "); + Visit(node.Right); + } + _textBuilder.Append(")"); + return node; + } + + protected override Expression VisitNew(NewExpression node) + { + SetParameterValue(node); + return node; + } + + protected override Expression VisitUnary(UnaryExpression node) + { + if (node.NodeType == ExpressionType.Not) + { + if (node.Operand is MethodCallExpression methodCallExpression + && (IsInExpression(methodCallExpression) || IsLikeExpression(methodCallExpression))) + { + _isNotExpression = true; + } + else + { + _textBuilder.AppendFormat("{0} ", Operator.ResovleExpressionType(ExpressionType.Not)); + } + Visit(node.Operand); + } + else + { + Visit(node.Operand); + } + return node; + } + + protected override Expression VisitConstant(ConstantExpression node) + { + SetParameterValue(node); + return node; + } + + private void SetParameterName(MemberExpression expression) + { + var name = GetColumnName(expression.Member.DeclaringType, expression.Member.Name); + _textBuilder.Append(name); + } + + private void SetParameterValue(Expression expression) + { + var value = VisitConstantValue(expression); + var parameterName = $"P_{_parameters.Count}"; + _parameters.Add(parameterName, value); + _textBuilder.Append($"{_prefix}{parameterName}"); + } + + private bool IsLikeExpression(MethodCallExpression node) + { + var array = new string[] + { + nameof(string.Contains), + nameof(string.StartsWith), + nameof(string.EndsWith) + }; + return node.Method.DeclaringType == typeof(string) + && array.Contains(node.Method.Name) + && node.Arguments.Count == 1; + } + + private bool IsParameterExpression(Expression expression) + { + return expression is MemberExpression memberExpression && + memberExpression.Expression?.NodeType == ExpressionType.Parameter; + } + + private bool IsInExpression(MethodCallExpression node) + { + return node.Method.DeclaringType == typeof(Enumerable) + && node.Method.Name == nameof(Enumerable.Contains) + && node.Arguments.Count == 2; + } + } +} diff --git a/src/Dapper.Common/Expressions/ExpressionActivator.cs b/src/Dapper.Common/Expressions/ExpressionActivator.cs new file mode 100644 index 0000000..71f23f1 --- /dev/null +++ b/src/Dapper.Common/Expressions/ExpressionActivator.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace Dapper.Expressions +{ + /// + /// 表达式树生成器 + /// + public class ExpressionActivator + { + private Dictionary Factorization(string expression) + { + var experssions = new Dictionary(); + var pattern = @"\([^\(\)]+\)"; + var text = $"({expression})"; + while (Regex.IsMatch(text, pattern)) + { + var key = $"${experssions.Count}"; + var value = Regex.Match(text, pattern).Value; + experssions.Add(key, value); + text = text.Replace(value, key); + } + return experssions; + } + + private Expression CreateExpression(ParameterExpression parameter, string expression) + { + var expressions1 = Factorization(expression); + var expressions2 = new Dictionary(); + foreach (var item in expressions1) + { + var subexpr = item.Value.Trim('(', ')'); + var @opterator = ResovleOperator(item.Value); + var opt = GetExpressionType(@opterator); + if (opt == ExpressionType.Not) + { + Expression exp; + var text = subexpr.Split(new string[] { @opterator }, StringSplitOptions.RemoveEmptyEntries)[0].Trim(); + if (expressions2.ContainsKey(text)) + { + exp = expressions2[text]; + } + else if (parameter.Type.GetProperties().Any(a => a.Name == text)) + { + var property = parameter.Type.GetProperty(text); + exp = Expression.MakeMemberAccess(parameter, property); + } + else + { + exp = Expression.Constant(Convert.ToBoolean(text)); + } + expressions2.Add(item.Key, Expression.MakeUnary(opt, exp, null)); + } + else + { + var text1 = subexpr + .Split(new string[] { @opterator }, StringSplitOptions.RemoveEmptyEntries)[0] + .Trim(); + var text2 = subexpr + .Split(new string[] { @opterator }, StringSplitOptions.RemoveEmptyEntries)[1] + .Trim(); + string temp = null; + Expression exp1, exp2; + //永远将变量放在第一个操作数 + if (parameter.Type.GetProperties().Any(a => a.Name == text2)) + { + temp = text1; + text1 = text2; + text2 = temp; + } + //是否为上一次的分式 + if (expressions2.ContainsKey(text1)) + { + exp1 = expressions2[text1]; + } + else if (parameter.Type.GetProperties().Any(a => a.Name == text1)) + { + //是否为变量 + var property = parameter.Type.GetProperty(text1); + exp1 = Expression.MakeMemberAccess(parameter, property); + } + else + { + exp1 = ResovleConstantExpression(text1); + } + //是否为上一次的分式 + if (expressions2.ContainsKey(text2)) + { + exp2 = expressions2[text2]; + } + //如果第一个操作数是变量 + else if (parameter.Type.GetProperties().Any(a => a.Name == text1)) + { + var constantType = parameter.Type.GetProperty(text1).PropertyType; + exp2 = ResovleConstantExpression(text2, constantType); + } + else + { + exp2 = ResovleConstantExpression(text1, (exp1 as ConstantExpression)?.Type); + } + expressions2.Add(item.Key, Expression.MakeBinary(opt, exp1, exp2)); + } + } + return expressions2.Last().Value; + } + + public ExpressionContextResult Create(string expression) + { + expression = Initialization(expression); + var parameter = Expression.Parameter(typeof(T), "p"); + var body = CreateExpression(parameter, expression); + var lambda = Expression.Lambda(body, parameter); + var func = lambda.Compile() as Func; + return new ExpressionContextResult() + { + Func = func, + LambdaExpression = lambda + }; + } + + private string Initialization(string expression) + { + expression = Regex.Replace(expression, @"(?<=[^\'][\s]+)and(?=\s+[^\'])","&&",RegexOptions.IgnoreCase); + expression = Regex.Replace(expression, @"(?<=[^\'][\s]+)or(?=\s+[^\'])", "||", RegexOptions.IgnoreCase); + expression = Regex.Replace(expression, @"(?<=[^\'][\s]+)not(?=\s+[^\'])", "!", RegexOptions.IgnoreCase); + return expression; + } + + private ExpressionType GetExpressionType(string text) + { + switch (text) + { + case "+": return ExpressionType.Add; + case "-": return ExpressionType.Subtract; + case "*": return ExpressionType.Multiply; + case "/": return ExpressionType.Divide; + case "%": return ExpressionType.Modulo; + case "!": return ExpressionType.Not; + case ">": return ExpressionType.GreaterThan; + case "<": return ExpressionType.LessThan; + case ">=": return ExpressionType.GreaterThanOrEqual; + case "<=": return ExpressionType.LessThanOrEqual; + case "==": return ExpressionType.Equal; + case "!=": return ExpressionType.NotEqual; + case "&&": return ExpressionType.AndAlso; + case "||": return ExpressionType.OrElse; + default: + throw new InvalidOperationException(text); + } + } + + private Expression ResovleConstantExpression(string expression, Type type) + { + //生成指定类型的表达式 + if (expression == "null") + { + return Expression.Constant(null, type); + } + else if (type == typeof(string)) + { + return Expression.Constant(expression.Trim('\'', '\''), type); + } + else + { + if (Nullable.GetUnderlyingType(type) == null) + { + var value = Convert.ChangeType(expression, type); + return Expression.Constant(value, type); + } + else + { + var undertype = Nullable.GetUnderlyingType(type); + var value = Convert.ChangeType(expression, undertype); + var expr = Expression.Constant(value, undertype); + return Expression.MakeUnary(ExpressionType.Convert, expr, type); + } + } + } + + private Expression ResovleConstantExpression(string expression) + { + //自动类型推断生成表达式 + if (expression.StartsWith("'") && expression.EndsWith("'")) + { + //字符串常量 + return Expression.Constant(expression.Trim('\''), typeof(string)); + } + else if (expression == "true" || expression == "false") + { + return Expression.Constant(expression, typeof(bool)); + } + else if (Regex.IsMatch(expression, @"^\d+$")) + { + //int类型常量 + return Expression.Constant(expression, typeof(int)); + } + else if (Regex.IsMatch(expression, @"^\d*\.\d*$")) + { + //double + return Expression.Constant(expression, typeof(int)); + } + else if (expression == "null") + { + return Expression.Constant(null, typeof(object)); + } + return Expression.Constant(expression, typeof(object)); + } + + private string ResovleOperator(string text) + { + var operators = new string[] { "!", "*", "/", "%", "+", "-", "<", ">", "<=", ">=", "==", "!=", "&&", "||" }; + for (int i = 0; i < text.Length - 1; i++) + { + var opt1 = text[i].ToString(); + var opt2 = text.Substring(i, 2); + if (operators.Contains(opt2)) + { + return opt2; + } + else if(operators.Contains(opt1)) + { + return opt1; + } + } + throw new Exception("resolve operator eroor"); + } + } + + public class ExpressionContextResult + { + public Func Func { get; internal set; } + public Expression LambdaExpression { get; internal set; } + } +} diff --git a/src/Dapper.Common/Expressions/ExpressionResovle.cs b/src/Dapper.Common/Expressions/ExpressionResovle.cs new file mode 100644 index 0000000..cf0cf0a --- /dev/null +++ b/src/Dapper.Common/Expressions/ExpressionResovle.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace Dapper.Expressions +{ + public abstract class ExpressionResovle : ExpressionVisitor + { + protected ExpressionResovle(Expression expression) + { + _expression = expression; + } + + protected readonly Expression _expression = null; + + protected readonly StringBuilder _textBuilder = new StringBuilder(); + + /// + /// 解析表达式参数 + /// + /// + /// + public object VisitConstantValue(Expression expression) + { + var names = new Stack(); + var exps = new Stack(); + var mifs = new Stack(); + if (expression is ConstantExpression constant) + return constant.Value; + else if (expression is MemberExpression) + { + var temp = expression; + object value = null; + while (temp is MemberExpression memberExpression) + { + names.Push(memberExpression.Member.Name); + exps.Push(memberExpression.Expression); + mifs.Push(memberExpression.Member); + temp = memberExpression.Expression; + } + foreach (var name in names) + { + var exp = exps.Pop(); + var mif = mifs.Pop(); + if (exp is ConstantExpression cex) + value = cex.Value; + if (mif is PropertyInfo pif) + value = pif.GetValue(value); + else if (mif is FieldInfo fif) + value = fif.GetValue(value); + } + return value; + } + else + { + return Expression.Lambda(expression).Compile().DynamicInvoke(); + } + } + + /// + /// 获取字段名 + /// + /// + /// + /// + protected string GetColumnName(Type type,string csharpName) + { + var columns = DbMetaInfoCache.GetColumns(type); + return columns.Where(a => a.CsharpName == csharpName) + .FirstOrDefault().ColumnName; + } + + public virtual string Resovle() + { + Visit(_expression); + return _textBuilder.ToString(); + } + } +} diff --git a/src/Dapper.Common/Expressions/FunctionExpressionResovle.cs b/src/Dapper.Common/Expressions/FunctionExpressionResovle.cs new file mode 100644 index 0000000..36beb06 --- /dev/null +++ b/src/Dapper.Common/Expressions/FunctionExpressionResovle.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; + +namespace Dapper.Expressions +{ + public class FunctionExpressionResovle : ExpressionResovle + { + public FunctionExpressionResovle(Expression expression) + : base(expression) + { + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + _textBuilder.Append(node.Method.Name.ToUpper()); + _textBuilder.Append("("); + for (var i = 0; i < node.Arguments.Count; i++) + { + var item = node.Arguments[i]; + Visit(item); + } + if (_textBuilder[_textBuilder.Length - 1] == ',') + { + _textBuilder.Remove(_textBuilder.Length - 1, 1); + } + _textBuilder.Append(")"); + return node; + } + + protected override Expression VisitConstant(ConstantExpression node) + { + var value = VisitConstantValue(node); + if (value == null) + { + value = "NULL"; + } + else if (value is string) + { + value = $"'{value}'"; + } + else if (value is bool) + { + value = Convert.ToBoolean(value) ? 1 : 0; + } + _textBuilder.Append($"{value},"); + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name},"); + return node; + } + + protected override Expression VisitNewArray(NewArrayExpression node) + { + foreach (var item in node.Expressions) + { + Visit(item); + } + return node; + } + + protected override Expression VisitUnary(UnaryExpression node) + { + if (node.Operand != null) + { + Visit(node.Operand); + } + return node; + } + + } +} diff --git a/src/Dapper.Common/Expressions/GroupExpressionResovle.cs b/src/Dapper.Common/Expressions/GroupExpressionResovle.cs new file mode 100644 index 0000000..bc11e73 --- /dev/null +++ b/src/Dapper.Common/Expressions/GroupExpressionResovle.cs @@ -0,0 +1,40 @@ +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + public class GroupExpressionResovle : ExpressionResovle + { + public GroupExpressionResovle(Expression expression) + : base(expression) + { + } + + protected override Expression VisitNew(NewExpression node) + { + foreach (var item in node.Arguments) + { + Visit(item); + } + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name},"); + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var result = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append($"{result},"); + return node; + } + + public override string Resovle() + { + return base.Resovle().Trim(','); + } + } +} diff --git a/src/Dapper.Common/Expressions/OrderExpressionResovle.cs b/src/Dapper.Common/Expressions/OrderExpressionResovle.cs new file mode 100644 index 0000000..1e65d20 --- /dev/null +++ b/src/Dapper.Common/Expressions/OrderExpressionResovle.cs @@ -0,0 +1,45 @@ +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + public class OrderExpressionResovle : ExpressionResovle + { + private readonly string _asc = string.Empty; + + public OrderExpressionResovle(Expression expression, bool asc) + : base(expression) + { + if (!asc) + { + _asc = " DESC"; + } + } + protected override Expression VisitNew(NewExpression node) + { + foreach (var item in node.Arguments) + { + Visit(item); + } + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var result = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append($"{result}{_asc},"); + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name}{_asc},"); + return node; + } + + public override string Resovle() + { + return base.Resovle().Trim(','); + } + } +} diff --git a/src/Dapper.Common/Expressions/SelectExpressionResovle.cs b/src/Dapper.Common/Expressions/SelectExpressionResovle.cs new file mode 100644 index 0000000..11ba643 --- /dev/null +++ b/src/Dapper.Common/Expressions/SelectExpressionResovle.cs @@ -0,0 +1,83 @@ +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + public class SelectExpressionResovle : ExpressionResovle + { + public SelectExpressionResovle(Expression expression) + : base(expression) + { + + } + + protected override Expression VisitMemberInit(MemberInitExpression node) + { + for (int i = 0; i < node.Bindings.Count; i++) + { + var item = node.Bindings[i] as MemberAssignment; + + if (item.Expression is MemberExpression member) + { + var name = GetColumnName(member.Member.DeclaringType, member.Member.Name); + _textBuilder.Append($"{name} AS {item.Member.Name}"); + } + else if (item.Expression is MethodCallExpression) + { + var expression = new FunctionExpressionResovle(item.Expression).Resovle(); + _textBuilder.Append($"{expression} AS {item.Member.Name}"); + } + if (i != node.Bindings.Count - 1) + { + _textBuilder.Append(","); + } + } + return node; + } + + protected override Expression VisitNew(NewExpression node) + { + for (int i = 0; i < node.Arguments.Count; i++) + { + var item = node.Arguments[i]; + var column = node.Members[i].Name; + if (item is MemberExpression member) + { + var name = GetColumnName(member.Member.DeclaringType, member.Member.Name); + if (name != column) + { + _textBuilder.Append($"{name} AS {column}"); + } + else + { + _textBuilder.Append($"{name}"); + } + } + else if (item is MethodCallExpression) + { + var expression = new FunctionExpressionResovle(item).Resovle(); + _textBuilder.Append($"{expression} AS {column}"); + } + if (i != node.Arguments.Count - 1) + { + _textBuilder.Append(","); + } + } + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name}"); + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var result = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append($"{result} AS expr"); + return node; + } + + } +} diff --git a/src/Dapper.Common/Queryables/DbMetaInfoCache.cs b/src/Dapper.Common/Queryables/DbMetaInfoCache.cs new file mode 100644 index 0000000..8323a75 --- /dev/null +++ b/src/Dapper.Common/Queryables/DbMetaInfoCache.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Text; +using System.Linq; +using Dapper.Attributes; +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + /// + /// 数据库元信息 + /// + public static class DbMetaInfoCache + { + private static readonly ConcurrentDictionary _tables + = new ConcurrentDictionary(); + + private static readonly ConcurrentDictionary> _columns + = new ConcurrentDictionary>(); + + public static TableInfo GetTable(Type type) + { + return _tables.GetOrAdd(type, t => + { + var name = t.Name; + if (t.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault() != null) + { + var attribute = t.GetCustomAttributes(typeof(TableAttribute), true) + .FirstOrDefault() as TableAttribute; + name = attribute.Name; + } + var table = new TableInfo() + { + TableName = name, + CsharpName = t.Name + }; + return table; + }); + } + + public static List GetColumns(Type type) + { + return _columns.GetOrAdd(type, t => + { + var list = new List(); + var properties = type.GetProperties(); + foreach (var item in properties) + { + var columnName = item.Name; + var isPrimaryKey = false; + var isDefault = false; + var isIdentity = false; + var isNotMapped = false; + var isConcurrencyCheck = false; + var isComplexType = false; + if (item.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault() != null) + { + var attribute = item.GetCustomAttributes(typeof(ColumnAttribute), true) + .FirstOrDefault() as ColumnAttribute; + columnName = attribute.Name; + } + if (item.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).FirstOrDefault() != null) + { + isPrimaryKey = true; + } + if (item.GetCustomAttributes(typeof(IdentityAttribute), true).FirstOrDefault() != null) + { + isIdentity = true; + } + if (item.GetCustomAttributes(typeof(DefaultAttribute), true).FirstOrDefault() != null) + { + isDefault = true; + } + if (item.GetCustomAttributes(typeof(ConcurrencyCheckAttribute), true).FirstOrDefault() != null) + { + isConcurrencyCheck = true; + } + if (item.GetCustomAttributes(typeof(NotMappedAttribute), true).FirstOrDefault() != null) + { + isNotMapped = true; + } + if (item.GetCustomAttributes(typeof(ComplexTypeAttribute), true).FirstOrDefault() != null) + { + isComplexType = true; + } + list.Add(new ColumnInfo() + { + CsharpType = item.PropertyType, + IsDefault = isDefault, + ColumnName = columnName, + CsharpName = item.Name, + IsPrimaryKey = isPrimaryKey, + IsIdentity = isIdentity, + IsNotMapped = isNotMapped, + IsConcurrencyCheck = isConcurrencyCheck, + IsComplexType = isComplexType + }); + } + return list; + }); + } + + } + + /// + /// 表信息 + /// + public class TableInfo + { + public string TableName { get; set; } + public string CsharpName { get; set; } + } + + /// + /// 字段信息 + /// + public class ColumnInfo + { + public bool IsConcurrencyCheck { get; set; } + public bool IsDefault { get; set; } + public bool IsNotMapped { get; set; } + public string ColumnName { get; set; } + public string CsharpName { get; set; } + public Type CsharpType { get; set; } + public bool IsPrimaryKey { get; set; } + public bool IsIdentity { get; set; } + public bool IsComplexType { get; set; } + } +} diff --git a/src/Dapper.Common/Queryables/DbQuery.cs b/src/Dapper.Common/Queryables/DbQuery.cs new file mode 100644 index 0000000..e5f6722 --- /dev/null +++ b/src/Dapper.Common/Queryables/DbQuery.cs @@ -0,0 +1,433 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using Dapper.Expressions; + +namespace Dapper +{ + public partial class DbQuery : IDbQuery + { + #region fields + + private readonly Dictionary _parameters + = new Dictionary(); + + private readonly PageData _page = new PageData(); + + private string _lockname = string.Empty; + + private readonly IDbContext _context = null; + + private readonly List _whereExpressions = new List(); + + private readonly List _setExpressions = new List(); + + private readonly List _orderExpressions = new List(); + + private readonly List _groupExpressions = new List(); + + private readonly List _havingExpressions = new List(); + + private Expression _filterExpression = null; + + private Expression _selectExpression = null; + + private Expression _countExpression = null; + + public DbQuery(IDbContext context) + { + _context = context; + } + + #endregion + + #region resovles + private void ResovleParameter(T entity) + { + var serializer = EmitConvert.GetDeserializer(typeof(T)); + var values = serializer(entity); + foreach (var item in values) + { + if (_parameters.ContainsKey(item.Key)) + { + _parameters[item.Key] = item.Value; + } + else + { + _parameters.Add(item.Key, item.Value); + } + } + } + + private string ResovleCount() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var column = "COUNT(1)"; + var where = ResolveWhere(); + var group = ResolveGroup(); + if (group.Length > 0) + { + column = group.Remove(0, 10); + } + else if (_countExpression != null) + { + column = new SelectExpressionResovle(_countExpression).Resovle(); + } + var sql = $"SELECT {column} FROM {table}{where}{group}"; + if (group.Length > 0) + { + sql = $"SELECT COUNT(1) FROM ({sql}) as t"; + return sql; + } + return sql; + } + + private string ResovleSum() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var column = $"SUM({ResovleColumns()})"; + var where = ResolveWhere(); + var sql = $"SELECT {column} FROM {table}{where}"; + return sql; + } + + private string ResolveGet() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var columns = DbMetaInfoCache.GetColumns(typeof(T)); + var column = ResovleColumns(); + var where = $" WHERE {columns.Where(a => a.IsPrimaryKey == true).First().ColumnName}=@id"; + string sql; + if (_context.DbContextType == DbContextType.SqlServer) + { + sql = $"SELECT TOP 1 {column} FROM {table}{where}"; + } + else + { + sql = $"SELECT {column} FROM {table}{where} LIMIT 0,1"; + } + return sql; + } + + private string ResolveSelect() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var column = ResovleColumns(); + var where = ResolveWhere(); + var group = ResolveGroup(); + var having = ResolveHaving(); + var order = ResolveOrder(); + string sql; + if (_context.DbContextType == DbContextType.SqlServer) + { + if (_lockname != string.Empty) + { + _lockname = $" WITH({_lockname})"; + } + if (_page.Index == 0) + { + sql = $"SELECT TOP {_page.Count} {column} FROM {table}{_lockname}{where}{group}{having}{order}"; + } + else if (_page.Index > 0) + { + if (order == string.Empty) + { + order = " ORDER BY (SELECT 1)"; + } + var limit = $" OFFSET {_page.Index} ROWS FETCH NEXT {_page.Count} ROWS ONLY"; + sql = $"SELECT {column} FROM {_lockname}{table}{where}{group}{having}{order}{limit}"; + } + else + { + sql = $"SELECT {column} FROM {_lockname}{table}{where}{group}{having}{order}"; + } + } + else + { + var limit = _page.Index > 0 || _page.Count > 0 ? $" LIMIT {_page.Index},{_page.Count}" : string.Empty; + sql = $"SELECT {column} FROM {table}{where}{group}{having}{order}{limit}{_lockname}"; + } + return sql; + } + + private string ResovleInsert(bool identity) + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var filters = new GroupExpressionResovle(_filterExpression).Resovle().Split(','); + var columns = DbMetaInfoCache.GetColumns(typeof(T)); + var intcolumns = columns + .Where(a => !filters.Contains(a.ColumnName) && !a.IsNotMapped && !a.IsIdentity) + .Where(a=> !a.IsComplexType) + .Where(a => !a.IsDefault || (_parameters.ContainsKey(a.CsharpName) && _parameters[a.CsharpName] != null));//如果是默认字段 + var columnNames = string.Join(",", intcolumns.Select(s => s.ColumnName)); + var parameterNames = string.Join(",", intcolumns.Select(s => $"@{s.CsharpName}")); + var sql = $"INSERT INTO {table}({columnNames}) VALUES ({parameterNames})"; + if (identity) + { + sql = $"{sql};SELECT @@IDENTITY"; + } + return sql; + } + + private string ResovleBatchInsert(IEnumerable entitys) + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var filters = new GroupExpressionResovle(_filterExpression).Resovle().Split(','); + var columns = DbMetaInfoCache.GetColumns(typeof(T)) + .Where(a=>!a.IsComplexType).ToList(); + var intcolumns = columns + .Where(a => !filters.Contains(a.ColumnName) && !a.IsNotMapped && !a.IsIdentity) + .ToList(); + var columnNames = string.Join(",", intcolumns.Select(s => s.ColumnName)); + if (_context.DbContextType == DbContextType.Mysql) + { + var buffer = new StringBuilder(); + buffer.Append($"INSERT INTO {table}({columnNames}) VALUES "); + var serializer = EmitConvert.GetDeserializer(typeof(T)); + var list = entitys.ToList(); + for (var i = 0; i < list.Count; i++) + { + var item = list[i]; + var values = serializer(item); + buffer.Append("("); + for (var j = 0; j < intcolumns.Count; j++) + { + var column = intcolumns[j]; + var value = values[column.CsharpName]; + if (value == null) + { + buffer.Append(column.IsDefault ? "DEFAULT" : "NULL"); + } + else if (column.CsharpType == typeof(bool) || column.CsharpType == typeof(bool?)) + { + buffer.Append(Convert.ToBoolean(value) == true ? 1 : 0); + } + else if (column.CsharpType == typeof(DateTime) || column.CsharpType == typeof(DateTime?)) + { + buffer.Append($"'{value}'"); + } + else if (column.CsharpType.IsValueType || (Nullable.GetUnderlyingType(column.CsharpType)?.IsValueType == true)) + { + buffer.Append(value); + } + else + { + var str = SqlEncoding(value.ToString()); + buffer.Append($"'{str}'"); + } + if (j + 1 < intcolumns.Count) + { + buffer.Append(","); + } + } + buffer.Append(")"); + if (i + 1 < list.Count) + { + buffer.Append(","); + } + } + return buffer.Remove(buffer.Length - 1, 0).ToString(); + } + throw new NotImplementedException(); + } + + private string ResolveUpdate() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var builder = new StringBuilder(); + if (_setExpressions.Count > 0) + { + var where = ResolveWhere(); + foreach (var item in _setExpressions) + { + var column = new BooleanExpressionResovle(item.Column).Resovle(); + var expression = new BooleanExpressionResovle(item.Expression, _parameters).Resovle(); + builder.Append($"{column} = {expression},"); + } + var sql = $"UPDATE {table} SET {builder.ToString().Trim(',')}{where}"; + return sql; + } + else + { + var filters = new GroupExpressionResovle(_filterExpression).Resovle().Split(','); + var where = ResolveWhere(); + var columns = DbMetaInfoCache.GetColumns(typeof(T)); + var updcolumns = columns + .Where(a => !filters.Contains(a.ColumnName)) + .Where(a => !a.IsComplexType) + .Where(a => !a.IsIdentity && !a.IsPrimaryKey && !a.IsNotMapped) + .Where(a => !a.IsConcurrencyCheck) + .Select(s => $"{s.ColumnName} = @{s.CsharpName}"); + if (string.IsNullOrEmpty(where)) + { + var primaryKey = columns.Where(a => a.IsPrimaryKey).FirstOrDefault() + ?? columns.First(); + where = $" WHERE {primaryKey.ColumnName} = @{primaryKey.CsharpName}"; + if (columns.Exists(a => a.IsConcurrencyCheck)) + { + var checkColumn = columns.Where(a => a.IsConcurrencyCheck).FirstOrDefault(); + where += $" AND {checkColumn.ColumnName} = @{checkColumn.CsharpName}"; + } + } + var sql = $"UPDATE {table} SET {string.Join(",", updcolumns)}"; + if (columns.Exists(a => a.IsConcurrencyCheck)) + { + var checkColumn = columns.Where(a => a.IsConcurrencyCheck).FirstOrDefault(); + sql += $",{checkColumn.ColumnName} = @New{checkColumn.CsharpName}"; + if (checkColumn.CsharpType.IsValueType) + { + var version = Convert.ToInt32((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds); + _parameters.Add($"New{checkColumn.CsharpName}", version); + } + else + { + var version = Guid.NewGuid().ToString("N"); + _parameters.Add($"New{checkColumn.CsharpName}", version); + } + } + sql += where; + return sql; + } + } + + private string ResovleDelete() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var where = ResolveWhere(); + var sql = $"DELETE FROM {table}{where}"; + return sql; + } + + private string SqlEncoding(string sql) + { + var buffer = new StringBuilder(); + for (int i = 0; i < sql.Length; i++) + { + var ch = sql[i]; + if (ch=='\''||ch=='-'||ch=='\\'||ch=='*'||ch=='@') + { + buffer.Append('\\'); + } + buffer.Append(ch); + } + return buffer.ToString(); + } + + private string ResovleExists() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var where = ResolveWhere(); + var group = ResolveGroup(); + var having = ResolveHaving(); + var sql = $"SELECT 1 WHERE EXISTS(SELECT 1 FROM {table}{where}{group}{having})"; + return sql; + } + + private string ResovleColumns() + { + if (_selectExpression == null) + { + var filters = new GroupExpressionResovle(_filterExpression) + .Resovle().Split(','); + var columns = DbMetaInfoCache.GetColumns(typeof(T)) + .Where(a => !filters.Contains(a.ColumnName) && !a.IsNotMapped) + .Select(s => s.ColumnName != s.CsharpName ? $"{s.ColumnName} AS {s.CsharpName}" : s.CsharpName); + return string.Join(",", columns); + } + else + { + return new SelectExpressionResovle(_selectExpression).Resovle(); + } + } + + private string ResolveWhere() + { + var builder = new StringBuilder(); + foreach (var expression in _whereExpressions) + { + var result = new BooleanExpressionResovle(expression, _parameters).Resovle(); + if (expression == _whereExpressions.First()) + { + builder.Append($" WHERE {result}"); + } + else + { + builder.Append($" AND {result}"); + } + } + return builder.ToString(); + } + + private string ResolveGroup() + { + var buffer = new StringBuilder(); + foreach (var item in _groupExpressions) + { + var result = new GroupExpressionResovle(item).Resovle(); + buffer.Append($"{result},"); + } + var sql = string.Empty; + if (buffer.Length > 0) + { + buffer.Remove(buffer.Length - 1, 1); + sql = $" GROUP BY {buffer.ToString()}"; + } + return sql; + } + + private string ResolveHaving() + { + var buffer = new StringBuilder(); + foreach (var item in _havingExpressions) + { + var result = new BooleanExpressionResovle(item, _parameters).Resovle(); + if (item == _havingExpressions.First()) + { + buffer.Append($" HAVING {result}"); + } + else + { + buffer.Append($" AND {result}"); + } + } + return buffer.ToString(); + } + + private string ResolveOrder() + { + var buffer = new StringBuilder(); + foreach (var item in _orderExpressions) + { + if (item == _orderExpressions.First()) + { + buffer.Append($" ORDER BY "); + } + var result = new OrderExpressionResovle(item.Expression, item.Asc).Resovle(); + buffer.Append(result); + buffer.Append(","); + } + return buffer.ToString().Trim(','); + } + + class PageData + { + public int Index { get; set; } = -1; + public int Count { get; set; } + } + + class OrderExpression + { + public bool Asc { get; set; } = true; + public Expression Expression { get; set; } + } + + class SetExpression + { + public Expression Column { get; set; } + public Expression Expression { get; set; } + } + #endregion + } +} diff --git a/src/Dapper.Common/Queryables/DbQueryAsync.cs b/src/Dapper.Common/Queryables/DbQueryAsync.cs new file mode 100644 index 0000000..4c9e0e8 --- /dev/null +++ b/src/Dapper.Common/Queryables/DbQueryAsync.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Dapper.Expressions; + +namespace Dapper +{ + /// + /// 异步linq查询 + /// + public partial class DbQuery + { + + #region async + public async Task GetAsync(object id) + { + var sql = ResolveGet(); + var values = new Dictionary + { + { "id", id } + }; + return (await _context.ExecuteQueryAsync(sql, values)).FirstOrDefault(); + } + public Task CountAsync(int? commandTimeout = null) + { + var sql = ResovleCount(); + return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); + } + + public Task CountAsync(Expression> expression) + { + _countExpression = expression; + return CountAsync(); + } + + public Task DeleteAsync(int? commandTimeout = null) + { + var sql = ResovleDelete(); + return _context.ExecuteNonQueryAsync(sql, _parameters, commandTimeout); + } + + public Task DeleteAsync(Expression> expression) + { + Where(expression); + return DeleteAsync(); + } + + public Task ExistsAsync(int? commandTimeout = null) + { + var sql = ResovleExists(); + return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); + } + + public Task ExistsAsync(Expression> expression) + { + Where(expression); + return ExistsAsync(); + } + + public Task UpdateAsync(int? commandTimeout = null) + { + if (_setExpressions.Count > 0) + { + var sql = ResolveUpdate(); + return _context.ExecuteNonQueryAsync(sql, _parameters, commandTimeout); + } + return default; + } + + public async Task UpdateAsync(T entity) + { + ResovleParameter(entity); + var sql = ResolveUpdate(); + var row = await _context.ExecuteNonQueryAsync(sql, _parameters); + if (DbMetaInfoCache.GetColumns(typeof(T)).Exists(a => a.IsConcurrencyCheck) && row == 0) + { + throw new DbUpdateConcurrencyException("更新失败:数据版本不一致"); + } + return row; + } + + public Task InsertAsync(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(false); + return _context.ExecuteNonQueryAsync(sql, _parameters); + } + + public async Task InsertAsync(IEnumerable entitys, int? commandTimeout = null) + { + if (entitys == null || entitys.Count() == 0) + { + return 0; + } + var sql = ResovleBatchInsert(entitys); + return await _context.ExecuteNonQueryAsync(sql, _parameters, commandTimeout); + } + + public Task InsertReturnIdAsync(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(true); + return _context.ExecuteScalarAsync(sql, _parameters); + } + + public async Task SumAsync(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResovleSum(); + return await _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); + } + + public Task> SelectAsync(int? commandTimeout = null) + { + var sql = ResolveSelect(); + return _context.ExecuteQueryAsync(sql, _parameters, commandTimeout); + } + + public async Task<(IEnumerable, int)> SelectManyAsync(int? commandTimeout = null) + { + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = await multi.GetListAsync(); + var count = await multi.GetAsync(); + return (list, count); + } + } + + public Task> SelectAsync(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResolveSelect(); + return _context.ExecuteQueryAsync(sql, _parameters, commandTimeout); + } + + public async Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = await multi.GetListAsync(); + var count = await multi.GetAsync(); + return (list, count); + } + } + + public async Task SingleAsync(int? commandTimeout = null) + { + Take(1); + return (await SelectAsync(commandTimeout)).FirstOrDefault(); + } + + public async Task SingleAsync(Expression> expression, int? commandTimeout = null) + { + Take(1); + return (await SelectAsync(expression, commandTimeout)).FirstOrDefault(); + } + #endregion + } +} diff --git a/src/Dapper.Common/Queryables/DbQuerySync.cs b/src/Dapper.Common/Queryables/DbQuerySync.cs new file mode 100644 index 0000000..dcb5e2d --- /dev/null +++ b/src/Dapper.Common/Queryables/DbQuerySync.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using Dapper.Expressions; + +namespace Dapper +{ + /// + /// 同步linq查询 + /// + /// + public partial class DbQuery + { + #region sync + public T Get(object id) + { + var sql = ResolveGet(); + var values = new Dictionary + { + { "id", id } + }; + return _context.ExecuteQuery(sql, values).FirstOrDefault(); + } + + public int Count(int? commandTimeout = null) + { + var sql = ResovleCount(); + return _context.ExecuteScalar(sql, _parameters, commandTimeout); + } + + public int Count(Expression> expression) + { + _countExpression = expression; + return Count(); + } + + public int Insert(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(false); + return _context.ExecuteNonQuery(sql, _parameters); + } + + public int InsertReturnId(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(true); + return _context.ExecuteScalar(sql, _parameters); + } + + public int Insert(IEnumerable entitys, int? commandTimeout = null) + { + if (entitys==null || entitys.Count()==0) + { + return 0; + } + var sql = ResovleBatchInsert(entitys); + return _context.ExecuteNonQuery(sql, _parameters, commandTimeout); + } + + public int Update(int? commandTimeout = null) + { + if (_setExpressions.Count > 0) + { + var sql = ResolveUpdate(); + return _context.ExecuteNonQuery(sql, _parameters, commandTimeout); + } + return default; + } + + public int Update(T entity) + { + ResovleParameter(entity); + var sql = ResolveUpdate(); + var row = _context.ExecuteNonQuery(sql, _parameters); + if (DbMetaInfoCache.GetColumns(typeof(T)).Exists(a => a.IsConcurrencyCheck) && row == 0) + { + throw new DbUpdateConcurrencyException("更新失败:数据版本不一致"); + } + return row; + } + + public int Delete(int? commandTimeout = null) + { + var sql = ResovleDelete(); + return _context.ExecuteNonQuery(sql, _parameters, commandTimeout); + } + + public int Delete(Expression> expression) + { + Where(expression); + return Delete(); + } + + public bool Exists(int? commandTimeout = null) + { + var sql = ResovleExists(); + return _context.ExecuteScalar(sql, _parameters, commandTimeout); + } + + public bool Exists(Expression> expression) + { + Where(expression); + return Exists(); + } + + public IDbQuery Set(Expression> column, TResult value, bool condition = true) + { + if (true) + { + _setExpressions.Add(new SetExpression + { + Column = column, + Expression = Expression.Constant(value) + }); + } + return this; + } + + public IDbQuery Set(Expression> column, Expression> expression, bool condition = true) + { + if (true) + { + _setExpressions.Add(new SetExpression + { + Column = column, + Expression = expression + }); + } + return this; + } + + public IDbQuery GroupBy(Expression> expression) + { + _groupExpressions.Add(expression); + return this; + } + + public IDbQuery Having(Expression> expression, bool condition = true) + { + if (condition) + { + _havingExpressions.Add(expression); + } + return this; + } + + public IDbQuery OrderBy(Expression> expression) + { + _orderExpressions.Add(new OrderExpression + { + Asc = true, + Expression = expression + }); + return this; + } + + public IDbQuery OrderByDescending(Expression> expression) + { + _orderExpressions.Add(new OrderExpression + { + Asc = false, + Expression = expression + }); + return this; + } + + public IDbQuery Filter(Expression> column) + { + _filterExpression = column; + return this; + } + + public IDbQuery Page(int index, int count, bool condition = true) + { + if (condition) + { + Skip((index - 1) * count, count); + } + return this; + } + + public IDbQuery With(string lockname) + { + _lockname = $" {lockname}"; + return this; + } + + public IEnumerable Select(int? commandTimeout = null) + { + var sql = ResolveSelect(); + return _context.ExecuteQuery(sql, _parameters, commandTimeout); + } + + public (IEnumerable, int) SelectMany(int? commandTimeout = null) + { + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = multi.GetList(); + var count = multi.Get(); + return (list, count); + } + } + public TResult Sum(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResovleSum(); + return _context.ExecuteScalar(sql, _parameters, commandTimeout); + } + public IEnumerable Select(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResolveSelect(); + return _context.ExecuteQuery(sql, _parameters, commandTimeout); + } + + public (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = multi.GetList(); + var count = multi.Get(); + return (list, count); + } + } + + public T Single(int? commandTimeout = null) + { + Take(1); + return Select(commandTimeout).FirstOrDefault(); + } + + public TResult Single(Expression> expression, int? commandTimeout = null) + { + Take(1); + return Select(expression, commandTimeout).FirstOrDefault(); + } + + public IDbQuery Skip(int index, int count, bool condition = true) + { + if (condition) + { + _page.Index = index; + _page.Count = count; + } + return this; + } + + public IDbQuery Take(int count,bool condition=true) + { + if (condition) + { + Skip(0, count); + } + return this; + } + + public IDbQuery Where(Expression> expression, bool condition = true) + { + if (condition) + { + _whereExpressions.Add(expression); + } + return this; + } + + #endregion + } +} diff --git a/src/Dapper.Common/Queryables/IDbQuery.cs b/src/Dapper.Common/Queryables/IDbQuery.cs new file mode 100644 index 0000000..3d10a12 --- /dev/null +++ b/src/Dapper.Common/Queryables/IDbQuery.cs @@ -0,0 +1,354 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Dapper +{ + /// + /// linq 查询 + /// + /// + public interface IDbQuery + { + /// + /// 通过主键检索数据 + /// + /// + /// + T Get(object id); + /// + /// 异步通过主键检索数据 + /// + /// + /// + Task GetAsync(object id); + /// + /// count查询 + /// + /// 超时时间 + /// + int Count(int? commandTimeout = null); + /// + /// 异步count查询 + /// + /// 超时时间 + /// + Task CountAsync(int? commandTimeout = null); + /// + /// count查询 + /// + /// 类型推断 + /// 字段列表 + /// + int Count(Expression> expression); + /// + /// 异步count查询 + /// + /// 类型推断 + /// 字段列表 + /// + Task CountAsync(Expression> expression); + /// + /// delete查询 + /// + /// + /// + int Delete(int? commandTimeout = null); + /// + /// 异步delete查询 + /// + /// + /// + Task DeleteAsync(int? commandTimeout = null); + /// + /// delete查询 + /// + /// 查询条件 + /// + int Delete(Expression> expression); + /// + /// 异步delete查询 + /// + /// 查询条件 + /// + Task DeleteAsync(Expression> expression); + /// + /// exists查询 + /// + /// 超时时间 + /// + bool Exists(int? commandTimeout = null); + /// + /// 异步exists查询 + /// + /// 超时时间 + /// + Task ExistsAsync(int? commandTimeout = null); + /// + /// exists查询 + /// + /// 查询条件 + /// + bool Exists(Expression> expression); + /// + /// 异步exists查询 + /// + /// 查询条件 + /// + Task ExistsAsync(Expression> expression); + /// + /// update查询,如果没有指定where则应用到所有记录 + /// + /// 超时时间 + /// + int Update(int? commandTimeout = null); + /// + /// 异步update查询,如果没有指定where则应用到所有记录 + /// + /// 超时时间 + /// + Task UpdateAsync(int? commandTimeout = null); + /// + /// update查询,默认根据Primarkey更新,如果存在where则仅使用指定更新条件, + /// 无法通过该接口更新主键字段和主键字段 + /// + /// 参数 + /// + int Update(T entity); + /// + /// 异步update查询,默认根据Primarkey更新,如果存在where则仅使用指定更新条件, + /// 无法通过该接口更新主键字段和主键字段 + /// + /// 参数 + /// + Task UpdateAsync(T entity); + /// + /// insert查询,该接口会忽略identity字段 + /// + /// 参数 + /// + int Insert(T entity); + /// + /// 异步insert查询,该接口会忽略identity字段 + /// + /// 参数 + /// + Task InsertAsync(T entity); + /// + /// insert查询,并返回id,该接口会忽略identity字段 + /// + /// 参数 + /// + int InsertReturnId(T entity); + /// + /// 异步insert查询,并返回id,该接口会忽略identity字段 + /// + /// + /// + Task InsertReturnIdAsync(T entity); + /// + /// 批量insert查询,该接口会忽略identity字段 + /// + /// 参数集合 + /// 超时时间 + /// + int Insert(IEnumerable entitys,int? commandTimeout = null); + /// + /// 异步批量insert查询,该接口会忽略identity字段 + /// + /// + /// 超时时间 + /// + Task InsertAsync(IEnumerable entitys, int? commandTimeout = null); + /// + /// select查询 + /// + /// 超时时间 + /// + IEnumerable Select(int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 超时时间 + /// + Task> SelectAsync(int? commandTimeout = null); + /// + /// 分页select查询 + /// + /// 超时时间 + /// 结果集,总记录数 + (IEnumerable, int) SelectMany(int? commandTimeout = null); + /// + /// 异步分页select查询 + /// + /// 超时时间 + /// + Task<(IEnumerable, int)> SelectManyAsync(int? commandTimeout = null); + /// + /// select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + IEnumerable Select(Expression> expression, int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task> SelectAsync(Expression> expression, int? commandTimeout = null); + /// + /// 分页select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null); + /// + /// 异步分页select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null); + /// + /// select查询 + /// + /// 超时时间 + /// + T Single(int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 超时时间 + /// + Task SingleAsync(int? commandTimeout = null); + /// + /// select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + TResult Single(Expression> expression, int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task SingleAsync(Expression> expression, int? commandTimeout = null); + /// + /// 在insert,update,select时过滤字段 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery Filter(Expression> expression); + /// + /// set查询 + /// + /// 类型推断 + /// 字段 + /// 参数 + /// 是否有效 + /// + IDbQuery Set(Expression> column, TResult value, bool condition = true); + /// + /// set查询 + /// + /// 类型推断 + /// 字段 + /// 表达式 + /// 是否有效 + /// + IDbQuery Set(Expression> column, Expression> expression, bool condition = true); + /// + /// take查询,从下标为0的行获取count条记录 + /// + /// 记录个数 + /// 条件 + /// + IDbQuery Take(int count, bool condition = true); + /// + /// skip,从下标为index的行获取count条记录 + /// + /// 起始下标 + /// 记录个数 + /// 条件 + /// + IDbQuery Skip(int index, int count, bool condition = true); + /// + /// page查询,从下标为(index-1)*count的行获取count条记录 + /// + /// 起始页码 + /// 记录个数 + /// 条件 + /// + IDbQuery Page(int index, int count, bool condition = true); + /// + /// 指定读锁 + /// + /// + /// + IDbQuery With(string lockname); + /// + /// where查询,多个where有效使用and连接 + /// + /// 表达式 + /// 是否有效 + /// + IDbQuery Where(Expression> expression, bool condition = true); + /// + /// having查询,多个having查询有效使用and连接 + /// + /// 表达式 + /// 是否有效 + /// + IDbQuery Having(Expression> expression, bool condition = true); + /// + /// group查询 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery GroupBy(Expression> expression); + /// + /// orderby查询,升序 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery OrderBy(Expression> expression); + /// + /// orderby查询,降序 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery OrderByDescending(Expression> expression); + /// + /// 求和 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + TResult Sum(Expression> expression, int? commandTimeout = null); + /// + /// 异步求和 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task SumAsync(Expression> expression, int? commandTimeout = null); + } +} diff --git a/src/Dapper.Common/Queryables/Operator.cs b/src/Dapper.Common/Queryables/Operator.cs new file mode 100644 index 0000000..a45fcb5 --- /dev/null +++ b/src/Dapper.Common/Queryables/Operator.cs @@ -0,0 +1,201 @@ +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Dapper +{ + /// + /// 数据库操作符 + /// + public class Operator + { + /// + /// in + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool In(T column, IEnumerable values) => default; + /// + /// in(低性能) + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool In(T column, params T[] values) => default; + /// + /// not in + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotIn(T column, IEnumerable values) => default; + /// + /// not in(低性能) + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotIn(T column, params T[] values) => default; + /// + /// like %value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool Contains(string column, string value) => default; + /// + /// not like %value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotContains(string column, string value) => default; + /// + /// like value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool StartsWith(string column, string value) => default; + /// + /// not like value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotStartsWith(string column, string value) => default; + /// + /// like %value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool EndsWith(string column, string value) => default; + /// + /// not like %value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotEndsWith(string column, string value) => default; + /// + /// regex value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool Regexp(string column, string value) => default; + /// + /// not regex value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotRegexp(string column, string value) => default; + /// + /// 解析表达式 + /// + /// + /// + internal static string ResovleExpressionType(ExpressionType type) + { + var condition = string.Empty; + switch (type) + { + case ExpressionType.Add: + condition = "+"; + break; + case ExpressionType.Subtract: + condition = "-"; + break; + case ExpressionType.Multiply: + condition = "*"; + break; + case ExpressionType.Divide: + condition = "/"; + break; + case ExpressionType.Modulo: + condition = "%"; + break; + case ExpressionType.Equal: + condition = "="; + break; + case ExpressionType.NotEqual: + condition = "<>"; + break; + case ExpressionType.GreaterThan: + condition = ">"; + break; + case ExpressionType.GreaterThanOrEqual: + condition = ">="; + break; + case ExpressionType.LessThan: + condition = "<"; + break; + case ExpressionType.LessThanOrEqual: + condition = "<="; + break; + case ExpressionType.OrElse: + condition = "OR"; + break; + case ExpressionType.AndAlso: + condition = "AND"; + break; + case ExpressionType.Not: + condition = "NOT"; + break; + } + return condition; + } + /// + /// 解析表达式 + /// + /// + /// + internal static string ResovleExpressionType(string type) + { + switch (type) + { + case nameof(Operator.In): + type = "IN"; + break; + case nameof(Operator.NotIn): + type = "NOT IN"; + break; + case nameof(Operator.Contains): + case nameof(Operator.StartsWith): + case nameof(Operator.EndsWith): + type = "LIKE"; + break; + case nameof(Operator.NotContains): + case nameof(Operator.NotStartsWith): + case nameof(Operator.NotEndsWith): + type = "NOT LIKE"; + break; + case nameof(Operator.Regexp): + type = "REGEXP"; + break; + case nameof(Operator.NotRegexp): + type = "NOT REGEXP"; + break; + } + return type; + } + } +} diff --git a/src/Dapper.Common/XmlResovles/Nodes/CommandNode.cs b/src/Dapper.Common/XmlResovles/Nodes/CommandNode.cs new file mode 100644 index 0000000..bd5d6c3 --- /dev/null +++ b/src/Dapper.Common/XmlResovles/Nodes/CommandNode.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Dapper.Expressions; + +namespace Dapper.XmlResovles +{ + internal class CommandNode : INode + { + public List Nodes { get; set; } = new List(); + + private string ResolveTextNode(TextNode node) + { + return node.Value; + } + + private string ResolveIfNode(IfNode node, T parameter) + { + if (node.Delegate == null) + { + lock (this) + { + var context = new ExpressionActivator(); + var result = context.Create(node.Test); + node.Delegate = result.Func; + } + } + var func = node.Delegate as Func; + if (func(parameter)) + { + return ResolveTextNode(new TextNode { Value = node.Value }); + } + return string.Empty; + } + + private string ResolveWhereNode(WhereNode node, T parameter) where T :class + { + var buffer = new StringBuilder(); + foreach (var item in node.Nodes) + { + if (parameter!=default && item is IfNode) + { + var text = ResolveIfNode(item as IfNode, parameter); + buffer.Append($"{text} "); + } + else if (item is TextNode) + { + var text = ResolveTextNode(item as TextNode); + buffer.Append($"{text} "); + } + } + var sql = buffer.ToString().Trim(' '); + if (sql.StartsWith("and", StringComparison.OrdinalIgnoreCase)) + { + sql = sql.Remove(0, 3); + } + else if (sql.StartsWith("or", StringComparison.OrdinalIgnoreCase)) + { + sql = sql.Remove(0, 2); + } + return sql.Length > 0 ? "WHERE " + sql : string.Empty; + } + + public string Resolve(CommandNode command, T parameter) where T : class + { + var buffer = new StringBuilder(); + foreach (var item in command.Nodes) + { + if (item is TextNode) + { + var txt = ResolveTextNode(item as TextNode); + if (txt.Length > 0) + { + buffer.AppendFormat($" {txt}"); + } + } + else if (item is WhereNode) + { + var wheresql = ResolveWhereNode(item as WhereNode, parameter); + if (wheresql.Length > 0) + { + buffer.AppendFormat($" {wheresql}"); + } + } + else if (item is IfNode) + { + var txt = ResolveIfNode(item as IfNode, parameter); + if (txt.Length > 0) + { + buffer.AppendFormat($" {txt}"); + } + } + } + return buffer.ToString().Trim(' '); + } + + } +} diff --git a/src/Dapper.Common/XmlResovles/Nodes/INode.cs b/src/Dapper.Common/XmlResovles/Nodes/INode.cs new file mode 100644 index 0000000..e77516e --- /dev/null +++ b/src/Dapper.Common/XmlResovles/Nodes/INode.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.XmlResovles +{ + interface INode + { + + } +} diff --git a/src/Dapper.Common/XmlResovles/Nodes/IfNode.cs b/src/Dapper.Common/XmlResovles/Nodes/IfNode.cs new file mode 100644 index 0000000..dc45def --- /dev/null +++ b/src/Dapper.Common/XmlResovles/Nodes/IfNode.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.XmlResovles +{ + internal class IfNode : INode + { + public Delegate Delegate { get; set; } + public string Test { get; set; } + public string Value { get; set; } + } +} diff --git a/src/Dapper.Common/XmlResovles/Nodes/TextNode.cs b/src/Dapper.Common/XmlResovles/Nodes/TextNode.cs new file mode 100644 index 0000000..c35b324 --- /dev/null +++ b/src/Dapper.Common/XmlResovles/Nodes/TextNode.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Dapper.XmlResovles +{ + internal class TextNode : INode + { + public string Value { get; set; } + + } +} diff --git a/src/Dapper.Common/XmlResovles/Nodes/WhereNode.cs b/src/Dapper.Common/XmlResovles/Nodes/WhereNode.cs new file mode 100644 index 0000000..69ba7ac --- /dev/null +++ b/src/Dapper.Common/XmlResovles/Nodes/WhereNode.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.XmlResovles +{ + internal class WhereNode : INode + { + public List Nodes { get; set; } = new List(); + } +} diff --git a/src/Dapper.Common/XmlResovles/XmlQuery.cs b/src/Dapper.Common/XmlResovles/XmlQuery.cs new file mode 100644 index 0000000..34f8cd1 --- /dev/null +++ b/src/Dapper.Common/XmlResovles/XmlQuery.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; + +namespace Dapper +{ + /// + /// xml命令映射器 + /// + public interface IXmlQuery + { + /// + ///执行多结果集查询,返回IMultiResult + /// + /// 超时时间 + /// 命令类型 + /// + IMultiResult ExecuteMultiQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回dynamic类型的结果集 + /// + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回dynamic类型的结果集 + /// + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回受影响的行数 + /// + /// 超时时间 + /// 命令类型 + /// + int ExecuteNonQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回受影响的行数 + /// + /// 超时时间 + /// 命令类型 + /// + Task ExecuteNonQueryAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回指定类型的数据 + /// + /// 返回类型 + /// 超时时间 + /// 命令类型 + /// + T ExecuteScalar(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回指定类型的数据 + /// + /// + /// 超时时间 + /// 命令类型 + /// + Task ExecuteScalarAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 添加数据库参数 + /// + /// + /// + void AddDbParameter(string name, object value); + } + + /// + /// 实现xml命令映射器 + /// + internal class XmlQuery : IXmlQuery + { + private readonly string _sql = null; + + private Dictionary _parameters = null; + + private readonly IDbContext _mapper = null; + + public XmlQuery(IDbContext mapper, string sql, Dictionary parameters=null) + { + _mapper = mapper; + _sql = sql; + _parameters = parameters; + } + + public IMultiResult ExecuteMultiQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteMultiQuery(_sql, _parameters, commandTimeout,commandType); + } + + public int ExecuteNonQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteNonQuery(_sql, _parameters, commandTimeout, commandType); + } + + public Task ExecuteNonQueryAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteNonQueryAsync(_sql, _parameters, commandTimeout, commandType); + } + + public IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQuery(_sql, _parameters, commandTimeout, commandType); + } + + public IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQuery(_sql, _parameters, commandTimeout, commandType); + } + + public Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQueryAsync(_sql, _parameters, commandTimeout, commandType); + } + + public Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQueryAsync(_sql, _parameters, commandTimeout, commandType); + } + + public T ExecuteScalar(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteScalar(_sql, _parameters, commandTimeout, commandType); + } + + public Task ExecuteScalarAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteScalarAsync(_sql, _parameters, commandTimeout, commandType); + } + + public void AddDbParameter(string name,object value) + { + if (_parameters == null) + { + _parameters = new Dictionary(); + } + if (_parameters.ContainsKey(name)) + { + _parameters[name] = value; + } + else + { + _parameters.Add(name,value); + } + } + } +} diff --git a/src/Dapper.Common/XmlResovles/XmlResovle.cs b/src/Dapper.Common/XmlResovles/XmlResovle.cs new file mode 100644 index 0000000..ef53721 --- /dev/null +++ b/src/Dapper.Common/XmlResovles/XmlResovle.cs @@ -0,0 +1,240 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using Dapper.XmlResovles; + +namespace Dapper +{ + public interface IXmlResovle + { + /// + /// 解析动态sql + /// + /// + /// + /// + /// + string Resolve(string id, T parameter) where T : class; + /// + /// 解析sql + /// + /// + /// + string Resolve(string id); + /// + /// 加载配置文件 + /// + /// 文件名 + void Load(string filename); + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 文件通配符 + void Load(string path, string pattern); + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 文件通配符 + /// 查找选项 + void Load(string path, string pattern, SearchOption options); + } + + public class XmlResovle : IXmlResovle + { + private readonly Dictionary _commands + = new Dictionary(); + + private Dictionary ResolveVariables(XmlElement element) + { + var variables = new Dictionary(); + var elements = element.Cast() + .Where(a => a.Name == "var"); + foreach (XmlElement item in elements) + { + if (item.Name == "var") + { + var id = item.GetAttribute("id"); + var value = string.IsNullOrEmpty(item.InnerXml) + ? item.GetAttribute("value") : item.InnerXml; + variables.Add(id, value); + } + } + return variables; + } + + private string ReplaceVariable(Dictionary variables, string text) + { + var matches = Regex.Matches(text, @"\${(?.*?)}"); + foreach (Match item in matches) + { + var key = item.Groups["key"].Value; + if (variables.ContainsKey(key)) + { + var value = variables[key]; + text = text.Replace("${" + key + "}", value); + } + } + return Regex.Replace(text, @"\s+", " ").Trim(' '); + } + + private CommandNode ResolveCommand(XmlElement element) + { + var cmd = new CommandNode(); + foreach (XmlNode item in element.ChildNodes) + { + if (item.Name == "var" || item.NodeType == XmlNodeType.Comment) + { + continue; + } + if (item.NodeType == XmlNodeType.Text) + { + cmd.Nodes.Add(new TextNode + { + Value = item.Value + }); + } + else if (item.NodeType == XmlNodeType.Element && item.Name == "where") + { + var whereNode = new WhereNode(); + foreach (XmlNode iitem in item.ChildNodes) + { + if (iitem.NodeType == XmlNodeType.Text) + { + whereNode.Nodes.Add(new TextNode + { + Value = iitem.Value + }); + } + else if (iitem.NodeType == XmlNodeType.Element && iitem.Name == "if") + { + var test = iitem.Attributes["test"].Value; + var value = string.IsNullOrEmpty(iitem.InnerText) ? + (iitem.Attributes["value"]?.Value ?? string.Empty) : iitem.InnerText; + whereNode.Nodes.Add(new IfNode + { + Test = test, + Value = value + }); + } + } + cmd.Nodes.Add(whereNode); + } + else if (item.NodeType == XmlNodeType.Element && item.Name == "if") + { + var test = item.Attributes["test"].Value; + var value = string.IsNullOrEmpty(item.InnerText) ? + (item.Attributes["value"]?.Value ?? string.Empty) : item.InnerText; + cmd.Nodes.Add(new IfNode + { + Test = test, + Value = value + }); + } + } + return cmd; + } + + public string Resolve(string id, T parameter) where T : class + { + if (!_commands.ContainsKey(id)) + { + return null; + } + var cmd = _commands[id]; + return cmd.Resolve(cmd, parameter); + } + + public string Resolve(string id) + { + return Resolve(id, (object)null); + } + + /// + /// 加载配置文件 + /// + /// + public void Load(string filename) + { + lock (this) + { + XmlDocument document = new XmlDocument(); + document.Load(filename); + var @namespace = document.DocumentElement + .GetAttribute("namespace") ?? string.Empty; + //解析全局变量 + var globalVariables = ResolveVariables(document.DocumentElement); + //获取命令节点 + var elements = document.DocumentElement + .Cast() + .Where(a => a.Name != "var" && a is XmlElement); + foreach (XmlElement item in elements) + { + var id = item.GetAttribute("id"); + id = string.IsNullOrEmpty(@namespace) ? $"{id}" : $"{@namespace}.{id}"; + //解析局部变量 + var localVariables = ResolveVariables(item); + //合并局部和全局变量,局部变量可以覆盖全局变量 + var variables = new Dictionary(globalVariables); + foreach (var ariable in localVariables) + { + if (variables.ContainsKey(ariable.Key)) + { + variables[ariable.Key] = ariable.Value; + } + else + { + variables.Add(ariable.Key, ariable.Value); + } + } + //替换变量 + var xml = ReplaceVariable(variables, item.OuterXml); + var doc = new XmlDocument(); + doc.LoadXml(xml); + //通过变量解析命令 + var cmd = ResolveCommand(doc.DocumentElement); + if (_commands.ContainsKey(id)) + { + _commands[id] = cmd; + } + else + { + _commands.Add(id, cmd); + } + } + } + } + + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 通配符 + public void Load(string path, string pattern) + { + var files = System.IO.Directory.GetFiles(path, pattern); + foreach (var item in files) + { + Load(item); + } + } + + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 通配符 + /// 查找选项 + public void Load(string path, string pattern, SearchOption options) + { + var files = System.IO.Directory.GetFiles(path, pattern, options); + foreach (var item in files) + { + Load(item); + } + } + } +} diff --git a/src/Dapper.Linq.NUnitTest/AutoEntitys.cs b/src/Dapper.Linq.NUnitTest/AutoEntitys.cs new file mode 100644 index 0000000..67ad10d --- /dev/null +++ b/src/Dapper.Linq.NUnitTest/AutoEntitys.cs @@ -0,0 +1,42 @@ +using System; +using Dapper.Attributes; + +namespace Daper.Entitys +{ + + /// + /// + /// 更新时间:2020-05-09 17:04:41 + /// + [Table("student")] + public partial class Student + { + + /// + /// + /// + [Column("id")] + [PrimaryKey] +[Identity] + + public int? Id { get; set; } + + /// + /// + /// + [Column("stu_name")] + + public string StuName { get; set; } + + /// + /// + /// + [Column("create_time")] + [Default] + + public DateTime? CreateTime { get; set; } + } +} + + + diff --git a/src/Dapper.Linq.NUnitTest/AutoEntitys.tt b/src/Dapper.Linq.NUnitTest/AutoEntitys.tt new file mode 100644 index 0000000..27a120d --- /dev/null +++ b/src/Dapper.Linq.NUnitTest/AutoEntitys.tt @@ -0,0 +1,116 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ assembly name="System.Data" #> +<#@ assembly name="$(SolutionDir)\References\Dapper.dll" #> +<#@ assembly name="$(SolutionDir)\References\MySql.Data.dll" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Data" #> +<#@ import namespace="MySql.Data.MySqlClient" #> +<#@ import namespace="Dapper" #> +<#@ output extension=".cs" #> +<# + //数据库连接字符串 + var connectionString = "server=127.0.0.1;user id=root;password=1024;database=test;"; +#> +<# + var connection = new MySqlConnection(connectionString); + var tablesql = string.Format("SELECT TABLE_NAME as TableName,TABLE_TYPE as TableType,TABLE_COMMENT as TableComment from INFORmation_schema.TABLES WHERE TABLE_SCHEMA='{0}'", connection.Database); + var columnsql = string.Format("SELECT TABLE_NAME as TableName,COLUMN_NAME as ColumnName,COLUMN_COMMENT as ColumnComment,COLUMN_DEFAULT as ColumnDefault,IS_NULLABLE as IsNullable,DATA_TYPE as DataType,COLUMN_TYPE as ColumnType,COLUMN_KEY as ColumnKey,EXTRA as Extra FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='{0}' ORDER BY ORDINAL_POSITION", connection.Database); + var tables = connection.Query
(tablesql).ToList(); + var columns = connection.Query(columnsql).ToList(); + connection.Close(); +#> +using System; +using Dapper.Attributes; + +namespace Daper.Entitys +{ +<#foreach(var table in tables){#> + + /// + /// <#=table.TableComment#> + /// 更新时间:<#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> + /// + [Table("<#=table.TableName#>")] + public partial class <#=Utils.Pascal(table.TableName)#> + { + <#foreach(var column in columns.FindAll(f => f.TableName == table.TableName)){#> + /// + /// <#=column.ColumnComment#> + /// + [Column("<#= column.ColumnName #>")] + <#=column.ColumnKey == "PRI"?"[PrimaryKey]\r\n":""#><#=column.Extra == "auto_increment"?"[Identity]\r\n":"" #><#=column.ColumnDefault != null?"[Default]\r\n":"" #> + public <#= Utils.GetCSharpType(column.DataType) #> <#= Utils.Pascal(column.ColumnName) #> { get; set; } + <#}#>} +<#}#> +} + + + +<#+ + public class Table + { + public string TableName { get; set; } + public string TableType { get; set; } + public string TableComment { get; set; } + } + public class Column + { + public string TableName { get; set; } + public string ColumnName { get; set; } + public string ColumnComment { get; set; } + public string ColumnDefault { get; set; } + public string IsNullable { get; set; } + public string ColumnType { get; set; } + public string DataType { get; set; } + public string ColumnKey { get; set; } + public string Extra { get; set; } + } + public static class Utils + { + //字段类型映射 + public static string GetCSharpType(string columnType) + { + var type = "object"; + switch (columnType) + { + case "varchar": type = "string"; break; + case "text": type = "string"; break; + case "char": type = "string"; break; + case "bit": type = "bool?"; break; + case "tinyint": type = "int?"; break; + case "smallint": type = "int?"; break; + case "int": type = "int?"; break; + case "integer": type = "int?"; break; + case "bigint": type = "int?"; break; + case "mediumint": type = "int?"; break; + case "real": type = "float?"; break; + case "float": type = "float?"; break; + case "double": type = "double?"; break; + case "decimal": type = "decimal?"; break; + case "date": type = "DateTime?"; break; + case "datetime": type = "DateTime?"; break; + case "json": type = "System.Text.Json.JsonElement?"; break; + } + return type; + } + //Pacsl命名转换 + public static string Pascal(string name) + { + var list = new List(); + foreach (var item in name.Split('_')) + { + list.Add(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(item.ToLower())); + } + return string.Join("",list); + } + //Camel命名转换 + public static string Camel(string name) + { + name = Pascal(name); + return char.ToLower(name[0]) + name.Substring(1); + } + } +#> \ No newline at end of file diff --git a/src/Dapper.Linq.NUnitTest/Dapper.Linq.NUnitTest.csproj b/src/Dapper.Linq.NUnitTest/Dapper.Linq.NUnitTest.csproj new file mode 100644 index 0000000..7973b5c --- /dev/null +++ b/src/Dapper.Linq.NUnitTest/Dapper.Linq.NUnitTest.csproj @@ -0,0 +1,39 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + TextTemplatingFileGenerator + AutoEntitys.cs + + + + + + + + + + True + True + AutoEntitys.tt + + + + diff --git a/src/Dapper.Linq.NUnitTest/UnitTest1.cs b/src/Dapper.Linq.NUnitTest/UnitTest1.cs new file mode 100644 index 0000000..911ea2c --- /dev/null +++ b/src/Dapper.Linq.NUnitTest/UnitTest1.cs @@ -0,0 +1,55 @@ +using Daper.Entitys; +using NUnit.Framework; + +namespace Dapper.Linq.NUnitTest +{ + public class Tests + { + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + var db = new DbContext(new DbContextBuilder + { + Connection = new MySql.Data.MySqlClient.MySqlConnection("server=127.0.0.1;user id=root;password=1024;database=test;") + }); + db.Open(); + var student = db.From().Get(1); + var list = db.From() + .Where(a => a.Id > 1) + .Select(); + Assert.Pass(); + } + + public void Test2() + { + //һӦֻҪһʵ + var resovle = new XmlResovle(); + //ָxml· + resovle.Load(@"D:\Dapper.Linq\src\Dapper.Linq.NUnitTest\student.xml"); + var db = new DbContext(new DbContextBuilder + { + Connection = new MySql.Data.MySqlClient.MySqlConnection("server=127.0.0.1;user id=root;password=1024;database=test;") + }); + db.Open(); + var student = db.From().Get(1); + var list = db.From() + .Where(a => a.Id > 1) + .Select(); + //ڵײʽǻڱʽʵֵģIdAgeһ + //1.ΪnullΪʽId!=null,Id>0Idintͣ + //2.public + using (var multi = db.From("student.list", new { Id = (int?)null, Age = (int?)90 }).ExecuteMultiQuery()) + { + //ִеһsql + var list2 = multi.GetList(); + //ִеڶsql + var count = multi.Get(); + } + } + } +} \ No newline at end of file diff --git a/src/Dapper.Linq.NUnitTest/student.xml b/src/Dapper.Linq.NUnitTest/student.xml new file mode 100644 index 0000000..bc6f2c3 --- /dev/null +++ b/src/Dapper.Linq.NUnitTest/student.xml @@ -0,0 +1,35 @@ + + + + + + + \ No newline at end of file diff --git a/src/Dapper.Linq.sln b/src/Dapper.Linq.sln new file mode 100644 index 0000000..0444d20 --- /dev/null +++ b/src/Dapper.Linq.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Linq", "Dapper.Linq\Dapper.Linq.csproj", "{6AC69AD3-DA59-43B7-84CA-E18BF14C0BD7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapper.Common", "Dapper.Common\Dapper.Common.csproj", "{9B90659A-8991-45E5-9FBD-670DD6FD99A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dapper.Linq.NUnitTest", "Dapper.Linq.NUnitTest\Dapper.Linq.NUnitTest.csproj", "{68602072-9777-44F6-A187-F91DAECA4237}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6AC69AD3-DA59-43B7-84CA-E18BF14C0BD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AC69AD3-DA59-43B7-84CA-E18BF14C0BD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AC69AD3-DA59-43B7-84CA-E18BF14C0BD7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AC69AD3-DA59-43B7-84CA-E18BF14C0BD7}.Release|Any CPU.Build.0 = Release|Any CPU + {9B90659A-8991-45E5-9FBD-670DD6FD99A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B90659A-8991-45E5-9FBD-670DD6FD99A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B90659A-8991-45E5-9FBD-670DD6FD99A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B90659A-8991-45E5-9FBD-670DD6FD99A9}.Release|Any CPU.Build.0 = Release|Any CPU + {68602072-9777-44F6-A187-F91DAECA4237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68602072-9777-44F6-A187-F91DAECA4237}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68602072-9777-44F6-A187-F91DAECA4237}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68602072-9777-44F6-A187-F91DAECA4237}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {942A2E6F-7B96-4BDC-8CB8-3B7CE10950C4} + EndGlobalSection +EndGlobal diff --git a/src/Dapper.Linq/Attributes/ColumnAttribute.cs b/src/Dapper.Linq/Attributes/ColumnAttribute.cs new file mode 100644 index 0000000..4c631b2 --- /dev/null +++ b/src/Dapper.Linq/Attributes/ColumnAttribute.cs @@ -0,0 +1,21 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 字段映射 + /// + [AttributeUsage(AttributeTargets.Property)] + public class ColumnAttribute : Attribute + { + internal string Name { get; set; } + /// + /// 属性字段映射 + /// + /// 数据库字段名 + public ColumnAttribute(string name = null) + { + Name = name; + } + } +} diff --git a/src/Dapper.Linq/Attributes/ComplexTypeAttribute.cs b/src/Dapper.Linq/Attributes/ComplexTypeAttribute.cs new file mode 100644 index 0000000..557443c --- /dev/null +++ b/src/Dapper.Linq/Attributes/ComplexTypeAttribute.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.Attributes +{ + /// + /// 计算列,如果字段一个是计算列则新增和修改的时候不会处理 + /// + public class ComplexTypeAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/ConcurrencyCheckAttribute.cs b/src/Dapper.Linq/Attributes/ConcurrencyCheckAttribute.cs new file mode 100644 index 0000000..7eab1a9 --- /dev/null +++ b/src/Dapper.Linq/Attributes/ConcurrencyCheckAttribute.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.Attributes +{ + /// + /// 并发检查,如果字段属性是number类型则用时间戳,否则使用GUID + /// + public class ConcurrencyCheckAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/DefaultAttribute.cs b/src/Dapper.Linq/Attributes/DefaultAttribute.cs new file mode 100644 index 0000000..d0f7e7c --- /dev/null +++ b/src/Dapper.Linq/Attributes/DefaultAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 默认值约束 + /// + [AttributeUsage(AttributeTargets.Property)] + public class DefaultAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/FunctionAttribute.cs b/src/Dapper.Linq/Attributes/FunctionAttribute.cs new file mode 100644 index 0000000..ef6ffb5 --- /dev/null +++ b/src/Dapper.Linq/Attributes/FunctionAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 数据库函数标识 + /// + [AttributeUsage(AttributeTargets.Class)] + public class FunctionAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/IdentityAttribute.cs b/src/Dapper.Linq/Attributes/IdentityAttribute.cs new file mode 100644 index 0000000..2605c87 --- /dev/null +++ b/src/Dapper.Linq/Attributes/IdentityAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 自增列标识 + /// + [AttributeUsage(AttributeTargets.Property)] + public class IdentityAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/NotMappedAttribute.cs b/src/Dapper.Linq/Attributes/NotMappedAttribute.cs new file mode 100644 index 0000000..c0a85b4 --- /dev/null +++ b/src/Dapper.Linq/Attributes/NotMappedAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 忽略映射 + /// + [AttributeUsage(AttributeTargets.Property)] + public class NotMappedAttribute: Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/PrimaryKeyAttribute.cs b/src/Dapper.Linq/Attributes/PrimaryKeyAttribute.cs new file mode 100644 index 0000000..f9ed31c --- /dev/null +++ b/src/Dapper.Linq/Attributes/PrimaryKeyAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 主键约束 + /// + [AttributeUsage(AttributeTargets.Property)] + public class PrimaryKeyAttribute : Attribute + { + + } +} diff --git a/src/Dapper.Linq/Attributes/TableAttribute.cs b/src/Dapper.Linq/Attributes/TableAttribute.cs new file mode 100644 index 0000000..8631d9b --- /dev/null +++ b/src/Dapper.Linq/Attributes/TableAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Dapper.Attributes +{ + /// + /// 表名映射 + /// + [AttributeUsage(AttributeTargets.Class)] + public class TableAttribute : Attribute + { + internal string Name { get; set; } + public TableAttribute(string name = null) + { + Name = name; + } + } +} diff --git a/src/Dapper.Linq/Dapper.Linq.csproj b/src/Dapper.Linq/Dapper.Linq.csproj new file mode 100644 index 0000000..968bdc9 --- /dev/null +++ b/src/Dapper.Linq/Dapper.Linq.csproj @@ -0,0 +1,33 @@ + + + + net45;netstandard2.0 + $(NoWarn);1591 + chaeyeon + Apache-2.0 + A high performance Micro-ORM supporting SQL Server, MySQL, Sqlite, etc.. + https://github.com/1448376744/Dapper.Linq + https://github.com/1448376744/Dapper.Linq + GIT + true + true + chaeyeon + chaeyeon + Dapper.Linq + 3.0.0 + 3.0.0 + 3.0.0 + + + + + + + + + + + + + + diff --git a/src/Dapper.Linq/DbContexts/DbContext.cs b/src/Dapper.Linq/DbContexts/DbContext.cs new file mode 100644 index 0000000..f043db3 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/DbContext.cs @@ -0,0 +1,493 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Dapper +{ + /// + /// 数据库上下文 + /// + public interface IDbContext : IDisposable + { + /// + /// 数据库连接 + /// + IDbConnection Connection { get; } + /// + /// 数据库上下文类型 + /// + DbContextType DbContextType { get; } + /// + /// 获取一个xml执行器 + /// + /// 参数类型 + /// 命令id + /// 参数 + /// + IXmlQuery From(string id, T parameter) where T : class; + /// + /// 获取一个xml执行器 + /// + /// 命令id + /// + IXmlQuery From(string id); + /// + /// 获取一个linq执行器 + /// + /// + /// + IDbQuery From(); + /// + /// 开启事务会话 + /// + void BeginTransaction(); + /// + /// 开启事务会话 + /// + /// 事务隔离级别 + void BeginTransaction(IsolationLevel level); + /// + /// 关闭连接和事务 + /// + void Close(); + /// + /// 提交当前事务会话 + /// + void CommitTransaction(); + /// + /// 执行多结果集查询,返回IMultiResult + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + IMultiResult ExecuteMultiQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回dynamic类型的结果集 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回dynamic类型的结果集 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回受影响的行数 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + int ExecuteNonQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回受影响的行数 + /// + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task ExecuteNonQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回指定类型的数据 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + T ExecuteScalar(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回指定类型的数据 + /// + /// 返回类型 + /// sql命令 + /// 参数 + /// 超时时间 + /// 命令类型 + /// + Task ExecuteScalarAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null); + /// + /// 打开数据库连接 + /// + void Open(); + /// + /// 异步打开数据库连接 + /// + /// + Task OpenAsync(); + /// + /// 回滚当前事务会话 + /// + void RollbackTransaction(); + } + + public class DbContext : IDbContext + { + public DbContextState DbContextState = DbContextState.Closed; + + private readonly IXmlResovle _xmlResovle = null; + + private IDbTransaction _transaction = null; + + private readonly IEntityMapper _typeMapper = null; + + public IDbConnection Connection { get; } = null; + + public DbContextType DbContextType { get; } = DbContextType.Mysql; + + protected virtual void OnLogging(string message, IDataParameterCollection parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + + } + + protected virtual DbContextBuilder OnConfiguring(DbContextBuilder builder) + { + return builder; + } + + protected DbContext() + { + var builder = OnConfiguring(new DbContextBuilder()); + Connection = builder.Connection; + _xmlResovle = builder.XmlResovle; + _typeMapper = builder.TypeMapper ?? new EntityMapper(); + DbContextType = builder.DbContextType; + } + + public DbContext(DbContextBuilder builder) + { + Connection = builder.Connection; + _xmlResovle = builder.XmlResovle; + _typeMapper = builder.TypeMapper ?? new EntityMapper(); + DbContextType = builder.DbContextType; + } + public IXmlQuery From(string id, T parameter) where T : class + { + var sql = _xmlResovle.Resolve(id, parameter); + var deserializer = EmitConvert.GetDeserializer(typeof(T)); + var values = deserializer(parameter); + return new XmlQuery(this, sql, values); + } + public IXmlQuery From(string id) + { + var sql = _xmlResovle.Resolve(id); + return new XmlQuery(this, sql); + } + + public IDbQuery From() + { + return new DbQuery(this); + } + + public IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + var list = new List(); + using (var reader = cmd.ExecuteReader()) + { + var handler = EmitConvert.GetSerializer(); + while (reader.Read()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public async Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + using (var reader = await cmd.ExecuteReaderAsync()) + { + var list = new List(); + var handler = EmitConvert.GetSerializer(); + while (reader.Read()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public IMultiResult ExecuteMultiQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + var cmd = Connection.CreateCommand(); + Initialize(cmd, sql, parameter, commandTimeout, commandType); + return new MultiResult(cmd, _typeMapper); + } + + public IEnumerable ExecuteQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + var list = new List(); + Initialize(cmd, sql, parameter, commandTimeout, commandType); + using (var reader = cmd.ExecuteReader()) + { + var handler = EmitConvert.GetSerializer(_typeMapper, reader); + while (reader.Read()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public async Task> ExecuteQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + using (var reader = await cmd.ExecuteReaderAsync()) + { + var list = new List(); + var handler = EmitConvert.GetSerializer(_typeMapper, reader); + while (await reader.ReadAsync()) + { + list.Add(handler(reader)); + } + return list; + } + } + } + + public int ExecuteNonQuery(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + return cmd.ExecuteNonQuery(); + } + } + + public async Task ExecuteNonQueryAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + return await cmd.ExecuteNonQueryAsync(); + } + } + + public T ExecuteScalar(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = Connection.CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + var result = cmd.ExecuteScalar(); + if (result is DBNull || result == null) + { + return default; + } + return (T)Convert.ChangeType(result, typeof(T)); + } + } + + public async Task ExecuteScalarAsync(string sql, object parameter = null, int? commandTimeout = null, CommandType? commandType = null) + { + using (var cmd = (Connection as DbConnection).CreateCommand()) + { + Initialize(cmd, sql, parameter, commandTimeout, commandType); + var result = await cmd.ExecuteScalarAsync(); + if (result is DBNull || result == null) + { + return default; + } + return (T)Convert.ChangeType(result, typeof(T)); + } + } + + public void BeginTransaction() + { + _transaction = Connection.BeginTransaction(); + OnLogging("Begin transaction"); + } + + public void BeginTransaction(IsolationLevel level) + { + _transaction = Connection.BeginTransaction(level); + OnLogging("Begin transaction isolationLevel = " + level); + } + + public void Close() + { + _transaction?.Dispose(); + Connection?.Dispose(); + DbContextState = DbContextState.Closed; + OnLogging("Colsed connection"); + } + + public void CommitTransaction() + { + _transaction?.Commit(); + DbContextState = DbContextState.Commit; + OnLogging("Commit transaction"); + } + + public void Open() + { + Connection?.Open(); + DbContextState = DbContextState.Open; + OnLogging("Open connection"); + } + + public async Task OpenAsync() + { + await (Connection as DbConnection).OpenAsync(); + DbContextState = DbContextState.Open; + OnLogging("Open connection"); + } + + public void RollbackTransaction() + { + _transaction?.Rollback(); + DbContextState = DbContextState.Rollback; + OnLogging("rollback"); + + } + + private void Initialize(IDbCommand cmd, string sql, object parameter, int? commandTimeout = null, CommandType? commandType = null) + { + var dbParameters = new List(); + cmd.Transaction = _transaction; + cmd.CommandText = sql; + if (commandTimeout.HasValue) + { + cmd.CommandTimeout = commandTimeout.Value; + } + if (commandType.HasValue) + { + cmd.CommandType = commandType.Value; + } + if (parameter is IDbDataParameter) + { + dbParameters.Add(parameter as IDbDataParameter); + } + else if (parameter is IEnumerable parameters) + { + dbParameters.AddRange(parameters); + } + else if (parameter is Dictionary keyValues) + { + foreach (var item in keyValues) + { + var param = CreateParameter(cmd, item.Key, item.Value); + dbParameters.Add(param); + } + } + else if (parameter != null) + { + var handler = EmitConvert.GetDeserializer(parameter.GetType()); + var values = handler(parameter); + foreach (var item in values) + { + var param = CreateParameter(cmd, item.Key, item.Value); + dbParameters.Add(param); + } + } + if (dbParameters.Count > 0) + { + foreach (IDataParameter item in dbParameters) + { + if (item.Value == null) + { + item.Value = DBNull.Value; + } + var pattern = $@"in\s+([\@,\:,\?]?{item.ParameterName})"; + var options = RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline; + if (cmd.CommandText.IndexOf("in", StringComparison.OrdinalIgnoreCase) != -1 && Regex.IsMatch(cmd.CommandText, pattern, options)) + { + var name = Regex.Match(cmd.CommandText, pattern, options).Groups[1].Value; + var list = new List(); + if (item.Value is IEnumerable || item.Value is Array) + { + list = (item.Value as IEnumerable).Cast().Where(a => a != null && a != DBNull.Value).ToList(); + } + else + { + list.Add(item.Value); + } + if (list.Count() > 0) + { + cmd.CommandText = Regex.Replace(cmd.CommandText, name, $"({string.Join(",", list.Select(s => $"{name}{list.IndexOf(s)}"))})"); + foreach (var iitem in list) + { + var key = $"{item.ParameterName}{list.IndexOf(iitem)}"; + var param = CreateParameter(cmd, key, iitem); + cmd.Parameters.Add(param); + } + } + else + { + cmd.CommandText = Regex.Replace(cmd.CommandText, name, $"(SELECT 1 WHERE 1 = 0)"); + } + } + else + { + cmd.Parameters.Add(item); + } + } + } + OnLogging(cmd.CommandText, cmd.Parameters, commandTimeout, commandType); + } + + private IDbDataParameter CreateParameter(IDbCommand command, string name, object value) + { + var parameter = command.CreateParameter(); + parameter.ParameterName = name; + parameter.Value = value; + return parameter; + } + + public void Dispose() + { + _transaction?.Dispose(); + Connection?.Dispose(); + } + } +} diff --git a/src/Dapper.Linq/DbContexts/DbContextBuilder.cs b/src/Dapper.Linq/DbContexts/DbContextBuilder.cs new file mode 100644 index 0000000..2490c52 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/DbContextBuilder.cs @@ -0,0 +1,12 @@ +using System.Data; + +namespace Dapper +{ + public class DbContextBuilder + { + public IXmlResovle XmlResovle { get; set; } + public IDbConnection Connection { get; set; } + public DbContextType DbContextType { get; set; } + public IEntityMapper TypeMapper { get; set; } + } +} diff --git a/src/Dapper.Linq/DbContexts/DbContextState.cs b/src/Dapper.Linq/DbContexts/DbContextState.cs new file mode 100644 index 0000000..f475352 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/DbContextState.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper +{ + public enum DbContextState + { + Closed = 0, + Open = 1, + Commit = 2, + Rollback = 3, + } +} diff --git a/src/Dapper.Linq/DbContexts/DbContextType.cs b/src/Dapper.Linq/DbContexts/DbContextType.cs new file mode 100644 index 0000000..7498409 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/DbContextType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper +{ + public enum DbContextType + { + Mysql, + SqlServer, + Postgresql, + Oracle, + Sqlite, + } +} diff --git a/src/Dapper.Linq/DbContexts/EmitConvert.cs b/src/Dapper.Linq/DbContexts/EmitConvert.cs new file mode 100644 index 0000000..1f13006 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/EmitConvert.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace Dapper +{ + /// + /// DbColumn Information + /// + public class DbDataInfo + { + public string TypeName { get; set; } + public Type DataType { get; set; } + public string DataName { get; set; } + public int Ordinal { get; set; } + public DbDataInfo(int ordinal, string typeName, Type dataType, string dataName) + { + Ordinal = ordinal; + TypeName = typeName; + DataType = dataType; + DataName = dataName; + } + } + /// + /// IL Convert + /// + public class EmitConvert + { + private readonly static ConcurrentDictionary _serializers + = new ConcurrentDictionary(); + private readonly static ConcurrentDictionary>> _deserializers + = new ConcurrentDictionary>>(); + + private struct SerializerKey : IEquatable + { + private string[] Names { get; set; } + private Type Type { get; set; } + public override bool Equals(object obj) + { + return obj is SerializerKey && Equals((SerializerKey)obj); + } + public bool Equals(SerializerKey other) + { + if (Type != other.Type) + { + return false; + } + else if (Names == other.Names) + { + return true; + } + else if (Names.Length != other.Names.Length) + { + return false; + } + else + { + for (int i = 0; i < Names.Length; i++) + { + if (Names[i] != other.Names[i]) + { + return false; + } + } + return true; + } + } + public override int GetHashCode() + { + return Type.GetHashCode(); + } + public SerializerKey(Type type, string[] names) + { + Type = type; + Names = names; + } + } + /// + /// IDataRecord Converted to T + /// + public static Func GetSerializer(IEntityMapper mapper, IDataRecord record) + { + string[] names = new string[record.FieldCount]; + for (int i = 0; i < record.FieldCount; i++) + { + names[i] = record.GetName(i); + } + var key = new SerializerKey(typeof(T), names.Length == 1 ? null : names); + var handler = _serializers.GetOrAdd(key, k => + { + return CreateTypeSerializerHandler(mapper, record); + }); + return handler as Func; + } + /// + /// IDataRecord Converted to dynamic + /// + public static Func GetSerializer() + { + return (reader) => + { + dynamic obj = new System.Dynamic.ExpandoObject(); + var row = (IDictionary)obj; + for (int i = 0; i < reader.FieldCount; i++) + { + var name = reader.GetName(i); + row.Add(name, reader.GetValue(i)); + } + return row; + }; + } + /// + /// IDataRecord Converted to T + /// + public static Func GetSerializer(IDataRecord record) + { + return GetSerializer(new EntityMapper(), record); + } + /// + /// Object To Dictionary<tstring, object> + /// + public static Func> GetDeserializer(Type type) + { + if (type == typeof(Dictionary)) + { + return (object param) => param as Dictionary; + } + var handler = _deserializers.GetOrAdd(type, t => + { + return CreateTypeDeserializerHandler(type) as Func>; + }); + return handler; + } + private static Func> CreateTypeDeserializerHandler(Type type) + { + var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var methodName = $"{type.Name}Deserializer{Guid.NewGuid():N}"; + var dynamicMethod = new DynamicMethod(methodName, typeof(Dictionary), new Type[] { typeof(object) }, type, true); + var generator = dynamicMethod.GetILGenerator(); + LocalBuilder entityLocal1 = generator.DeclareLocal(typeof(Dictionary)); + LocalBuilder entityLocal2 = generator.DeclareLocal(type); + generator.Emit(OpCodes.Newobj, typeof(Dictionary).GetConstructor(Type.EmptyTypes)); + generator.Emit(OpCodes.Stloc, entityLocal1); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Castclass, type); + generator.Emit(OpCodes.Stloc, entityLocal2); + foreach (var item in properties) + { + generator.Emit(OpCodes.Ldloc, entityLocal1); + generator.Emit(OpCodes.Ldstr, item.Name); + generator.Emit(OpCodes.Ldloc, entityLocal2); + generator.Emit(OpCodes.Callvirt, item.GetGetMethod()); + if (item.PropertyType.IsValueType) + { + generator.Emit(OpCodes.Box, item.PropertyType); + } + var addMethod = typeof(Dictionary).GetMethod(nameof(Dictionary.Add), new Type[] { typeof(string), typeof(object) }); + generator.Emit(OpCodes.Callvirt, addMethod); + } + generator.Emit(OpCodes.Ldloc, entityLocal1); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func>)) as Func>; + } + + private static Func CreateTypeSerializerHandler(IEntityMapper mapper, IDataRecord record) + { + var type = typeof(T); + var methodName = $"{type.Name}Serializer{Guid.NewGuid():N}"; + var dynamicMethod = new DynamicMethod(methodName, type, new Type[] { typeof(IDataRecord) }, type, true); + var generator = dynamicMethod.GetILGenerator(); + LocalBuilder local = generator.DeclareLocal(type); + var dataInfos = new DbDataInfo[record.FieldCount]; + for (int i = 0; i < record.FieldCount; i++) + { + var dataname = record.GetName(i); + var datatype = record.GetFieldType(i); + var typename = record.GetDataTypeName(i); + dataInfos[i] = new DbDataInfo(i, typename, datatype, dataname); + } + /* + * T t; + * t = ConvertTo***(reader,i); + * return t; + */ + if (dataInfos.Length == 1 && (type.IsValueType || type == typeof(string) || type == typeof(object))) + { + var dataInfo = dataInfos.First(); + var convertMethod = mapper.FindConvertMethod(type, dataInfo.DataType); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, 0); + if (convertMethod.IsVirtual) + generator.Emit(OpCodes.Callvirt, convertMethod); + else + generator.Emit(OpCodes.Call, convertMethod); + if (type == typeof(object) && convertMethod.ReturnType.IsValueType) + { + generator.Emit(OpCodes.Box, convertMethod.ReturnType); + } + generator.Emit(OpCodes.Stloc, local); + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func)) as Func; + } + var constructor = mapper.FindConstructor(type); + /* + * T t; + * int a; + * int b; + * a = ConvertTo**(reader,i); + * b = ConvertTo***(reader,i); + * t = new T(a,b); + * return t; + */ + if (constructor.GetParameters().Length > 0) + { + var parameters = constructor.GetParameters(); + var locals = new LocalBuilder[parameters.Length]; + for (int i = 0; i < locals.Length; i++) + { + locals[i] = generator.DeclareLocal(parameters[i].ParameterType); + } + for (int i = 0; i < locals.Length; i++) + { + var item = mapper.FindConstructorParameter(dataInfos, parameters[i]); + if (item == null) + { + continue; + } + var convertMethod = mapper.FindConvertMethod(parameters[i].ParameterType, item.DataType); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, item.Ordinal); + if (convertMethod.IsVirtual) + generator.Emit(OpCodes.Callvirt, convertMethod); + else + generator.Emit(OpCodes.Call, convertMethod); + generator.Emit(OpCodes.Stloc, locals[i]); + } + for (int i = 0; i < locals.Length; i++) + { + generator.Emit(OpCodes.Ldloc, locals[i]); + } + generator.Emit(OpCodes.Newobj, constructor); + generator.Emit(OpCodes.Stloc, local); + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func)) as Func; + } + /* + * T t; + * t = new T(); + * t.A = ConvertTo**(reader,i); + * t.B = = ConvertTo***(reader,i); + * return t; + */ + else + { + var properties = type.GetProperties(); + generator.Emit(OpCodes.Newobj, constructor); + generator.Emit(OpCodes.Stloc, local); + foreach (var item in dataInfos) + { + var property = mapper.FindMember(properties, item) as PropertyInfo; + if (property == null) + { + continue; + } + var convertMethod = mapper.FindConvertMethod(property.PropertyType, item.DataType); + if (convertMethod == null) + { + continue; + } + int i = record.GetOrdinal(item.DataName); + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldc_I4, i); + if (convertMethod.IsVirtual) + generator.Emit(OpCodes.Callvirt, convertMethod); + else + generator.Emit(OpCodes.Call, convertMethod); + generator.Emit(OpCodes.Callvirt, property.GetSetMethod()); + } + generator.Emit(OpCodes.Ldloc, local); + generator.Emit(OpCodes.Ret); + return dynamicMethod.CreateDelegate(typeof(Func)) as Func; + } + } + } +} diff --git a/src/Dapper.Linq/DbContexts/EntityMapper.cs b/src/Dapper.Linq/DbContexts/EntityMapper.cs new file mode 100644 index 0000000..2761cc4 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/EntityMapper.cs @@ -0,0 +1,585 @@ +using System; +using System.Data; +using System.Linq; +using System.Reflection; + +namespace Dapper +{ + /// + /// TypeMapper Interface + /// + public interface IEntityMapper + { + MemberInfo FindMember(MemberInfo[] properties, DbDataInfo dataInfo); + MethodInfo FindConvertMethod(Type csharpType, Type dbType); + DbDataInfo FindConstructorParameter(DbDataInfo[] dataInfos, ParameterInfo parameterInfo); + ConstructorInfo FindConstructor(Type csharpType); + } + + /// + /// 返回数据记录到Csharp类型的策略 + /// + public class EntityMapper : IEntityMapper + { + public bool MatchNamesWithUnderscores { get; set; } + + /// + /// Find parametric constructors. + /// If there is no default constructor, the constructor with the most parameters is returned. + /// + public ConstructorInfo FindConstructor(Type csharpType) + { + var constructor = csharpType.GetConstructor(Type.EmptyTypes); + if (constructor == null) + { + var constructors = csharpType.GetConstructors(); + constructor = constructors.Where(a => a.GetParameters().Length == constructors.Max(s => s.GetParameters().Length)).FirstOrDefault(); + } + return constructor; + } + + /// + /// Returns field information based on parameter information + /// + public DbDataInfo FindConstructorParameter(DbDataInfo[] dataInfos, ParameterInfo parameterInfo) + { + foreach (var item in dataInfos) + { + if (item.DataName.Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase)) + { + return item; + } + else if (MatchNamesWithUnderscores && item.DataName.Replace("_", "").Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase)) + { + return item; + } + } + return null; + } + + /// + /// Returns attribute information based on field information + /// + public MemberInfo FindMember(MemberInfo[] properties, DbDataInfo dataInfo) + { + foreach (var item in properties) + { + if (item.Name.Equals(dataInfo.DataName, StringComparison.OrdinalIgnoreCase)) + { + return item; + } + else if (MatchNamesWithUnderscores && item.Name.Equals(dataInfo.DataName.Replace("_", ""), StringComparison.OrdinalIgnoreCase)) + { + return item; + } + } + return null; + } + + /// + /// Return type conversion function. + /// + public MethodInfo FindConvertMethod(Type csharpType, Type dbType) + { + if (GetUnderlyingType(dbType) == typeof(bool) || GetUnderlyingType(csharpType) == typeof(bool)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToBooleanMethod : DataConvertMethod.ToBooleanNullableMethod; + } + if (GetUnderlyingType(csharpType).IsEnum) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToEnumMethod.MakeGenericMethod(csharpType) : DataConvertMethod.ToEnumNullableMethod.MakeGenericMethod(GetUnderlyingType(csharpType)); + } + if (GetUnderlyingType(dbType) == typeof(char) || GetUnderlyingType(csharpType) == typeof(char)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToCharMethod : DataConvertMethod.ToCharNullableMethod; + } + if (csharpType == typeof(string)) + { + return DataConvertMethod.ToStringMethod; + } + if (GetUnderlyingType(dbType) == typeof(Guid) || GetUnderlyingType(csharpType) == typeof(Guid)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToGuidMethod : DataConvertMethod.ToGuidNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(DateTime) || GetUnderlyingType(csharpType) == typeof(DateTime)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToDateTimeMethod : DataConvertMethod.ToDateTimeNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(byte) || GetUnderlyingType(dbType) == typeof(sbyte) || GetUnderlyingType(csharpType) == typeof(byte) || GetUnderlyingType(csharpType) == typeof(sbyte)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToByteMethod : DataConvertMethod.ToByteNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(short) || GetUnderlyingType(dbType) == typeof(ushort) || GetUnderlyingType(csharpType) == typeof(short) || GetUnderlyingType(csharpType) == typeof(ushort)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToIn16Method : DataConvertMethod.ToIn16NullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(int) || GetUnderlyingType(dbType) == typeof(uint) || GetUnderlyingType(csharpType) == typeof(int) || GetUnderlyingType(csharpType) == typeof(uint)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToIn32Method : DataConvertMethod.ToIn32NullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(long) || GetUnderlyingType(dbType) == typeof(long) || GetUnderlyingType(csharpType) == typeof(long) || GetUnderlyingType(csharpType) == typeof(ulong)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToIn64Method : DataConvertMethod.ToIn64NullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(float) || GetUnderlyingType(csharpType) == typeof(float)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToFloatMethod : DataConvertMethod.ToFloatNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(double) || GetUnderlyingType(csharpType) == typeof(double)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToDoubleMethod : DataConvertMethod.ToDoubleNullableMethod; + } + if (GetUnderlyingType(dbType) == typeof(decimal) || GetUnderlyingType(csharpType) == typeof(decimal)) + { + return !IsNullableType(csharpType) ? DataConvertMethod.ToDecimalMethod : DataConvertMethod.ToDecimalNullableMethod; + } + return !IsNullableType(csharpType) ? DataConvertMethod.ToObjectMethod.MakeGenericMethod(csharpType) : DataConvertMethod.ToObjectNullableMethod.MakeGenericMethod(Nullable.GetUnderlyingType(GetUnderlyingType(csharpType))); + } + + private Type GetUnderlyingType(Type type) + { + var underlyingType = Nullable.GetUnderlyingType(type); + return underlyingType ?? type; + } + + private bool IsNullableType(Type type) + { + if (type.IsValueType && Nullable.GetUnderlyingType(type) == null) + { + return false; + } + return true; + } + + public EntityMapper(bool matchNamesWithUnderscores = false) + { + MatchNamesWithUnderscores = matchNamesWithUnderscores; + } + } + + /// + /// 数据库类型到Csharp类型转换器 + /// + static class DataConvertMethod + { + #region Method Field + public static MethodInfo ToObjectMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToObject)); + public static MethodInfo ToByteMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToByte)); + public static MethodInfo ToIn16Method = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt16)); + public static MethodInfo ToIn32Method = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt32)); + public static MethodInfo ToIn64Method = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt64)); + public static MethodInfo ToFloatMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToFloat)); + public static MethodInfo ToDoubleMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDouble)); + public static MethodInfo ToDecimalMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDecimal)); + public static MethodInfo ToBooleanMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToBoolean)); + public static MethodInfo ToCharMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToChar)); + public static MethodInfo ToStringMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToString)); + public static MethodInfo ToDateTimeMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDateTime)); + public static MethodInfo ToEnumMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToEnum)); + public static MethodInfo ToGuidMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToGuid)); + #endregion + + #region NullableMethod Field + public static MethodInfo ToObjectNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertObjectNullable)); + public static MethodInfo ToByteNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToByteNullable)); + public static MethodInfo ToIn16NullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt16Nullable)); + public static MethodInfo ToIn32NullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt32Nullable)); + public static MethodInfo ToIn64NullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToInt64Nullable)); + public static MethodInfo ToFloatNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToFloatNullable)); + public static MethodInfo ToDoubleNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDoubleNullable)); + public static MethodInfo ToBooleanNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToBooleanNullable)); + public static MethodInfo ToDecimalNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDecimalNullable)); + public static MethodInfo ToCharNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToCharNullable)); + public static MethodInfo ToDateTimeNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToDateTimeNullable)); + public static MethodInfo ToEnumNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToEnumNullable)); + public static MethodInfo ToGuidNullableMethod = typeof(DataConvertMethod).GetMethod(nameof(DataConvertMethod.ConvertToGuidNullable)); + #endregion + + #region Define Convert + public static T ConvertToObject(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var data = dr.GetValue(i); + return (T)Convert.ChangeType(data, typeof(T)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static byte ConvertToByte(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var result = dr.GetByte(i); + return result; + } + catch + { + throw ThrowException(dr, i); + } + } + + public static short ConvertToInt16(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(short)) + { + return dr.GetInt16(i); + } + return Convert.ToInt16(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static int ConvertToInt32(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(int)) + { + return dr.GetInt32(i); + } + return Convert.ToInt32(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static long ConvertToInt64(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(long)) + { + return dr.GetInt64(i); + } + return Convert.ToInt64(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static float ConvertToFloat(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(float)) + { + return dr.GetFloat(i); + } + return Convert.ToSingle(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static double ConvertToDouble(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(double)) + { + return dr.GetDouble(i); + } + return Convert.ToDouble(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static bool ConvertToBoolean(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + if (dr.GetFieldType(i) == typeof(bool)) + { + var result = dr.GetBoolean(i); + return result; + } + else if (int.TryParse(dr.GetValue(i).ToString(), out int value)) + { + return !(value == 0); + } + return Convert.ToBoolean(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static decimal ConvertToDecimal(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(decimal)) + { + return dr.GetDecimal(i); + } + return Convert.ToDecimal(dr.GetValue(i)); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static char ConvertToChar(this IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var result = dr.GetChar(i); + return result; + } + catch + { + throw ThrowException(dr, i); + } + } + + public static string ConvertToString(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(string)) + { + return dr.GetString(i); + } + var result = dr.GetValue(i); + return Convert.ToString(result); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static DateTime ConvertToDateTime(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + else if (dr.GetFieldType(i) == typeof(DateTime)) + { + return dr.GetDateTime(i); + } + return DateTime.Parse(dr.GetValue(i).ToString()); + } + catch + { + throw ThrowException(dr, i); + } + } + + public static T ConvertToEnum(IDataRecord dr, int i) where T : struct + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var value = dr.GetValue(i); + if (Enum.TryParse(value.ToString(), out T result)) return result; + return default; + } + catch + { + throw ThrowException(dr, i); + } + } + + public static Guid ConvertToGuid(IDataRecord dr, int i) + { + try + { + if (dr.IsDBNull(i)) + { + return default; + } + var result = dr.GetGuid(i); + return result; + } + catch + { + throw ThrowException(dr, i); + } + } + + private static Exception ThrowException(IDataRecord dr, int i) + { + var inner = new FormatException($"Column of {dr.GetName(i)} {dr.GetFieldType(i)} '{dr.GetValue(i)}' was not recognized as a valid {typeof(T).Name}."); + return new InvalidCastException($"Unable to cast object of type '{dr.GetFieldType(i).Name}' to type '{typeof(int).Name}'.", inner); + } + #endregion + + #region Define Nullable Convert + public static T ConvertObjectNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToObject(dr, i); + } + public static byte? ConvertToByteNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToByte(dr, i); + } + public static short? ConvertToInt16Nullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToInt16(dr, i); + } + public static int? ConvertToInt32Nullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToInt32(dr, i); + } + public static long? ConvertToInt64Nullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToInt64(dr, i); + } + public static float? ConvertToFloatNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToFloat(dr, i); + } + public static double? ConvertToDoubleNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToDouble(dr, i); + } + public static bool? ConvertToBooleanNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToBoolean(dr, i); + } + public static decimal? ConvertToDecimalNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToDecimal(dr, i); + } + public static char? ConvertToCharNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToChar(dr, i); + } + public static DateTime? ConvertToDateTimeNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToDateTime(dr, i); + } + public static T? ConvertToEnumNullable(IDataRecord dr, int i) where T : struct + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToEnum(dr, i); + } + public static Guid? ConvertToGuidNullable(IDataRecord dr, int i) + { + if (dr.IsDBNull(i)) + { + return default; + } + return ConvertToGuid(dr, i); + } + #endregion + } +} diff --git a/src/Dapper.Linq/DbContexts/MultiResult.cs b/src/Dapper.Linq/DbContexts/MultiResult.cs new file mode 100644 index 0000000..62e4425 --- /dev/null +++ b/src/Dapper.Linq/DbContexts/MultiResult.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; + +namespace Dapper +{ + public interface IMultiResult : IDisposable + { + /// + /// 返回当前dynamic类型结果集 + /// + /// + List GetList(); + /// + /// 异步返回当前dynamic类型结果集 + /// + /// + Task> GetListAsync(); + /// + /// 返回当前T结果集 + /// + /// 结果集类型 + /// + List GetList(); + /// + /// 异步返回当前T类型结果集 + /// + /// + /// + Task> GetListAsync(); + /// + /// 返回当前dynamic类型结果 + /// + /// + object Get(); + /// + /// 异步返回当前dynamic类型结果 + /// + /// + Task GetAsync(); + /// + /// 返回当前T类型结果 + /// + /// 结果集类型 + /// + T Get(); + /// + /// 异步返回当前T类型结果 + /// + /// 结果集类型 + /// + Task GetAsync(); + } + + public class MultiResult : IMultiResult + { + private readonly IDataReader _reader = null; + + private readonly IDbCommand _command = null; + + private readonly IEntityMapper _mapper = null; + + internal MultiResult(IDbCommand command, IEntityMapper mapper) + { + _command = command; + _reader = command.ExecuteReader(); + _mapper = mapper; + } + + public void Dispose() + { + _reader?.Dispose(); + _command?.Dispose(); + } + + public T Get() + { + return GetList().FirstOrDefault(); + } + + public async Task GetAsync() + { + return (await GetListAsync()).FirstOrDefault(); + } + + public object Get() + { + return GetList().FirstOrDefault(); + } + + public async Task GetAsync() + { + return (await GetListAsync()).FirstOrDefault(); + } + + public async Task> GetListAsync() + { + var handler = EmitConvert.GetSerializer(); + var list = new List(); + while (await (_reader as DbDataReader).ReadAsync()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + + public List GetList() + { + var handler = EmitConvert.GetSerializer(); + var list = new List(); + while (_reader.Read()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + + public List GetList() + { + var handler = EmitConvert.GetSerializer(_mapper, _reader); + var list = new List(); + while (_reader.Read()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + + public async Task> GetListAsync() + { + var handler = EmitConvert.GetSerializer(_mapper, _reader); + var list = new List(); + while (await (_reader as DbDataReader).ReadAsync()) + { + list.Add(handler(_reader)); + } + _reader.NextResult(); + return list; + } + } +} diff --git a/src/Dapper.Linq/Exceptions/DbUpdateConcurrencyException.cs b/src/Dapper.Linq/Exceptions/DbUpdateConcurrencyException.cs new file mode 100644 index 0000000..9ebd516 --- /dev/null +++ b/src/Dapper.Linq/Exceptions/DbUpdateConcurrencyException.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.Expressions +{ + /// + /// 修改数据时并发冲突 + /// + public class DbUpdateConcurrencyException : Exception + { + public DbUpdateConcurrencyException(string message) + : base(message) + { + + } + } +} diff --git a/src/Dapper.Linq/Expressions/BooleanExpressionResovle.cs b/src/Dapper.Linq/Expressions/BooleanExpressionResovle.cs new file mode 100644 index 0000000..c1f065f --- /dev/null +++ b/src/Dapper.Linq/Expressions/BooleanExpressionResovle.cs @@ -0,0 +1,228 @@ +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Dapper.Attributes; + +namespace Dapper.Expressions +{ + public class BooleanExpressionResovle : ExpressionResovle + { + private readonly string _prefix = "@"; + + private bool _isNotExpression = false; + + private readonly Dictionary _parameters = new Dictionary(); + + public BooleanExpressionResovle(Expression expression) + : base(expression) + { + _parameters = new Dictionary(); + } + + public BooleanExpressionResovle(Expression expression, Dictionary parameters) + : base(expression) + { + _parameters = parameters; + } + + protected override Expression VisitMember(MemberExpression node) + { + if (IsParameterExpression(node)) + { + SetParameterName(node); + } + else + { + SetParameterValue(node); + } + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.DeclaringType == typeof(Operator)) + { + if (node.Arguments.Count == 2) + { + _textBuilder.Append("("); + SetParameterName(node.Arguments[0] as MemberExpression); + var type = Operator.ResovleExpressionType(node.Method.Name); + _textBuilder.Append($" {type} "); + var value = VisitConstantValue(node.Arguments[1]); + if (node.Method.Name == nameof(Operator.StartsWith) || node.Method.Name == nameof(Operator.NotStartsWith)) + { + SetParameterValue(Expression.Constant($"{value}%", typeof(string))); + } + else if (node.Method.Name == nameof(Operator.EndsWith) || node.Method.Name == nameof(Operator.NotEndsWith)) + { + SetParameterValue(Expression.Constant($"%{value}", typeof(string))); + } + else if (node.Method.Name == nameof(Operator.Contains) || node.Method.Name == nameof(Operator.NotContains)) + { + SetParameterValue(Expression.Constant($"%{value}%", typeof(string))); + } + else + { + SetParameterValue(Expression.Constant(value)); + } + _textBuilder.Append(")"); + } + } + else if (IsLikeExpression(node)) + { + _textBuilder.Append("("); + object value = null; + if (IsParameterExpression(node.Object)) + { + SetParameterName(node.Object as MemberExpression); + value = VisitConstantValue(node.Arguments[0]); + } + else + { + SetParameterName(node.Arguments[0] as MemberExpression); + value = VisitConstantValue(node.Object); + } + if (_isNotExpression) + { + _isNotExpression = false; + _textBuilder.Append(" NOT LIKE "); + } + else + { + _textBuilder.Append(" LIKE "); + } + if (node.Method.Name == nameof(string.Contains)) + { + SetParameterValue(Expression.Constant($"%{value}%")); + } + else if (node.Method.Name == nameof(string.StartsWith)) + { + SetParameterValue(Expression.Constant($"{value}%")); + } + else + { + SetParameterValue(Expression.Constant($"%{value}")); + } + _textBuilder.Append(")"); + } + else if (IsInExpression(node)) + { + _textBuilder.Append("("); + SetParameterName(node.Arguments[1] as MemberExpression); + if (_isNotExpression) + { + _isNotExpression = false; + _textBuilder.Append(" NOT IN "); + } + else + { + _textBuilder.Append(" IN "); + } + SetParameterValue(node.Arguments[0] as MemberExpression); + _textBuilder.Append(")"); + } + else if (node.Method.DeclaringType.GetCustomAttribute(typeof(FunctionAttribute), true) != null) + { + var function = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append(function); + } + else + { + SetParameterValue(node); + } + return node; + } + + protected override Expression VisitBinary(BinaryExpression node) + { + _textBuilder.Append("("); + Visit(node.Left); + if (node.Right is ConstantExpression right && right.Value == null && (node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual)) + { + _textBuilder.AppendFormat(" {0}", node.NodeType == ExpressionType.Equal ? "IS NULL" : "IS NOT NULL"); + } + else + { + _textBuilder.Append($" {Operator.ResovleExpressionType(node.NodeType)} "); + Visit(node.Right); + } + _textBuilder.Append(")"); + return node; + } + + protected override Expression VisitNew(NewExpression node) + { + SetParameterValue(node); + return node; + } + + protected override Expression VisitUnary(UnaryExpression node) + { + if (node.NodeType == ExpressionType.Not) + { + if (node.Operand is MethodCallExpression methodCallExpression + && (IsInExpression(methodCallExpression) || IsLikeExpression(methodCallExpression))) + { + _isNotExpression = true; + } + else + { + _textBuilder.AppendFormat("{0} ", Operator.ResovleExpressionType(ExpressionType.Not)); + } + Visit(node.Operand); + } + else + { + Visit(node.Operand); + } + return node; + } + + protected override Expression VisitConstant(ConstantExpression node) + { + SetParameterValue(node); + return node; + } + + private void SetParameterName(MemberExpression expression) + { + var name = GetColumnName(expression.Member.DeclaringType, expression.Member.Name); + _textBuilder.Append(name); + } + + private void SetParameterValue(Expression expression) + { + var value = VisitConstantValue(expression); + var parameterName = $"P_{_parameters.Count}"; + _parameters.Add(parameterName, value); + _textBuilder.Append($"{_prefix}{parameterName}"); + } + + private bool IsLikeExpression(MethodCallExpression node) + { + var array = new string[] + { + nameof(string.Contains), + nameof(string.StartsWith), + nameof(string.EndsWith) + }; + return node.Method.DeclaringType == typeof(string) + && array.Contains(node.Method.Name) + && node.Arguments.Count == 1; + } + + private bool IsParameterExpression(Expression expression) + { + return expression is MemberExpression memberExpression && + memberExpression.Expression?.NodeType == ExpressionType.Parameter; + } + + private bool IsInExpression(MethodCallExpression node) + { + return node.Method.DeclaringType == typeof(Enumerable) + && node.Method.Name == nameof(Enumerable.Contains) + && node.Arguments.Count == 2; + } + } +} diff --git a/src/Dapper.Linq/Expressions/ExpressionActivator.cs b/src/Dapper.Linq/Expressions/ExpressionActivator.cs new file mode 100644 index 0000000..71f23f1 --- /dev/null +++ b/src/Dapper.Linq/Expressions/ExpressionActivator.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text.RegularExpressions; + +namespace Dapper.Expressions +{ + /// + /// 表达式树生成器 + /// + public class ExpressionActivator + { + private Dictionary Factorization(string expression) + { + var experssions = new Dictionary(); + var pattern = @"\([^\(\)]+\)"; + var text = $"({expression})"; + while (Regex.IsMatch(text, pattern)) + { + var key = $"${experssions.Count}"; + var value = Regex.Match(text, pattern).Value; + experssions.Add(key, value); + text = text.Replace(value, key); + } + return experssions; + } + + private Expression CreateExpression(ParameterExpression parameter, string expression) + { + var expressions1 = Factorization(expression); + var expressions2 = new Dictionary(); + foreach (var item in expressions1) + { + var subexpr = item.Value.Trim('(', ')'); + var @opterator = ResovleOperator(item.Value); + var opt = GetExpressionType(@opterator); + if (opt == ExpressionType.Not) + { + Expression exp; + var text = subexpr.Split(new string[] { @opterator }, StringSplitOptions.RemoveEmptyEntries)[0].Trim(); + if (expressions2.ContainsKey(text)) + { + exp = expressions2[text]; + } + else if (parameter.Type.GetProperties().Any(a => a.Name == text)) + { + var property = parameter.Type.GetProperty(text); + exp = Expression.MakeMemberAccess(parameter, property); + } + else + { + exp = Expression.Constant(Convert.ToBoolean(text)); + } + expressions2.Add(item.Key, Expression.MakeUnary(opt, exp, null)); + } + else + { + var text1 = subexpr + .Split(new string[] { @opterator }, StringSplitOptions.RemoveEmptyEntries)[0] + .Trim(); + var text2 = subexpr + .Split(new string[] { @opterator }, StringSplitOptions.RemoveEmptyEntries)[1] + .Trim(); + string temp = null; + Expression exp1, exp2; + //永远将变量放在第一个操作数 + if (parameter.Type.GetProperties().Any(a => a.Name == text2)) + { + temp = text1; + text1 = text2; + text2 = temp; + } + //是否为上一次的分式 + if (expressions2.ContainsKey(text1)) + { + exp1 = expressions2[text1]; + } + else if (parameter.Type.GetProperties().Any(a => a.Name == text1)) + { + //是否为变量 + var property = parameter.Type.GetProperty(text1); + exp1 = Expression.MakeMemberAccess(parameter, property); + } + else + { + exp1 = ResovleConstantExpression(text1); + } + //是否为上一次的分式 + if (expressions2.ContainsKey(text2)) + { + exp2 = expressions2[text2]; + } + //如果第一个操作数是变量 + else if (parameter.Type.GetProperties().Any(a => a.Name == text1)) + { + var constantType = parameter.Type.GetProperty(text1).PropertyType; + exp2 = ResovleConstantExpression(text2, constantType); + } + else + { + exp2 = ResovleConstantExpression(text1, (exp1 as ConstantExpression)?.Type); + } + expressions2.Add(item.Key, Expression.MakeBinary(opt, exp1, exp2)); + } + } + return expressions2.Last().Value; + } + + public ExpressionContextResult Create(string expression) + { + expression = Initialization(expression); + var parameter = Expression.Parameter(typeof(T), "p"); + var body = CreateExpression(parameter, expression); + var lambda = Expression.Lambda(body, parameter); + var func = lambda.Compile() as Func; + return new ExpressionContextResult() + { + Func = func, + LambdaExpression = lambda + }; + } + + private string Initialization(string expression) + { + expression = Regex.Replace(expression, @"(?<=[^\'][\s]+)and(?=\s+[^\'])","&&",RegexOptions.IgnoreCase); + expression = Regex.Replace(expression, @"(?<=[^\'][\s]+)or(?=\s+[^\'])", "||", RegexOptions.IgnoreCase); + expression = Regex.Replace(expression, @"(?<=[^\'][\s]+)not(?=\s+[^\'])", "!", RegexOptions.IgnoreCase); + return expression; + } + + private ExpressionType GetExpressionType(string text) + { + switch (text) + { + case "+": return ExpressionType.Add; + case "-": return ExpressionType.Subtract; + case "*": return ExpressionType.Multiply; + case "/": return ExpressionType.Divide; + case "%": return ExpressionType.Modulo; + case "!": return ExpressionType.Not; + case ">": return ExpressionType.GreaterThan; + case "<": return ExpressionType.LessThan; + case ">=": return ExpressionType.GreaterThanOrEqual; + case "<=": return ExpressionType.LessThanOrEqual; + case "==": return ExpressionType.Equal; + case "!=": return ExpressionType.NotEqual; + case "&&": return ExpressionType.AndAlso; + case "||": return ExpressionType.OrElse; + default: + throw new InvalidOperationException(text); + } + } + + private Expression ResovleConstantExpression(string expression, Type type) + { + //生成指定类型的表达式 + if (expression == "null") + { + return Expression.Constant(null, type); + } + else if (type == typeof(string)) + { + return Expression.Constant(expression.Trim('\'', '\''), type); + } + else + { + if (Nullable.GetUnderlyingType(type) == null) + { + var value = Convert.ChangeType(expression, type); + return Expression.Constant(value, type); + } + else + { + var undertype = Nullable.GetUnderlyingType(type); + var value = Convert.ChangeType(expression, undertype); + var expr = Expression.Constant(value, undertype); + return Expression.MakeUnary(ExpressionType.Convert, expr, type); + } + } + } + + private Expression ResovleConstantExpression(string expression) + { + //自动类型推断生成表达式 + if (expression.StartsWith("'") && expression.EndsWith("'")) + { + //字符串常量 + return Expression.Constant(expression.Trim('\''), typeof(string)); + } + else if (expression == "true" || expression == "false") + { + return Expression.Constant(expression, typeof(bool)); + } + else if (Regex.IsMatch(expression, @"^\d+$")) + { + //int类型常量 + return Expression.Constant(expression, typeof(int)); + } + else if (Regex.IsMatch(expression, @"^\d*\.\d*$")) + { + //double + return Expression.Constant(expression, typeof(int)); + } + else if (expression == "null") + { + return Expression.Constant(null, typeof(object)); + } + return Expression.Constant(expression, typeof(object)); + } + + private string ResovleOperator(string text) + { + var operators = new string[] { "!", "*", "/", "%", "+", "-", "<", ">", "<=", ">=", "==", "!=", "&&", "||" }; + for (int i = 0; i < text.Length - 1; i++) + { + var opt1 = text[i].ToString(); + var opt2 = text.Substring(i, 2); + if (operators.Contains(opt2)) + { + return opt2; + } + else if(operators.Contains(opt1)) + { + return opt1; + } + } + throw new Exception("resolve operator eroor"); + } + } + + public class ExpressionContextResult + { + public Func Func { get; internal set; } + public Expression LambdaExpression { get; internal set; } + } +} diff --git a/src/Dapper.Linq/Expressions/ExpressionResovle.cs b/src/Dapper.Linq/Expressions/ExpressionResovle.cs new file mode 100644 index 0000000..cf0cf0a --- /dev/null +++ b/src/Dapper.Linq/Expressions/ExpressionResovle.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; + +namespace Dapper.Expressions +{ + public abstract class ExpressionResovle : ExpressionVisitor + { + protected ExpressionResovle(Expression expression) + { + _expression = expression; + } + + protected readonly Expression _expression = null; + + protected readonly StringBuilder _textBuilder = new StringBuilder(); + + /// + /// 解析表达式参数 + /// + /// + /// + public object VisitConstantValue(Expression expression) + { + var names = new Stack(); + var exps = new Stack(); + var mifs = new Stack(); + if (expression is ConstantExpression constant) + return constant.Value; + else if (expression is MemberExpression) + { + var temp = expression; + object value = null; + while (temp is MemberExpression memberExpression) + { + names.Push(memberExpression.Member.Name); + exps.Push(memberExpression.Expression); + mifs.Push(memberExpression.Member); + temp = memberExpression.Expression; + } + foreach (var name in names) + { + var exp = exps.Pop(); + var mif = mifs.Pop(); + if (exp is ConstantExpression cex) + value = cex.Value; + if (mif is PropertyInfo pif) + value = pif.GetValue(value); + else if (mif is FieldInfo fif) + value = fif.GetValue(value); + } + return value; + } + else + { + return Expression.Lambda(expression).Compile().DynamicInvoke(); + } + } + + /// + /// 获取字段名 + /// + /// + /// + /// + protected string GetColumnName(Type type,string csharpName) + { + var columns = DbMetaInfoCache.GetColumns(type); + return columns.Where(a => a.CsharpName == csharpName) + .FirstOrDefault().ColumnName; + } + + public virtual string Resovle() + { + Visit(_expression); + return _textBuilder.ToString(); + } + } +} diff --git a/src/Dapper.Linq/Expressions/FunctionExpressionResovle.cs b/src/Dapper.Linq/Expressions/FunctionExpressionResovle.cs new file mode 100644 index 0000000..36beb06 --- /dev/null +++ b/src/Dapper.Linq/Expressions/FunctionExpressionResovle.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; + +namespace Dapper.Expressions +{ + public class FunctionExpressionResovle : ExpressionResovle + { + public FunctionExpressionResovle(Expression expression) + : base(expression) + { + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + _textBuilder.Append(node.Method.Name.ToUpper()); + _textBuilder.Append("("); + for (var i = 0; i < node.Arguments.Count; i++) + { + var item = node.Arguments[i]; + Visit(item); + } + if (_textBuilder[_textBuilder.Length - 1] == ',') + { + _textBuilder.Remove(_textBuilder.Length - 1, 1); + } + _textBuilder.Append(")"); + return node; + } + + protected override Expression VisitConstant(ConstantExpression node) + { + var value = VisitConstantValue(node); + if (value == null) + { + value = "NULL"; + } + else if (value is string) + { + value = $"'{value}'"; + } + else if (value is bool) + { + value = Convert.ToBoolean(value) ? 1 : 0; + } + _textBuilder.Append($"{value},"); + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name},"); + return node; + } + + protected override Expression VisitNewArray(NewArrayExpression node) + { + foreach (var item in node.Expressions) + { + Visit(item); + } + return node; + } + + protected override Expression VisitUnary(UnaryExpression node) + { + if (node.Operand != null) + { + Visit(node.Operand); + } + return node; + } + + } +} diff --git a/src/Dapper.Linq/Expressions/GroupExpressionResovle.cs b/src/Dapper.Linq/Expressions/GroupExpressionResovle.cs new file mode 100644 index 0000000..bc11e73 --- /dev/null +++ b/src/Dapper.Linq/Expressions/GroupExpressionResovle.cs @@ -0,0 +1,40 @@ +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + public class GroupExpressionResovle : ExpressionResovle + { + public GroupExpressionResovle(Expression expression) + : base(expression) + { + } + + protected override Expression VisitNew(NewExpression node) + { + foreach (var item in node.Arguments) + { + Visit(item); + } + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name},"); + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var result = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append($"{result},"); + return node; + } + + public override string Resovle() + { + return base.Resovle().Trim(','); + } + } +} diff --git a/src/Dapper.Linq/Expressions/OrderExpressionResovle.cs b/src/Dapper.Linq/Expressions/OrderExpressionResovle.cs new file mode 100644 index 0000000..1e65d20 --- /dev/null +++ b/src/Dapper.Linq/Expressions/OrderExpressionResovle.cs @@ -0,0 +1,45 @@ +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + public class OrderExpressionResovle : ExpressionResovle + { + private readonly string _asc = string.Empty; + + public OrderExpressionResovle(Expression expression, bool asc) + : base(expression) + { + if (!asc) + { + _asc = " DESC"; + } + } + protected override Expression VisitNew(NewExpression node) + { + foreach (var item in node.Arguments) + { + Visit(item); + } + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var result = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append($"{result}{_asc},"); + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name}{_asc},"); + return node; + } + + public override string Resovle() + { + return base.Resovle().Trim(','); + } + } +} diff --git a/src/Dapper.Linq/Expressions/SelectExpressionResovle.cs b/src/Dapper.Linq/Expressions/SelectExpressionResovle.cs new file mode 100644 index 0000000..11ba643 --- /dev/null +++ b/src/Dapper.Linq/Expressions/SelectExpressionResovle.cs @@ -0,0 +1,83 @@ +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + public class SelectExpressionResovle : ExpressionResovle + { + public SelectExpressionResovle(Expression expression) + : base(expression) + { + + } + + protected override Expression VisitMemberInit(MemberInitExpression node) + { + for (int i = 0; i < node.Bindings.Count; i++) + { + var item = node.Bindings[i] as MemberAssignment; + + if (item.Expression is MemberExpression member) + { + var name = GetColumnName(member.Member.DeclaringType, member.Member.Name); + _textBuilder.Append($"{name} AS {item.Member.Name}"); + } + else if (item.Expression is MethodCallExpression) + { + var expression = new FunctionExpressionResovle(item.Expression).Resovle(); + _textBuilder.Append($"{expression} AS {item.Member.Name}"); + } + if (i != node.Bindings.Count - 1) + { + _textBuilder.Append(","); + } + } + return node; + } + + protected override Expression VisitNew(NewExpression node) + { + for (int i = 0; i < node.Arguments.Count; i++) + { + var item = node.Arguments[i]; + var column = node.Members[i].Name; + if (item is MemberExpression member) + { + var name = GetColumnName(member.Member.DeclaringType, member.Member.Name); + if (name != column) + { + _textBuilder.Append($"{name} AS {column}"); + } + else + { + _textBuilder.Append($"{name}"); + } + } + else if (item is MethodCallExpression) + { + var expression = new FunctionExpressionResovle(item).Resovle(); + _textBuilder.Append($"{expression} AS {column}"); + } + if (i != node.Arguments.Count - 1) + { + _textBuilder.Append(","); + } + } + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + var name = GetColumnName(node.Member.DeclaringType, node.Member.Name); + _textBuilder.Append($"{name}"); + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + var result = new FunctionExpressionResovle(node).Resovle(); + _textBuilder.Append($"{result} AS expr"); + return node; + } + + } +} diff --git a/src/Dapper.Linq/Queryables/DbMetaInfoCache.cs b/src/Dapper.Linq/Queryables/DbMetaInfoCache.cs new file mode 100644 index 0000000..8323a75 --- /dev/null +++ b/src/Dapper.Linq/Queryables/DbMetaInfoCache.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Text; +using System.Linq; +using Dapper.Attributes; +using System.Linq.Expressions; + +namespace Dapper.Expressions +{ + /// + /// 数据库元信息 + /// + public static class DbMetaInfoCache + { + private static readonly ConcurrentDictionary _tables + = new ConcurrentDictionary(); + + private static readonly ConcurrentDictionary> _columns + = new ConcurrentDictionary>(); + + public static TableInfo GetTable(Type type) + { + return _tables.GetOrAdd(type, t => + { + var name = t.Name; + if (t.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault() != null) + { + var attribute = t.GetCustomAttributes(typeof(TableAttribute), true) + .FirstOrDefault() as TableAttribute; + name = attribute.Name; + } + var table = new TableInfo() + { + TableName = name, + CsharpName = t.Name + }; + return table; + }); + } + + public static List GetColumns(Type type) + { + return _columns.GetOrAdd(type, t => + { + var list = new List(); + var properties = type.GetProperties(); + foreach (var item in properties) + { + var columnName = item.Name; + var isPrimaryKey = false; + var isDefault = false; + var isIdentity = false; + var isNotMapped = false; + var isConcurrencyCheck = false; + var isComplexType = false; + if (item.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault() != null) + { + var attribute = item.GetCustomAttributes(typeof(ColumnAttribute), true) + .FirstOrDefault() as ColumnAttribute; + columnName = attribute.Name; + } + if (item.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).FirstOrDefault() != null) + { + isPrimaryKey = true; + } + if (item.GetCustomAttributes(typeof(IdentityAttribute), true).FirstOrDefault() != null) + { + isIdentity = true; + } + if (item.GetCustomAttributes(typeof(DefaultAttribute), true).FirstOrDefault() != null) + { + isDefault = true; + } + if (item.GetCustomAttributes(typeof(ConcurrencyCheckAttribute), true).FirstOrDefault() != null) + { + isConcurrencyCheck = true; + } + if (item.GetCustomAttributes(typeof(NotMappedAttribute), true).FirstOrDefault() != null) + { + isNotMapped = true; + } + if (item.GetCustomAttributes(typeof(ComplexTypeAttribute), true).FirstOrDefault() != null) + { + isComplexType = true; + } + list.Add(new ColumnInfo() + { + CsharpType = item.PropertyType, + IsDefault = isDefault, + ColumnName = columnName, + CsharpName = item.Name, + IsPrimaryKey = isPrimaryKey, + IsIdentity = isIdentity, + IsNotMapped = isNotMapped, + IsConcurrencyCheck = isConcurrencyCheck, + IsComplexType = isComplexType + }); + } + return list; + }); + } + + } + + /// + /// 表信息 + /// + public class TableInfo + { + public string TableName { get; set; } + public string CsharpName { get; set; } + } + + /// + /// 字段信息 + /// + public class ColumnInfo + { + public bool IsConcurrencyCheck { get; set; } + public bool IsDefault { get; set; } + public bool IsNotMapped { get; set; } + public string ColumnName { get; set; } + public string CsharpName { get; set; } + public Type CsharpType { get; set; } + public bool IsPrimaryKey { get; set; } + public bool IsIdentity { get; set; } + public bool IsComplexType { get; set; } + } +} diff --git a/src/Dapper.Linq/Queryables/DbQuery.cs b/src/Dapper.Linq/Queryables/DbQuery.cs new file mode 100644 index 0000000..e5f6722 --- /dev/null +++ b/src/Dapper.Linq/Queryables/DbQuery.cs @@ -0,0 +1,433 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using Dapper.Expressions; + +namespace Dapper +{ + public partial class DbQuery : IDbQuery + { + #region fields + + private readonly Dictionary _parameters + = new Dictionary(); + + private readonly PageData _page = new PageData(); + + private string _lockname = string.Empty; + + private readonly IDbContext _context = null; + + private readonly List _whereExpressions = new List(); + + private readonly List _setExpressions = new List(); + + private readonly List _orderExpressions = new List(); + + private readonly List _groupExpressions = new List(); + + private readonly List _havingExpressions = new List(); + + private Expression _filterExpression = null; + + private Expression _selectExpression = null; + + private Expression _countExpression = null; + + public DbQuery(IDbContext context) + { + _context = context; + } + + #endregion + + #region resovles + private void ResovleParameter(T entity) + { + var serializer = EmitConvert.GetDeserializer(typeof(T)); + var values = serializer(entity); + foreach (var item in values) + { + if (_parameters.ContainsKey(item.Key)) + { + _parameters[item.Key] = item.Value; + } + else + { + _parameters.Add(item.Key, item.Value); + } + } + } + + private string ResovleCount() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var column = "COUNT(1)"; + var where = ResolveWhere(); + var group = ResolveGroup(); + if (group.Length > 0) + { + column = group.Remove(0, 10); + } + else if (_countExpression != null) + { + column = new SelectExpressionResovle(_countExpression).Resovle(); + } + var sql = $"SELECT {column} FROM {table}{where}{group}"; + if (group.Length > 0) + { + sql = $"SELECT COUNT(1) FROM ({sql}) as t"; + return sql; + } + return sql; + } + + private string ResovleSum() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var column = $"SUM({ResovleColumns()})"; + var where = ResolveWhere(); + var sql = $"SELECT {column} FROM {table}{where}"; + return sql; + } + + private string ResolveGet() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var columns = DbMetaInfoCache.GetColumns(typeof(T)); + var column = ResovleColumns(); + var where = $" WHERE {columns.Where(a => a.IsPrimaryKey == true).First().ColumnName}=@id"; + string sql; + if (_context.DbContextType == DbContextType.SqlServer) + { + sql = $"SELECT TOP 1 {column} FROM {table}{where}"; + } + else + { + sql = $"SELECT {column} FROM {table}{where} LIMIT 0,1"; + } + return sql; + } + + private string ResolveSelect() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var column = ResovleColumns(); + var where = ResolveWhere(); + var group = ResolveGroup(); + var having = ResolveHaving(); + var order = ResolveOrder(); + string sql; + if (_context.DbContextType == DbContextType.SqlServer) + { + if (_lockname != string.Empty) + { + _lockname = $" WITH({_lockname})"; + } + if (_page.Index == 0) + { + sql = $"SELECT TOP {_page.Count} {column} FROM {table}{_lockname}{where}{group}{having}{order}"; + } + else if (_page.Index > 0) + { + if (order == string.Empty) + { + order = " ORDER BY (SELECT 1)"; + } + var limit = $" OFFSET {_page.Index} ROWS FETCH NEXT {_page.Count} ROWS ONLY"; + sql = $"SELECT {column} FROM {_lockname}{table}{where}{group}{having}{order}{limit}"; + } + else + { + sql = $"SELECT {column} FROM {_lockname}{table}{where}{group}{having}{order}"; + } + } + else + { + var limit = _page.Index > 0 || _page.Count > 0 ? $" LIMIT {_page.Index},{_page.Count}" : string.Empty; + sql = $"SELECT {column} FROM {table}{where}{group}{having}{order}{limit}{_lockname}"; + } + return sql; + } + + private string ResovleInsert(bool identity) + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var filters = new GroupExpressionResovle(_filterExpression).Resovle().Split(','); + var columns = DbMetaInfoCache.GetColumns(typeof(T)); + var intcolumns = columns + .Where(a => !filters.Contains(a.ColumnName) && !a.IsNotMapped && !a.IsIdentity) + .Where(a=> !a.IsComplexType) + .Where(a => !a.IsDefault || (_parameters.ContainsKey(a.CsharpName) && _parameters[a.CsharpName] != null));//如果是默认字段 + var columnNames = string.Join(",", intcolumns.Select(s => s.ColumnName)); + var parameterNames = string.Join(",", intcolumns.Select(s => $"@{s.CsharpName}")); + var sql = $"INSERT INTO {table}({columnNames}) VALUES ({parameterNames})"; + if (identity) + { + sql = $"{sql};SELECT @@IDENTITY"; + } + return sql; + } + + private string ResovleBatchInsert(IEnumerable entitys) + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var filters = new GroupExpressionResovle(_filterExpression).Resovle().Split(','); + var columns = DbMetaInfoCache.GetColumns(typeof(T)) + .Where(a=>!a.IsComplexType).ToList(); + var intcolumns = columns + .Where(a => !filters.Contains(a.ColumnName) && !a.IsNotMapped && !a.IsIdentity) + .ToList(); + var columnNames = string.Join(",", intcolumns.Select(s => s.ColumnName)); + if (_context.DbContextType == DbContextType.Mysql) + { + var buffer = new StringBuilder(); + buffer.Append($"INSERT INTO {table}({columnNames}) VALUES "); + var serializer = EmitConvert.GetDeserializer(typeof(T)); + var list = entitys.ToList(); + for (var i = 0; i < list.Count; i++) + { + var item = list[i]; + var values = serializer(item); + buffer.Append("("); + for (var j = 0; j < intcolumns.Count; j++) + { + var column = intcolumns[j]; + var value = values[column.CsharpName]; + if (value == null) + { + buffer.Append(column.IsDefault ? "DEFAULT" : "NULL"); + } + else if (column.CsharpType == typeof(bool) || column.CsharpType == typeof(bool?)) + { + buffer.Append(Convert.ToBoolean(value) == true ? 1 : 0); + } + else if (column.CsharpType == typeof(DateTime) || column.CsharpType == typeof(DateTime?)) + { + buffer.Append($"'{value}'"); + } + else if (column.CsharpType.IsValueType || (Nullable.GetUnderlyingType(column.CsharpType)?.IsValueType == true)) + { + buffer.Append(value); + } + else + { + var str = SqlEncoding(value.ToString()); + buffer.Append($"'{str}'"); + } + if (j + 1 < intcolumns.Count) + { + buffer.Append(","); + } + } + buffer.Append(")"); + if (i + 1 < list.Count) + { + buffer.Append(","); + } + } + return buffer.Remove(buffer.Length - 1, 0).ToString(); + } + throw new NotImplementedException(); + } + + private string ResolveUpdate() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var builder = new StringBuilder(); + if (_setExpressions.Count > 0) + { + var where = ResolveWhere(); + foreach (var item in _setExpressions) + { + var column = new BooleanExpressionResovle(item.Column).Resovle(); + var expression = new BooleanExpressionResovle(item.Expression, _parameters).Resovle(); + builder.Append($"{column} = {expression},"); + } + var sql = $"UPDATE {table} SET {builder.ToString().Trim(',')}{where}"; + return sql; + } + else + { + var filters = new GroupExpressionResovle(_filterExpression).Resovle().Split(','); + var where = ResolveWhere(); + var columns = DbMetaInfoCache.GetColumns(typeof(T)); + var updcolumns = columns + .Where(a => !filters.Contains(a.ColumnName)) + .Where(a => !a.IsComplexType) + .Where(a => !a.IsIdentity && !a.IsPrimaryKey && !a.IsNotMapped) + .Where(a => !a.IsConcurrencyCheck) + .Select(s => $"{s.ColumnName} = @{s.CsharpName}"); + if (string.IsNullOrEmpty(where)) + { + var primaryKey = columns.Where(a => a.IsPrimaryKey).FirstOrDefault() + ?? columns.First(); + where = $" WHERE {primaryKey.ColumnName} = @{primaryKey.CsharpName}"; + if (columns.Exists(a => a.IsConcurrencyCheck)) + { + var checkColumn = columns.Where(a => a.IsConcurrencyCheck).FirstOrDefault(); + where += $" AND {checkColumn.ColumnName} = @{checkColumn.CsharpName}"; + } + } + var sql = $"UPDATE {table} SET {string.Join(",", updcolumns)}"; + if (columns.Exists(a => a.IsConcurrencyCheck)) + { + var checkColumn = columns.Where(a => a.IsConcurrencyCheck).FirstOrDefault(); + sql += $",{checkColumn.ColumnName} = @New{checkColumn.CsharpName}"; + if (checkColumn.CsharpType.IsValueType) + { + var version = Convert.ToInt32((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds); + _parameters.Add($"New{checkColumn.CsharpName}", version); + } + else + { + var version = Guid.NewGuid().ToString("N"); + _parameters.Add($"New{checkColumn.CsharpName}", version); + } + } + sql += where; + return sql; + } + } + + private string ResovleDelete() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var where = ResolveWhere(); + var sql = $"DELETE FROM {table}{where}"; + return sql; + } + + private string SqlEncoding(string sql) + { + var buffer = new StringBuilder(); + for (int i = 0; i < sql.Length; i++) + { + var ch = sql[i]; + if (ch=='\''||ch=='-'||ch=='\\'||ch=='*'||ch=='@') + { + buffer.Append('\\'); + } + buffer.Append(ch); + } + return buffer.ToString(); + } + + private string ResovleExists() + { + var table = DbMetaInfoCache.GetTable(typeof(T)).TableName; + var where = ResolveWhere(); + var group = ResolveGroup(); + var having = ResolveHaving(); + var sql = $"SELECT 1 WHERE EXISTS(SELECT 1 FROM {table}{where}{group}{having})"; + return sql; + } + + private string ResovleColumns() + { + if (_selectExpression == null) + { + var filters = new GroupExpressionResovle(_filterExpression) + .Resovle().Split(','); + var columns = DbMetaInfoCache.GetColumns(typeof(T)) + .Where(a => !filters.Contains(a.ColumnName) && !a.IsNotMapped) + .Select(s => s.ColumnName != s.CsharpName ? $"{s.ColumnName} AS {s.CsharpName}" : s.CsharpName); + return string.Join(",", columns); + } + else + { + return new SelectExpressionResovle(_selectExpression).Resovle(); + } + } + + private string ResolveWhere() + { + var builder = new StringBuilder(); + foreach (var expression in _whereExpressions) + { + var result = new BooleanExpressionResovle(expression, _parameters).Resovle(); + if (expression == _whereExpressions.First()) + { + builder.Append($" WHERE {result}"); + } + else + { + builder.Append($" AND {result}"); + } + } + return builder.ToString(); + } + + private string ResolveGroup() + { + var buffer = new StringBuilder(); + foreach (var item in _groupExpressions) + { + var result = new GroupExpressionResovle(item).Resovle(); + buffer.Append($"{result},"); + } + var sql = string.Empty; + if (buffer.Length > 0) + { + buffer.Remove(buffer.Length - 1, 1); + sql = $" GROUP BY {buffer.ToString()}"; + } + return sql; + } + + private string ResolveHaving() + { + var buffer = new StringBuilder(); + foreach (var item in _havingExpressions) + { + var result = new BooleanExpressionResovle(item, _parameters).Resovle(); + if (item == _havingExpressions.First()) + { + buffer.Append($" HAVING {result}"); + } + else + { + buffer.Append($" AND {result}"); + } + } + return buffer.ToString(); + } + + private string ResolveOrder() + { + var buffer = new StringBuilder(); + foreach (var item in _orderExpressions) + { + if (item == _orderExpressions.First()) + { + buffer.Append($" ORDER BY "); + } + var result = new OrderExpressionResovle(item.Expression, item.Asc).Resovle(); + buffer.Append(result); + buffer.Append(","); + } + return buffer.ToString().Trim(','); + } + + class PageData + { + public int Index { get; set; } = -1; + public int Count { get; set; } + } + + class OrderExpression + { + public bool Asc { get; set; } = true; + public Expression Expression { get; set; } + } + + class SetExpression + { + public Expression Column { get; set; } + public Expression Expression { get; set; } + } + #endregion + } +} diff --git a/src/Dapper.Linq/Queryables/DbQueryAsync.cs b/src/Dapper.Linq/Queryables/DbQueryAsync.cs new file mode 100644 index 0000000..4c9e0e8 --- /dev/null +++ b/src/Dapper.Linq/Queryables/DbQueryAsync.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Dapper.Expressions; + +namespace Dapper +{ + /// + /// 异步linq查询 + /// + public partial class DbQuery + { + + #region async + public async Task GetAsync(object id) + { + var sql = ResolveGet(); + var values = new Dictionary + { + { "id", id } + }; + return (await _context.ExecuteQueryAsync(sql, values)).FirstOrDefault(); + } + public Task CountAsync(int? commandTimeout = null) + { + var sql = ResovleCount(); + return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); + } + + public Task CountAsync(Expression> expression) + { + _countExpression = expression; + return CountAsync(); + } + + public Task DeleteAsync(int? commandTimeout = null) + { + var sql = ResovleDelete(); + return _context.ExecuteNonQueryAsync(sql, _parameters, commandTimeout); + } + + public Task DeleteAsync(Expression> expression) + { + Where(expression); + return DeleteAsync(); + } + + public Task ExistsAsync(int? commandTimeout = null) + { + var sql = ResovleExists(); + return _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); + } + + public Task ExistsAsync(Expression> expression) + { + Where(expression); + return ExistsAsync(); + } + + public Task UpdateAsync(int? commandTimeout = null) + { + if (_setExpressions.Count > 0) + { + var sql = ResolveUpdate(); + return _context.ExecuteNonQueryAsync(sql, _parameters, commandTimeout); + } + return default; + } + + public async Task UpdateAsync(T entity) + { + ResovleParameter(entity); + var sql = ResolveUpdate(); + var row = await _context.ExecuteNonQueryAsync(sql, _parameters); + if (DbMetaInfoCache.GetColumns(typeof(T)).Exists(a => a.IsConcurrencyCheck) && row == 0) + { + throw new DbUpdateConcurrencyException("更新失败:数据版本不一致"); + } + return row; + } + + public Task InsertAsync(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(false); + return _context.ExecuteNonQueryAsync(sql, _parameters); + } + + public async Task InsertAsync(IEnumerable entitys, int? commandTimeout = null) + { + if (entitys == null || entitys.Count() == 0) + { + return 0; + } + var sql = ResovleBatchInsert(entitys); + return await _context.ExecuteNonQueryAsync(sql, _parameters, commandTimeout); + } + + public Task InsertReturnIdAsync(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(true); + return _context.ExecuteScalarAsync(sql, _parameters); + } + + public async Task SumAsync(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResovleSum(); + return await _context.ExecuteScalarAsync(sql, _parameters, commandTimeout); + } + + public Task> SelectAsync(int? commandTimeout = null) + { + var sql = ResolveSelect(); + return _context.ExecuteQueryAsync(sql, _parameters, commandTimeout); + } + + public async Task<(IEnumerable, int)> SelectManyAsync(int? commandTimeout = null) + { + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = await multi.GetListAsync(); + var count = await multi.GetAsync(); + return (list, count); + } + } + + public Task> SelectAsync(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResolveSelect(); + return _context.ExecuteQueryAsync(sql, _parameters, commandTimeout); + } + + public async Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = await multi.GetListAsync(); + var count = await multi.GetAsync(); + return (list, count); + } + } + + public async Task SingleAsync(int? commandTimeout = null) + { + Take(1); + return (await SelectAsync(commandTimeout)).FirstOrDefault(); + } + + public async Task SingleAsync(Expression> expression, int? commandTimeout = null) + { + Take(1); + return (await SelectAsync(expression, commandTimeout)).FirstOrDefault(); + } + #endregion + } +} diff --git a/src/Dapper.Linq/Queryables/DbQuerySync.cs b/src/Dapper.Linq/Queryables/DbQuerySync.cs new file mode 100644 index 0000000..dcb5e2d --- /dev/null +++ b/src/Dapper.Linq/Queryables/DbQuerySync.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using Dapper.Expressions; + +namespace Dapper +{ + /// + /// 同步linq查询 + /// + /// + public partial class DbQuery + { + #region sync + public T Get(object id) + { + var sql = ResolveGet(); + var values = new Dictionary + { + { "id", id } + }; + return _context.ExecuteQuery(sql, values).FirstOrDefault(); + } + + public int Count(int? commandTimeout = null) + { + var sql = ResovleCount(); + return _context.ExecuteScalar(sql, _parameters, commandTimeout); + } + + public int Count(Expression> expression) + { + _countExpression = expression; + return Count(); + } + + public int Insert(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(false); + return _context.ExecuteNonQuery(sql, _parameters); + } + + public int InsertReturnId(T entity) + { + ResovleParameter(entity); + var sql = ResovleInsert(true); + return _context.ExecuteScalar(sql, _parameters); + } + + public int Insert(IEnumerable entitys, int? commandTimeout = null) + { + if (entitys==null || entitys.Count()==0) + { + return 0; + } + var sql = ResovleBatchInsert(entitys); + return _context.ExecuteNonQuery(sql, _parameters, commandTimeout); + } + + public int Update(int? commandTimeout = null) + { + if (_setExpressions.Count > 0) + { + var sql = ResolveUpdate(); + return _context.ExecuteNonQuery(sql, _parameters, commandTimeout); + } + return default; + } + + public int Update(T entity) + { + ResovleParameter(entity); + var sql = ResolveUpdate(); + var row = _context.ExecuteNonQuery(sql, _parameters); + if (DbMetaInfoCache.GetColumns(typeof(T)).Exists(a => a.IsConcurrencyCheck) && row == 0) + { + throw new DbUpdateConcurrencyException("更新失败:数据版本不一致"); + } + return row; + } + + public int Delete(int? commandTimeout = null) + { + var sql = ResovleDelete(); + return _context.ExecuteNonQuery(sql, _parameters, commandTimeout); + } + + public int Delete(Expression> expression) + { + Where(expression); + return Delete(); + } + + public bool Exists(int? commandTimeout = null) + { + var sql = ResovleExists(); + return _context.ExecuteScalar(sql, _parameters, commandTimeout); + } + + public bool Exists(Expression> expression) + { + Where(expression); + return Exists(); + } + + public IDbQuery Set(Expression> column, TResult value, bool condition = true) + { + if (true) + { + _setExpressions.Add(new SetExpression + { + Column = column, + Expression = Expression.Constant(value) + }); + } + return this; + } + + public IDbQuery Set(Expression> column, Expression> expression, bool condition = true) + { + if (true) + { + _setExpressions.Add(new SetExpression + { + Column = column, + Expression = expression + }); + } + return this; + } + + public IDbQuery GroupBy(Expression> expression) + { + _groupExpressions.Add(expression); + return this; + } + + public IDbQuery Having(Expression> expression, bool condition = true) + { + if (condition) + { + _havingExpressions.Add(expression); + } + return this; + } + + public IDbQuery OrderBy(Expression> expression) + { + _orderExpressions.Add(new OrderExpression + { + Asc = true, + Expression = expression + }); + return this; + } + + public IDbQuery OrderByDescending(Expression> expression) + { + _orderExpressions.Add(new OrderExpression + { + Asc = false, + Expression = expression + }); + return this; + } + + public IDbQuery Filter(Expression> column) + { + _filterExpression = column; + return this; + } + + public IDbQuery Page(int index, int count, bool condition = true) + { + if (condition) + { + Skip((index - 1) * count, count); + } + return this; + } + + public IDbQuery With(string lockname) + { + _lockname = $" {lockname}"; + return this; + } + + public IEnumerable Select(int? commandTimeout = null) + { + var sql = ResolveSelect(); + return _context.ExecuteQuery(sql, _parameters, commandTimeout); + } + + public (IEnumerable, int) SelectMany(int? commandTimeout = null) + { + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = multi.GetList(); + var count = multi.Get(); + return (list, count); + } + } + public TResult Sum(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResovleSum(); + return _context.ExecuteScalar(sql, _parameters, commandTimeout); + } + public IEnumerable Select(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql = ResolveSelect(); + return _context.ExecuteQuery(sql, _parameters, commandTimeout); + } + + public (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null) + { + _selectExpression = expression; + var sql1 = ResolveSelect(); + var sql2 = ResovleCount(); + using (var multi = _context.ExecuteMultiQuery($"{sql1};{sql2}", _parameters, commandTimeout)) + { + var list = multi.GetList(); + var count = multi.Get(); + return (list, count); + } + } + + public T Single(int? commandTimeout = null) + { + Take(1); + return Select(commandTimeout).FirstOrDefault(); + } + + public TResult Single(Expression> expression, int? commandTimeout = null) + { + Take(1); + return Select(expression, commandTimeout).FirstOrDefault(); + } + + public IDbQuery Skip(int index, int count, bool condition = true) + { + if (condition) + { + _page.Index = index; + _page.Count = count; + } + return this; + } + + public IDbQuery Take(int count,bool condition=true) + { + if (condition) + { + Skip(0, count); + } + return this; + } + + public IDbQuery Where(Expression> expression, bool condition = true) + { + if (condition) + { + _whereExpressions.Add(expression); + } + return this; + } + + #endregion + } +} diff --git a/src/Dapper.Linq/Queryables/IDbQuery.cs b/src/Dapper.Linq/Queryables/IDbQuery.cs new file mode 100644 index 0000000..3d10a12 --- /dev/null +++ b/src/Dapper.Linq/Queryables/IDbQuery.cs @@ -0,0 +1,354 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace Dapper +{ + /// + /// linq 查询 + /// + /// + public interface IDbQuery + { + /// + /// 通过主键检索数据 + /// + /// + /// + T Get(object id); + /// + /// 异步通过主键检索数据 + /// + /// + /// + Task GetAsync(object id); + /// + /// count查询 + /// + /// 超时时间 + /// + int Count(int? commandTimeout = null); + /// + /// 异步count查询 + /// + /// 超时时间 + /// + Task CountAsync(int? commandTimeout = null); + /// + /// count查询 + /// + /// 类型推断 + /// 字段列表 + /// + int Count(Expression> expression); + /// + /// 异步count查询 + /// + /// 类型推断 + /// 字段列表 + /// + Task CountAsync(Expression> expression); + /// + /// delete查询 + /// + /// + /// + int Delete(int? commandTimeout = null); + /// + /// 异步delete查询 + /// + /// + /// + Task DeleteAsync(int? commandTimeout = null); + /// + /// delete查询 + /// + /// 查询条件 + /// + int Delete(Expression> expression); + /// + /// 异步delete查询 + /// + /// 查询条件 + /// + Task DeleteAsync(Expression> expression); + /// + /// exists查询 + /// + /// 超时时间 + /// + bool Exists(int? commandTimeout = null); + /// + /// 异步exists查询 + /// + /// 超时时间 + /// + Task ExistsAsync(int? commandTimeout = null); + /// + /// exists查询 + /// + /// 查询条件 + /// + bool Exists(Expression> expression); + /// + /// 异步exists查询 + /// + /// 查询条件 + /// + Task ExistsAsync(Expression> expression); + /// + /// update查询,如果没有指定where则应用到所有记录 + /// + /// 超时时间 + /// + int Update(int? commandTimeout = null); + /// + /// 异步update查询,如果没有指定where则应用到所有记录 + /// + /// 超时时间 + /// + Task UpdateAsync(int? commandTimeout = null); + /// + /// update查询,默认根据Primarkey更新,如果存在where则仅使用指定更新条件, + /// 无法通过该接口更新主键字段和主键字段 + /// + /// 参数 + /// + int Update(T entity); + /// + /// 异步update查询,默认根据Primarkey更新,如果存在where则仅使用指定更新条件, + /// 无法通过该接口更新主键字段和主键字段 + /// + /// 参数 + /// + Task UpdateAsync(T entity); + /// + /// insert查询,该接口会忽略identity字段 + /// + /// 参数 + /// + int Insert(T entity); + /// + /// 异步insert查询,该接口会忽略identity字段 + /// + /// 参数 + /// + Task InsertAsync(T entity); + /// + /// insert查询,并返回id,该接口会忽略identity字段 + /// + /// 参数 + /// + int InsertReturnId(T entity); + /// + /// 异步insert查询,并返回id,该接口会忽略identity字段 + /// + /// + /// + Task InsertReturnIdAsync(T entity); + /// + /// 批量insert查询,该接口会忽略identity字段 + /// + /// 参数集合 + /// 超时时间 + /// + int Insert(IEnumerable entitys,int? commandTimeout = null); + /// + /// 异步批量insert查询,该接口会忽略identity字段 + /// + /// + /// 超时时间 + /// + Task InsertAsync(IEnumerable entitys, int? commandTimeout = null); + /// + /// select查询 + /// + /// 超时时间 + /// + IEnumerable Select(int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 超时时间 + /// + Task> SelectAsync(int? commandTimeout = null); + /// + /// 分页select查询 + /// + /// 超时时间 + /// 结果集,总记录数 + (IEnumerable, int) SelectMany(int? commandTimeout = null); + /// + /// 异步分页select查询 + /// + /// 超时时间 + /// + Task<(IEnumerable, int)> SelectManyAsync(int? commandTimeout = null); + /// + /// select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + IEnumerable Select(Expression> expression, int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task> SelectAsync(Expression> expression, int? commandTimeout = null); + /// + /// 分页select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + (IEnumerable, int) SelectMany(Expression> expression, int? commandTimeout = null); + /// + /// 异步分页select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task<(IEnumerable, int)> SelectManyAsync(Expression> expression, int? commandTimeout = null); + /// + /// select查询 + /// + /// 超时时间 + /// + T Single(int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 超时时间 + /// + Task SingleAsync(int? commandTimeout = null); + /// + /// select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + TResult Single(Expression> expression, int? commandTimeout = null); + /// + /// 异步select查询 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task SingleAsync(Expression> expression, int? commandTimeout = null); + /// + /// 在insert,update,select时过滤字段 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery Filter(Expression> expression); + /// + /// set查询 + /// + /// 类型推断 + /// 字段 + /// 参数 + /// 是否有效 + /// + IDbQuery Set(Expression> column, TResult value, bool condition = true); + /// + /// set查询 + /// + /// 类型推断 + /// 字段 + /// 表达式 + /// 是否有效 + /// + IDbQuery Set(Expression> column, Expression> expression, bool condition = true); + /// + /// take查询,从下标为0的行获取count条记录 + /// + /// 记录个数 + /// 条件 + /// + IDbQuery Take(int count, bool condition = true); + /// + /// skip,从下标为index的行获取count条记录 + /// + /// 起始下标 + /// 记录个数 + /// 条件 + /// + IDbQuery Skip(int index, int count, bool condition = true); + /// + /// page查询,从下标为(index-1)*count的行获取count条记录 + /// + /// 起始页码 + /// 记录个数 + /// 条件 + /// + IDbQuery Page(int index, int count, bool condition = true); + /// + /// 指定读锁 + /// + /// + /// + IDbQuery With(string lockname); + /// + /// where查询,多个where有效使用and连接 + /// + /// 表达式 + /// 是否有效 + /// + IDbQuery Where(Expression> expression, bool condition = true); + /// + /// having查询,多个having查询有效使用and连接 + /// + /// 表达式 + /// 是否有效 + /// + IDbQuery Having(Expression> expression, bool condition = true); + /// + /// group查询 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery GroupBy(Expression> expression); + /// + /// orderby查询,升序 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery OrderBy(Expression> expression); + /// + /// orderby查询,降序 + /// + /// 类型推断 + /// 字段列表 + /// + IDbQuery OrderByDescending(Expression> expression); + /// + /// 求和 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + TResult Sum(Expression> expression, int? commandTimeout = null); + /// + /// 异步求和 + /// + /// 返回类型 + /// 字段列表 + /// 超时时间 + /// + Task SumAsync(Expression> expression, int? commandTimeout = null); + } +} diff --git a/src/Dapper.Linq/Queryables/Operator.cs b/src/Dapper.Linq/Queryables/Operator.cs new file mode 100644 index 0000000..a45fcb5 --- /dev/null +++ b/src/Dapper.Linq/Queryables/Operator.cs @@ -0,0 +1,201 @@ +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace Dapper +{ + /// + /// 数据库操作符 + /// + public class Operator + { + /// + /// in + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool In(T column, IEnumerable values) => default; + /// + /// in(低性能) + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool In(T column, params T[] values) => default; + /// + /// not in + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotIn(T column, IEnumerable values) => default; + /// + /// not in(低性能) + /// + /// 类型推断 + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotIn(T column, params T[] values) => default; + /// + /// like %value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool Contains(string column, string value) => default; + /// + /// not like %value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotContains(string column, string value) => default; + /// + /// like value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool StartsWith(string column, string value) => default; + /// + /// not like value% + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotStartsWith(string column, string value) => default; + /// + /// like %value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool EndsWith(string column, string value) => default; + /// + /// not like %value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotEndsWith(string column, string value) => default; + /// + /// regex value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool Regexp(string column, string value) => default; + /// + /// not regex value + /// + /// 字段 + /// 参数 + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("样式", "IDE0060:删除未使用的参数", Justification = "<挂起>")] + public static bool NotRegexp(string column, string value) => default; + /// + /// 解析表达式 + /// + /// + /// + internal static string ResovleExpressionType(ExpressionType type) + { + var condition = string.Empty; + switch (type) + { + case ExpressionType.Add: + condition = "+"; + break; + case ExpressionType.Subtract: + condition = "-"; + break; + case ExpressionType.Multiply: + condition = "*"; + break; + case ExpressionType.Divide: + condition = "/"; + break; + case ExpressionType.Modulo: + condition = "%"; + break; + case ExpressionType.Equal: + condition = "="; + break; + case ExpressionType.NotEqual: + condition = "<>"; + break; + case ExpressionType.GreaterThan: + condition = ">"; + break; + case ExpressionType.GreaterThanOrEqual: + condition = ">="; + break; + case ExpressionType.LessThan: + condition = "<"; + break; + case ExpressionType.LessThanOrEqual: + condition = "<="; + break; + case ExpressionType.OrElse: + condition = "OR"; + break; + case ExpressionType.AndAlso: + condition = "AND"; + break; + case ExpressionType.Not: + condition = "NOT"; + break; + } + return condition; + } + /// + /// 解析表达式 + /// + /// + /// + internal static string ResovleExpressionType(string type) + { + switch (type) + { + case nameof(Operator.In): + type = "IN"; + break; + case nameof(Operator.NotIn): + type = "NOT IN"; + break; + case nameof(Operator.Contains): + case nameof(Operator.StartsWith): + case nameof(Operator.EndsWith): + type = "LIKE"; + break; + case nameof(Operator.NotContains): + case nameof(Operator.NotStartsWith): + case nameof(Operator.NotEndsWith): + type = "NOT LIKE"; + break; + case nameof(Operator.Regexp): + type = "REGEXP"; + break; + case nameof(Operator.NotRegexp): + type = "NOT REGEXP"; + break; + } + return type; + } + } +} diff --git a/src/Dapper.Linq/XmlResovles/Nodes/CommandNode.cs b/src/Dapper.Linq/XmlResovles/Nodes/CommandNode.cs new file mode 100644 index 0000000..bd5d6c3 --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/Nodes/CommandNode.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Dapper.Expressions; + +namespace Dapper.XmlResovles +{ + internal class CommandNode : INode + { + public List Nodes { get; set; } = new List(); + + private string ResolveTextNode(TextNode node) + { + return node.Value; + } + + private string ResolveIfNode(IfNode node, T parameter) + { + if (node.Delegate == null) + { + lock (this) + { + var context = new ExpressionActivator(); + var result = context.Create(node.Test); + node.Delegate = result.Func; + } + } + var func = node.Delegate as Func; + if (func(parameter)) + { + return ResolveTextNode(new TextNode { Value = node.Value }); + } + return string.Empty; + } + + private string ResolveWhereNode(WhereNode node, T parameter) where T :class + { + var buffer = new StringBuilder(); + foreach (var item in node.Nodes) + { + if (parameter!=default && item is IfNode) + { + var text = ResolveIfNode(item as IfNode, parameter); + buffer.Append($"{text} "); + } + else if (item is TextNode) + { + var text = ResolveTextNode(item as TextNode); + buffer.Append($"{text} "); + } + } + var sql = buffer.ToString().Trim(' '); + if (sql.StartsWith("and", StringComparison.OrdinalIgnoreCase)) + { + sql = sql.Remove(0, 3); + } + else if (sql.StartsWith("or", StringComparison.OrdinalIgnoreCase)) + { + sql = sql.Remove(0, 2); + } + return sql.Length > 0 ? "WHERE " + sql : string.Empty; + } + + public string Resolve(CommandNode command, T parameter) where T : class + { + var buffer = new StringBuilder(); + foreach (var item in command.Nodes) + { + if (item is TextNode) + { + var txt = ResolveTextNode(item as TextNode); + if (txt.Length > 0) + { + buffer.AppendFormat($" {txt}"); + } + } + else if (item is WhereNode) + { + var wheresql = ResolveWhereNode(item as WhereNode, parameter); + if (wheresql.Length > 0) + { + buffer.AppendFormat($" {wheresql}"); + } + } + else if (item is IfNode) + { + var txt = ResolveIfNode(item as IfNode, parameter); + if (txt.Length > 0) + { + buffer.AppendFormat($" {txt}"); + } + } + } + return buffer.ToString().Trim(' '); + } + + } +} diff --git a/src/Dapper.Linq/XmlResovles/Nodes/INode.cs b/src/Dapper.Linq/XmlResovles/Nodes/INode.cs new file mode 100644 index 0000000..e77516e --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/Nodes/INode.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.XmlResovles +{ + interface INode + { + + } +} diff --git a/src/Dapper.Linq/XmlResovles/Nodes/IfNode.cs b/src/Dapper.Linq/XmlResovles/Nodes/IfNode.cs new file mode 100644 index 0000000..dc45def --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/Nodes/IfNode.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.XmlResovles +{ + internal class IfNode : INode + { + public Delegate Delegate { get; set; } + public string Test { get; set; } + public string Value { get; set; } + } +} diff --git a/src/Dapper.Linq/XmlResovles/Nodes/TextNode.cs b/src/Dapper.Linq/XmlResovles/Nodes/TextNode.cs new file mode 100644 index 0000000..c35b324 --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/Nodes/TextNode.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Dapper.XmlResovles +{ + internal class TextNode : INode + { + public string Value { get; set; } + + } +} diff --git a/src/Dapper.Linq/XmlResovles/Nodes/WhereNode.cs b/src/Dapper.Linq/XmlResovles/Nodes/WhereNode.cs new file mode 100644 index 0000000..69ba7ac --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/Nodes/WhereNode.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Dapper.XmlResovles +{ + internal class WhereNode : INode + { + public List Nodes { get; set; } = new List(); + } +} diff --git a/src/Dapper.Linq/XmlResovles/XmlQuery.cs b/src/Dapper.Linq/XmlResovles/XmlQuery.cs new file mode 100644 index 0000000..34f8cd1 --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/XmlQuery.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; + +namespace Dapper +{ + /// + /// xml命令映射器 + /// + public interface IXmlQuery + { + /// + ///执行多结果集查询,返回IMultiResult + /// + /// 超时时间 + /// 命令类型 + /// + IMultiResult ExecuteMultiQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回dynamic类型的结果集 + /// + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回dynamic类型的结果集 + /// + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// 超时时间 + /// 命令类型 + /// + IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行单结果集查询,并返回T类型的结果集 + /// + /// 返回类型 + /// 超时时间 + /// 命令类型 + /// + Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回受影响的行数 + /// + /// 超时时间 + /// 命令类型 + /// + int ExecuteNonQuery(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回受影响的行数 + /// + /// 超时时间 + /// 命令类型 + /// + Task ExecuteNonQueryAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 执行无结果集查询,并返回指定类型的数据 + /// + /// 返回类型 + /// 超时时间 + /// 命令类型 + /// + T ExecuteScalar(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 异步执行无结果集查询,并返回指定类型的数据 + /// + /// + /// 超时时间 + /// 命令类型 + /// + Task ExecuteScalarAsync(int? commandTimeout = null, CommandType? commandType = null); + /// + /// 添加数据库参数 + /// + /// + /// + void AddDbParameter(string name, object value); + } + + /// + /// 实现xml命令映射器 + /// + internal class XmlQuery : IXmlQuery + { + private readonly string _sql = null; + + private Dictionary _parameters = null; + + private readonly IDbContext _mapper = null; + + public XmlQuery(IDbContext mapper, string sql, Dictionary parameters=null) + { + _mapper = mapper; + _sql = sql; + _parameters = parameters; + } + + public IMultiResult ExecuteMultiQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteMultiQuery(_sql, _parameters, commandTimeout,commandType); + } + + public int ExecuteNonQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteNonQuery(_sql, _parameters, commandTimeout, commandType); + } + + public Task ExecuteNonQueryAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteNonQueryAsync(_sql, _parameters, commandTimeout, commandType); + } + + public IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQuery(_sql, _parameters, commandTimeout, commandType); + } + + public IEnumerable ExecuteQuery(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQuery(_sql, _parameters, commandTimeout, commandType); + } + + public Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQueryAsync(_sql, _parameters, commandTimeout, commandType); + } + + public Task> ExecuteQueryAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteQueryAsync(_sql, _parameters, commandTimeout, commandType); + } + + public T ExecuteScalar(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteScalar(_sql, _parameters, commandTimeout, commandType); + } + + public Task ExecuteScalarAsync(int? commandTimeout = null, CommandType? commandType = null) + { + return _mapper.ExecuteScalarAsync(_sql, _parameters, commandTimeout, commandType); + } + + public void AddDbParameter(string name,object value) + { + if (_parameters == null) + { + _parameters = new Dictionary(); + } + if (_parameters.ContainsKey(name)) + { + _parameters[name] = value; + } + else + { + _parameters.Add(name,value); + } + } + } +} diff --git a/src/Dapper.Linq/XmlResovles/XmlResovle.cs b/src/Dapper.Linq/XmlResovles/XmlResovle.cs new file mode 100644 index 0000000..ef53721 --- /dev/null +++ b/src/Dapper.Linq/XmlResovles/XmlResovle.cs @@ -0,0 +1,240 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using Dapper.XmlResovles; + +namespace Dapper +{ + public interface IXmlResovle + { + /// + /// 解析动态sql + /// + /// + /// + /// + /// + string Resolve(string id, T parameter) where T : class; + /// + /// 解析sql + /// + /// + /// + string Resolve(string id); + /// + /// 加载配置文件 + /// + /// 文件名 + void Load(string filename); + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 文件通配符 + void Load(string path, string pattern); + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 文件通配符 + /// 查找选项 + void Load(string path, string pattern, SearchOption options); + } + + public class XmlResovle : IXmlResovle + { + private readonly Dictionary _commands + = new Dictionary(); + + private Dictionary ResolveVariables(XmlElement element) + { + var variables = new Dictionary(); + var elements = element.Cast() + .Where(a => a.Name == "var"); + foreach (XmlElement item in elements) + { + if (item.Name == "var") + { + var id = item.GetAttribute("id"); + var value = string.IsNullOrEmpty(item.InnerXml) + ? item.GetAttribute("value") : item.InnerXml; + variables.Add(id, value); + } + } + return variables; + } + + private string ReplaceVariable(Dictionary variables, string text) + { + var matches = Regex.Matches(text, @"\${(?.*?)}"); + foreach (Match item in matches) + { + var key = item.Groups["key"].Value; + if (variables.ContainsKey(key)) + { + var value = variables[key]; + text = text.Replace("${" + key + "}", value); + } + } + return Regex.Replace(text, @"\s+", " ").Trim(' '); + } + + private CommandNode ResolveCommand(XmlElement element) + { + var cmd = new CommandNode(); + foreach (XmlNode item in element.ChildNodes) + { + if (item.Name == "var" || item.NodeType == XmlNodeType.Comment) + { + continue; + } + if (item.NodeType == XmlNodeType.Text) + { + cmd.Nodes.Add(new TextNode + { + Value = item.Value + }); + } + else if (item.NodeType == XmlNodeType.Element && item.Name == "where") + { + var whereNode = new WhereNode(); + foreach (XmlNode iitem in item.ChildNodes) + { + if (iitem.NodeType == XmlNodeType.Text) + { + whereNode.Nodes.Add(new TextNode + { + Value = iitem.Value + }); + } + else if (iitem.NodeType == XmlNodeType.Element && iitem.Name == "if") + { + var test = iitem.Attributes["test"].Value; + var value = string.IsNullOrEmpty(iitem.InnerText) ? + (iitem.Attributes["value"]?.Value ?? string.Empty) : iitem.InnerText; + whereNode.Nodes.Add(new IfNode + { + Test = test, + Value = value + }); + } + } + cmd.Nodes.Add(whereNode); + } + else if (item.NodeType == XmlNodeType.Element && item.Name == "if") + { + var test = item.Attributes["test"].Value; + var value = string.IsNullOrEmpty(item.InnerText) ? + (item.Attributes["value"]?.Value ?? string.Empty) : item.InnerText; + cmd.Nodes.Add(new IfNode + { + Test = test, + Value = value + }); + } + } + return cmd; + } + + public string Resolve(string id, T parameter) where T : class + { + if (!_commands.ContainsKey(id)) + { + return null; + } + var cmd = _commands[id]; + return cmd.Resolve(cmd, parameter); + } + + public string Resolve(string id) + { + return Resolve(id, (object)null); + } + + /// + /// 加载配置文件 + /// + /// + public void Load(string filename) + { + lock (this) + { + XmlDocument document = new XmlDocument(); + document.Load(filename); + var @namespace = document.DocumentElement + .GetAttribute("namespace") ?? string.Empty; + //解析全局变量 + var globalVariables = ResolveVariables(document.DocumentElement); + //获取命令节点 + var elements = document.DocumentElement + .Cast() + .Where(a => a.Name != "var" && a is XmlElement); + foreach (XmlElement item in elements) + { + var id = item.GetAttribute("id"); + id = string.IsNullOrEmpty(@namespace) ? $"{id}" : $"{@namespace}.{id}"; + //解析局部变量 + var localVariables = ResolveVariables(item); + //合并局部和全局变量,局部变量可以覆盖全局变量 + var variables = new Dictionary(globalVariables); + foreach (var ariable in localVariables) + { + if (variables.ContainsKey(ariable.Key)) + { + variables[ariable.Key] = ariable.Value; + } + else + { + variables.Add(ariable.Key, ariable.Value); + } + } + //替换变量 + var xml = ReplaceVariable(variables, item.OuterXml); + var doc = new XmlDocument(); + doc.LoadXml(xml); + //通过变量解析命令 + var cmd = ResolveCommand(doc.DocumentElement); + if (_commands.ContainsKey(id)) + { + _commands[id] = cmd; + } + else + { + _commands.Add(id, cmd); + } + } + } + } + + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 通配符 + public void Load(string path, string pattern) + { + var files = System.IO.Directory.GetFiles(path, pattern); + foreach (var item in files) + { + Load(item); + } + } + + /// + /// 从指定路径加载所有匹配的文件 + /// + /// 路径 + /// 通配符 + /// 查找选项 + public void Load(string path, string pattern, SearchOption options) + { + var files = System.IO.Directory.GetFiles(path, pattern, options); + foreach (var item in files) + { + Load(item); + } + } + } +} diff --git a/src/References/Dapper.dll b/src/References/Dapper.dll new file mode 100644 index 0000000000000000000000000000000000000000..7b11f54fdc0fd0598fb0140a448d7f3ec2dea66d GIT binary patch literal 120320 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 literal 0 HcmV?d00001 diff --git a/src/References/MySql.Data.dll b/src/References/MySql.Data.dll new file mode 100644 index 0000000000000000000000000000000000000000..43158ae4a5db3ecfd8623bb3037fe1ba87ec94e4 GIT binary patch literal 431616 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 Yn_cS+yELUDL%F|+6=E|oJ5ha>1HEEDJ(E);Ze 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