From 082dcf1fa45a2ce8fab2fc74602277eb473c5eed Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Fri, 5 Feb 2021 18:20:38 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20FreeSql.Provider.Custo?= =?UTF-8?q?m=20=E8=87=AA=E5=AE=9A=E4=B9=89=E9=80=82=E9=85=8D=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E4=BB=BB=E4=BD=95=E6=95=B0=E6=8D=AE=E5=BA=93=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.DbContext/FreeSql.DbContext.xml | 9 - FreeSql.sln | 15 + FreeSql/DataType.cs | 8 +- FreeSql/FreeSql.xml | 189 +------- FreeSql/FreeSqlBuilder.cs | 5 + .../Curd/CustomDelete.cs | 26 + .../Curd/CustomInsert.cs | 113 +++++ .../Curd/CustomSelect.cs | 215 ++++++++ .../Curd/CustomUpdate.cs | 71 +++ .../FreeSql.Provider.Custom/CustomAdapter.cs | 173 +++++++ .../CustomAdo/CustomAdo.cs | 82 ++++ .../CustomCodeFirst.cs | 95 ++++ .../CustomExpression.cs | 458 ++++++++++++++++++ .../FreeSql.Provider.Custom/CustomProvider.cs | 91 ++++ .../FreeSql.Provider.Custom/CustomUtils.cs | 85 ++++ .../FreeSql.Provider.Custom.csproj | 37 ++ Providers/FreeSql.Provider.Custom/key.snk | Bin 0 -> 596 bytes Providers/FreeSql.Provider.Custom/readme.md | 30 ++ 18 files changed, 1509 insertions(+), 193 deletions(-) create mode 100644 Providers/FreeSql.Provider.Custom/Curd/CustomDelete.cs create mode 100644 Providers/FreeSql.Provider.Custom/Curd/CustomInsert.cs create mode 100644 Providers/FreeSql.Provider.Custom/Curd/CustomSelect.cs create mode 100644 Providers/FreeSql.Provider.Custom/Curd/CustomUpdate.cs create mode 100644 Providers/FreeSql.Provider.Custom/CustomAdapter.cs create mode 100644 Providers/FreeSql.Provider.Custom/CustomAdo/CustomAdo.cs create mode 100644 Providers/FreeSql.Provider.Custom/CustomCodeFirst.cs create mode 100644 Providers/FreeSql.Provider.Custom/CustomExpression.cs create mode 100644 Providers/FreeSql.Provider.Custom/CustomProvider.cs create mode 100644 Providers/FreeSql.Provider.Custom/CustomUtils.cs create mode 100644 Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj create mode 100644 Providers/FreeSql.Provider.Custom/key.snk create mode 100644 Providers/FreeSql.Provider.Custom/readme.md diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 27909b2e..02eb0609 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -512,14 +512,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.sln b/FreeSql.sln index fb6b29f9..7ba3a403 100644 --- a/FreeSql.sln +++ b/FreeSql.sln @@ -91,6 +91,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.KingbaseES EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Firebird", "Providers\FreeSql.Provider.Firebird\FreeSql.Provider.Firebird.csproj", "{101B11D2-7780-4E14-9B72-77F5D69B3DF9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Custom", "Providers\FreeSql.Provider.Custom\FreeSql.Provider.Custom.csproj", "{0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -557,6 +559,18 @@ Global {101B11D2-7780-4E14-9B72-77F5D69B3DF9}.Release|x64.Build.0 = Release|Any CPU {101B11D2-7780-4E14-9B72-77F5D69B3DF9}.Release|x86.ActiveCfg = Release|Any CPU {101B11D2-7780-4E14-9B72-77F5D69B3DF9}.Release|x86.Build.0 = Release|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Debug|x64.ActiveCfg = Debug|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Debug|x64.Build.0 = Debug|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Debug|x86.ActiveCfg = Debug|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Debug|x86.Build.0 = Debug|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|Any CPU.Build.0 = Release|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x64.ActiveCfg = Release|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x64.Build.0 = Release|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x86.ActiveCfg = Release|Any CPU + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -589,6 +603,7 @@ Global {3D2BD8EC-253A-437F-B4C8-74BC0D91429B} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {CDD6A896-F6DF-44CB-B430-06B383916EB0} = {2A381C57-2697-427B-9F10-55DA11FD02E4} {101B11D2-7780-4E14-9B72-77F5D69B3DF9} = {2A381C57-2697-427B-9F10-55DA11FD02E4} + {0DBAA21C-39B2-4AAD-A43D-88E67ED239D1} = {2A381C57-2697-427B-9F10-55DA11FD02E4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98} diff --git a/FreeSql/DataType.cs b/FreeSql/DataType.cs index e91745d9..fb574578 100644 --- a/FreeSql/DataType.cs +++ b/FreeSql/DataType.cs @@ -54,6 +54,12 @@ namespace FreeSql /// /// Firebird 是一个跨平台的关系数据库,能作为多用户环境下的数据库服务器运行,也提供嵌入式数据库的实现 /// - Firebird + Firebird, + + /// + /// 自定义适配器,访问任何数据库 + /// 注意:该类型不提供 DbFirst/CodeFirst 功能 + /// + Custom } } diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 0f9eda96..38d14c14 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -650,6 +650,12 @@ Firebird 是一个跨平台的关系数据库,能作为多用户环境下的数据库服务器运行,也提供嵌入式数据库的实现 + + + 自定义适配器,访问任何数据库 + 注意:该类型不提供 DbFirst/CodeFirst 功能 + + 获取 IDbConnection 对应的 IFreeSql 实例 @@ -3141,177 +3147,6 @@ - - - 测试数据库是否连接正确,本方法执行如下命令: - MySql/SqlServer/PostgreSQL/达梦/人大金仓/神通: SELECT 1 - Oracle: SELECT 1 FROM dual - - 命令超时设置(秒) - - true: 成功, false: 失败 - - - - 查询,若使用读写分离,查询【从库】条件cmdText.StartsWith("SELECT "),否则查询【主库】 - - - - - - - - - - 查询,ExecuteReaderAsync(dr => {}, "select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteArrayAsync("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteDataSetAsync("select * from user where age > @age; select 2", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 查询 - - - - - - - - - 查询,ExecuteDataTableAsync("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 在【主库】执行 - - - - - - - - - 在【主库】执行,ExecuteNonQueryAsync("delete from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 在【主库】执行 - - - - - - - - - 在【主库】执行,ExecuteScalarAsync("select 1 from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - - 执行SQL返回对象集合,QueryAsync<User>("select * from user where age > @age", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - - - 执行SQL返回对象集合,Query<User>("select * from user where age > @age; select * from address", new SqlParameter { ParameterName = "age", Value = 25 }) - - - - - - - - - - - - 执行SQL返回对象集合,Query<User, Address>("select * from user where age > @age; select * from address", new { age = 25 }) - 提示:parms 参数还可以传 Dictionary<string, object> - - - - - - - - 可自定义解析表达式 @@ -4145,12 +3980,6 @@ 超时 - - - 获取资源 - - - 使用完毕后,归还资源 @@ -4221,12 +4050,6 @@ 资源对象 - - - 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 - - 资源对象 - 归还对象给对象池的时候触发 diff --git a/FreeSql/FreeSqlBuilder.cs b/FreeSql/FreeSqlBuilder.cs index c6ce3ac3..2a15d347 100644 --- a/FreeSql/FreeSqlBuilder.cs +++ b/FreeSql/FreeSqlBuilder.cs @@ -256,6 +256,11 @@ namespace FreeSql if (type == null) throwNotFind("FreeSql.Provider.Firebird.dll", "FreeSql.Firebird.FirebirdProvider<>"); break; + case DataType.Custom: + type = Type.GetType("FreeSql.Custom.CustomProvider`1,FreeSql.Provider.Custom")?.MakeGenericType(typeof(TMark)); + if (type == null) throwNotFind("FreeSql.Provider.Custom.dll", "FreeSql.Custom.CustomProvider<>"); + break; + default: throw new Exception("未指定 UseConnectionString 或者 UseConnectionFactory"); } } diff --git a/Providers/FreeSql.Provider.Custom/Curd/CustomDelete.cs b/Providers/FreeSql.Provider.Custom/Curd/CustomDelete.cs new file mode 100644 index 00000000..a8bc014c --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/Curd/CustomDelete.cs @@ -0,0 +1,26 @@ +using FreeSql.Internal; +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FreeSql.Custom +{ + + class CustomDelete : Internal.CommonProvider.DeleteProvider + { + public CustomDelete(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) + : base(orm, commonUtils, commonExpression, dywhere) + { + } + + public override List ExecuteDeleted() => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); + +#if net40 +#else + public override Task> ExecuteDeletedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); +#endif + } +} diff --git a/Providers/FreeSql.Provider.Custom/Curd/CustomInsert.cs b/Providers/FreeSql.Provider.Custom/Curd/CustomInsert.cs new file mode 100644 index 00000000..e860209f --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/Curd/CustomInsert.cs @@ -0,0 +1,113 @@ +using FreeSql.Internal; +using FreeSql.Internal.ObjectPool; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FreeSql.Custom +{ + + class CustomInsert : Internal.CommonProvider.InsertProvider where T1 : class + { + CustomUtils _utils; + public CustomInsert(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) + : base(orm, commonUtils, commonExpression) + { + _utils = _commonUtils as CustomUtils; + } + + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchValuesLimit > 0 ? _batchValuesLimit : _utils.Adapter.InsertBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255); + public override long ExecuteIdentity() => base.SplitExecuteIdentity(_batchValuesLimit > 0 ? _batchValuesLimit : _utils.Adapter.InsertBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255); + public override List ExecuteInserted() => base.SplitExecuteInserted(_batchValuesLimit > 0 ? _batchValuesLimit : _utils.Adapter.InsertBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255); + + protected override long RawExecuteIdentity() + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + Object poolConn = null; + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, string.Concat(sql, $"; {_utils.Adapter.InsertAfterGetIdentitySql}"), _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + long ret = 0; + Exception exception = null; + try + { + var conn = _connection; + if (_transaction != null) conn = _transaction.Connection; + if (conn == null) + { + poolConn = _orm.Ado.MasterPool.Get(); + conn = poolConn.Value; + } + _orm.Ado.ExecuteNonQuery(conn, _transaction, CommandType.Text, sql, _commandTimeout, _params); + ret = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(conn, _transaction, CommandType.Text, $" {_utils.Adapter.InsertAfterGetIdentitySql}", _commandTimeout)), out var trylng) ? trylng : 0; + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + if (poolConn != null) + _orm.Ado.MasterPool.Return(poolConn); + + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + + protected override List RawExecuteInserted() => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); + +#if net40 +#else + public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : _utils.Adapter.InsertBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255, cancellationToken); + public override Task ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : _utils.Adapter.InsertBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255, cancellationToken); + public override Task> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : _utils.Adapter.InsertBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255, cancellationToken); + + async protected override Task RawExecuteIdentityAsync(CancellationToken cancellationToken = default) + { + var sql = this.ToSql(); + if (string.IsNullOrEmpty(sql)) return 0; + + Object poolConn = null; + var before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, string.Concat(sql, $"; {_utils.Adapter.InsertAfterGetIdentitySql}"), _params); + _orm.Aop.CurdBeforeHandler?.Invoke(this, before); + long ret = 0; + Exception exception = null; + try + { + var conn = _connection; + if (_transaction != null) conn = _transaction.Connection; + if (conn == null) + { + poolConn = _orm.Ado.MasterPool.Get(); + conn = poolConn.Value; + } + await _orm.Ado.ExecuteNonQueryAsync(conn, _transaction, CommandType.Text, sql, _commandTimeout, _params, cancellationToken); + ret = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(conn, _transaction, CommandType.Text, $" {_utils.Adapter.InsertAfterGetIdentitySql}", _commandTimeout, null, cancellationToken)), out var trylng) ? trylng : 0; + } + catch (Exception ex) + { + exception = ex; + throw ex; + } + finally + { + if (poolConn != null) + _orm.Ado.MasterPool.Return(poolConn); + + var after = new Aop.CurdAfterEventArgs(before, exception, ret); + _orm.Aop.CurdAfterHandler?.Invoke(this, after); + } + return ret; + } + protected override Task> RawExecuteInsertedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); +#endif + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Custom/Curd/CustomSelect.cs b/Providers/FreeSql.Provider.Custom/Curd/CustomSelect.cs new file mode 100644 index 00000000..41b9f7ca --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/Curd/CustomSelect.cs @@ -0,0 +1,215 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace FreeSql.Custom +{ + + class CustomSelect : FreeSql.Internal.CommonProvider.Select1Provider + { + internal static string ToSqlStatic(CommonUtils _commonUtils, CommonExpression _commonExpression, string _select, bool _distinct, string field, StringBuilder _join, StringBuilder _where, string _groupby, string _having, string _orderby, int _skip, int _limit, List _tables, List> tbUnions, Func _aliasRule, string _tosqlAppendContent, List _whereGlobalFilter, IFreeSql _orm) + { + var _utils = _commonUtils as CustomUtils; + if (_orm.CodeFirst.IsAutoSyncStructure) + _orm.CodeFirst.SyncStructure(_tables.Select(a => a.Table.Type).ToArray()); + + if (_whereGlobalFilter.Any()) + foreach (var tb in _tables.Where(a => a.Type != SelectTableInfoType.Parent)) + tb.Cascade = _commonExpression.GetWhereCascadeSql(tb, _whereGlobalFilter, true); + + var sb = new StringBuilder(); + var tbUnionsGt0 = tbUnions.Count > 1; + for (var tbUnionsIdx = 0; tbUnionsIdx < tbUnions.Count; tbUnionsIdx++) + { + if (tbUnionsIdx > 0) sb.Append("\r\n \r\nUNION ALL\r\n \r\n"); + if (tbUnionsGt0) sb.Append(_select).Append(" * from ("); + var tbUnion = tbUnions[tbUnionsIdx]; + + var sbnav = new StringBuilder(); + sb.Append(_select); + if (_distinct) sb.Append("DISTINCT "); + if (_limit > 0 && _utils.Adapter.SelectTopStyle == CustomAdapter.SelecTopStyle.Top) sb.Append("TOP ").Append(_skip + _limit).Append(" "); + sb.Append(field); + if (_skip > 0 && _utils.Adapter.SelectTopStyle == CustomAdapter.SelecTopStyle.Top) + throw new NotImplementedException("FreeSql.Provider.Custom 未实现 Skip/Offset 功能,如果需要分页请使用判断上一次 id"); + + sb.Append(" \r\nFROM "); + var tbsjoin = _tables.Where(a => a.Type != SelectTableInfoType.From).ToArray(); + var tbsfrom = _tables.Where(a => a.Type == SelectTableInfoType.From).ToArray(); + for (var a = 0; a < tbsfrom.Length; a++) + { + sb.Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[a].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[a].Table.Type, tbsfrom[a].Alias) ?? tbsfrom[a].Alias); + if (tbsjoin.Length > 0) + { + //如果存在 join 查询,则处理 from t1, t2 改为 from t1 inner join t2 on 1 = 1 + for (var b = 1; b < tbsfrom.Length; b++) + { + sb.Append(" \r\nLEFT JOIN ").Append(_commonUtils.QuoteSqlName(tbUnion[tbsfrom[b].Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tbsfrom[b].Table.Type, tbsfrom[b].Alias) ?? tbsfrom[b].Alias); + + if (string.IsNullOrEmpty(tbsfrom[b].NavigateCondition) && string.IsNullOrEmpty(tbsfrom[b].On) && string.IsNullOrEmpty(tbsfrom[b].Cascade)) sb.Append(" ON 1 = 1"); + else + { + var onSql = tbsfrom[b].NavigateCondition ?? tbsfrom[b].On; + sb.Append(" ON ").Append(onSql); + if (string.IsNullOrEmpty(tbsfrom[b].Cascade) == false) + { + if (string.IsNullOrEmpty(onSql)) sb.Append(tbsfrom[b].Cascade); + else sb.Append(" AND ").Append(tbsfrom[b].Cascade); + } + } + } + break; + } + else + { + if (!string.IsNullOrEmpty(tbsfrom[a].NavigateCondition)) sbnav.Append(" AND (").Append(tbsfrom[a].NavigateCondition).Append(")"); + if (!string.IsNullOrEmpty(tbsfrom[a].On)) sbnav.Append(" AND (").Append(tbsfrom[a].On).Append(")"); + if (a > 0 && !string.IsNullOrEmpty(tbsfrom[a].Cascade)) sbnav.Append(" AND ").Append(tbsfrom[a].Cascade); + } + if (a < tbsfrom.Length - 1) sb.Append(", "); + } + foreach (var tb in tbsjoin) + { + if (tb.Type == SelectTableInfoType.Parent) continue; + switch (tb.Type) + { + case SelectTableInfoType.LeftJoin: + sb.Append(" \r\nLEFT JOIN "); + break; + case SelectTableInfoType.InnerJoin: + sb.Append(" \r\nINNER JOIN "); + break; + case SelectTableInfoType.RightJoin: + sb.Append(" \r\nRIGHT JOIN "); + break; + } + sb.Append(_commonUtils.QuoteSqlName(tbUnion[tb.Table.Type])).Append(" ").Append(_aliasRule?.Invoke(tb.Table.Type, tb.Alias) ?? tb.Alias).Append(" ON ").Append(tb.On ?? tb.NavigateCondition); + if (!string.IsNullOrEmpty(tb.Cascade)) sb.Append(" AND ").Append(tb.Cascade); + if (!string.IsNullOrEmpty(tb.On) && !string.IsNullOrEmpty(tb.NavigateCondition)) sbnav.Append(" AND (").Append(tb.NavigateCondition).Append(")"); + } + if (_join.Length > 0) sb.Append(_join); + + sbnav.Append(_where); + if (!string.IsNullOrEmpty(_tables[0].Cascade)) + sbnav.Append(" AND ").Append(_tables[0].Cascade); + + if (sbnav.Length > 0) + { + sb.Append(" \r\nWHERE ").Append(sbnav.Remove(0, 5)); + } + if (string.IsNullOrEmpty(_groupby) == false) + { + sb.Append(_groupby); + if (string.IsNullOrEmpty(_having) == false) + sb.Append(" \r\nHAVING ").Append(_having.Substring(5)); + } + sb.Append(_orderby); + if (_limit > 0 && _utils.Adapter.SelectTopStyle == CustomAdapter.SelecTopStyle.Limit) sb.Append(" \r\nLIMIT ").Append(_limit); + if (_skip > 0 && _utils.Adapter.SelectTopStyle == CustomAdapter.SelecTopStyle.Limit) sb.Append(" \r\nOFFSET ").Append(_skip); + + sbnav.Clear(); + if (tbUnionsGt0) sb.Append(") ftb"); + } + return sb.Append(_tosqlAppendContent).ToString(); + } + + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override ISelect From(Expression, T2, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, ISelectFromExpression>> exp) { this.InternalFrom(exp); var ret = new CustomSelect(_orm, _commonUtils, _commonExpression, null); CustomSelect.CopyData(this, (Internal.CommonProvider.Select0Provider)ret, exp?.Parameters); return ret; } + public override string ToSql(string field = null) => ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select2Provider where T2 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select3Provider where T2 : class where T3 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select4Provider where T2 : class where T3 : class where T4 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select5Provider where T2 : class where T3 : class where T4 : class where T5 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select6Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select7Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select8Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select9Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select10Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + + class CustomSelect : FreeSql.Internal.CommonProvider.Select11Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select12Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select13Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select14Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select15Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } + class CustomSelect : FreeSql.Internal.CommonProvider.Select16Provider where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class where T11 : class where T12 : class where T13 : class where T14 : class where T15 : class where T16 : class + { + public CustomSelect(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) : base(orm, commonUtils, commonExpression, dywhere) { } + public override string ToSql(string field = null) => CustomSelect.ToSqlStatic(_commonUtils, _commonExpression, _select, _distinct, field ?? this.GetAllFieldExpressionTreeLevel2().Field, _join, _where, _groupby, _having, _orderby, _skip, _limit, _tables, this.GetTableRuleUnions(), _aliasRule, _tosqlAppendContent, _whereGlobalFilter, _orm); + } +} diff --git a/Providers/FreeSql.Provider.Custom/Curd/CustomUpdate.cs b/Providers/FreeSql.Provider.Custom/Curd/CustomUpdate.cs new file mode 100644 index 00000000..3d7add7c --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/Curd/CustomUpdate.cs @@ -0,0 +1,71 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FreeSql.Custom +{ + + class CustomUpdate : Internal.CommonProvider.UpdateProvider + { + CustomUtils _utils; + public CustomUpdate(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression, object dywhere) + : base(orm, commonUtils, commonExpression, dywhere) + { + _utils = _commonUtils as CustomUtils; + } + + public override int ExecuteAffrows() => base.SplitExecuteAffrows(_batchRowsLimit > 0 ? _batchRowsLimit : _utils.Adapter.UpdateBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(_batchRowsLimit > 0 ? _batchRowsLimit : _utils.Adapter.UpdateBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255); + + protected override List RawExecuteUpdated() => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能 未实现该功能"); + + protected override void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys) + { + if (_table.Primarys.Length == 1) + { + var pk = _table.Primarys.First(); + caseWhen.Append(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name))); + return; + } + caseWhen.Append("("); + var pkidx = 0; + foreach (var pk in _table.Primarys) + { + if (pkidx > 0) caseWhen.Append(" + '+' + "); + caseWhen.Append(_utils.Adapter.CastSql(_commonUtils.RereadColumn(pk, _commonUtils.QuoteSqlName(pk.Attribute.Name)), _utils.Adapter.MappingDbTypeVarChar)); + ++pkidx; + } + caseWhen.Append(")"); + } + + protected override void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d) + { + if (_table.Primarys.Length == 1) + { + sb.Append(_commonUtils.FormatSql("{0}", _table.Primarys[0].GetDbValue(d))); + return; + } + var pkidx = 0; + foreach (var pk in _table.Primarys) + { + if (pkidx > 0) sb.Append(" + '+' + "); + sb.Append(_utils.Adapter.CastSql(_commonUtils.FormatSql("{0}", pk.GetDbValue(d)), _utils.Adapter.MappingDbTypeVarChar)); + ++pkidx; + } + } + +#if net40 +#else + public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : _utils.Adapter.UpdateBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255, cancellationToken); + public override Task> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : _utils.Adapter.UpdateBatchSplitLimit, _batchParameterLimit > 0 ? _batchParameterLimit : 255, cancellationToken); + + protected override Task> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能 未实现该功能"); +#endif + } +} diff --git a/Providers/FreeSql.Provider.Custom/CustomAdapter.cs b/Providers/FreeSql.Provider.Custom/CustomAdapter.cs new file mode 100644 index 00000000..b32c2bfc --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/CustomAdapter.cs @@ -0,0 +1,173 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Text; + +namespace FreeSql.Custom +{ + public class CustomAdapter + { + /// + /// Select TOP 1,或 Limit 1 风格 + /// + public virtual SelecTopStyle SelectTopStyle => SelecTopStyle.Top; + public enum SelecTopStyle { Top, Limit } + + /// + /// 插入成功后,获取自增值 + /// + public virtual string InsertAfterGetIdentitySql => "SELECT SCOPE_IDENTITY()"; + /// + /// 批量插入时,自动拆分的每次执行数量 + /// + public virtual int InsertBatchSplitLimit => 255; + /// + /// 批量更新时,自动拆分的每次执行数量 + /// + public virtual int UpdateBatchSplitLimit => 255; + + public virtual string MappingDbTypeBit => "int"; + public virtual string MappingDbTypeSmallInt => "smallint"; + public virtual string MappingDbTypeInt => "int"; + public virtual string MappingDbTypeBigInt => "bigint"; + public virtual string MappingDbTypeTinyInt => "tinyint"; + public virtual string MappingDbTypeDecimal => "decimal"; + public virtual string MappingDbTypeDouble => "float"; + public virtual string MappingDbTypeReal => "real"; + public virtual string MappingDbTypeDateTime => "datetime"; + public virtual string MappingDbTypeVarBinary => "varbinary"; + public virtual string MappingDbTypeVarChar => "nvarchar"; + public virtual string MappingDbTypeChar => "char"; + public virtual string MappingDbTypeText => "nvarchar(max)"; + public virtual string MappingDbTypeUniqueIdentifier => "uniqueidentifier"; + + public virtual char QuoteSqlNameLeft => '['; + public virtual char QuoteSqlNameRight => ']'; + + public virtual string FieldSql(Type type, string columnName) => columnName; + public virtual string UnicodeStringRawSql(object value, ColumnInfo mapColumn) => value == null ? "NULL" : string.Concat("N'", value.ToString().Replace("'", "''"), "'"); + public virtual string DateTimeRawSql(object value) + { + if (value == null) return "NULL"; + if (value.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1); + return string.Concat("'", ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"), "'"); + } + public virtual string TimeSpanRawSql(object value) => value == null ? "NULL" : ((TimeSpan)value).TotalSeconds.ToString(); + public virtual string ByteRawSql(object value) + { + if (value == null) return "NULL"; + return $"0x{CommonUtils.BytesSqlRaw(value as byte[])}"; + } + + public virtual string CastSql(string sql, string to) => $"cast({sql} as {to})"; + public virtual string IsNullSql(string sql, object value) => $"isnull({sql}, {value})"; + public virtual string ConcatSql(string[] objs, Type[] types) + { + var sb = new StringBuilder(); + var news = new string[objs.Length]; + for (var a = 0; a < objs.Length; a++) + { + if (types[a] == typeof(string)) news[a] = objs[a]; + else if (types[a].NullableTypeOrThis() == typeof(Guid)) news[a] = $"cast({objs[a]} as char(36))"; + else news[a] = $"cast({objs[a]} as nvarchar)"; + } + return string.Join(" + ", news); + } + + public virtual string Mod(string left, string right, Type leftType, Type rightType) => $"{left} % {right}"; + public virtual string Div(string left, string right, Type leftType, Type rightType) => $"{left} / {right}"; + + public virtual string LambdaConvert_ToBoolean(Type type, string operand) => $"(cast({operand} as varchar) not in ('0','false'))"; + public virtual string LambdaConvert_ToByte(Type type, string operand) => $"cast({operand} as tinyint)"; + public virtual string LambdaConvert_ToChar(Type type, string operand) => $"substring(cast({operand} as varchar),1,1)"; + public virtual string LambdaConvert_ToDateTime(Type type, string operand) => $"cast({operand} as datetime)"; + public virtual string LambdaConvert_ToDecimal(Type type, string operand) => $"cast({operand} as decimal(36,18))"; + public virtual string LambdaConvert_ToDouble(Type type, string operand) => $"cast({operand} as decimal(32,16))"; + public virtual string LambdaConvert_ToInt16(Type type, string operand) => $"cast({operand} as smallint)"; + public virtual string LambdaConvert_ToInt32(Type type, string operand) => $"cast({operand} as int)"; + public virtual string LambdaConvert_ToInt64(Type type, string operand) => $"cast({operand} as bigint)"; + public virtual string LambdaConvert_ToSByte(Type type, string operand) => $"cast({operand} as tinyint)"; + public virtual string LambdaConvert_ToSingle(Type type, string operand) => $"cast({operand} as decimal(14,7))"; + public virtual string LambdaConvert_ToString(Type type, string operand) => type.NullableTypeOrThis() == typeof(Guid) ? $"cast({operand} as varchar(36))" : $"cast({operand} as nvarchar)"; + public virtual string LambdaConvert_ToUInt16(Type type, string operand) => $"cast({operand} as smallint)"; + public virtual string LambdaConvert_ToUInt32(Type type, string operand) => $"cast({operand} as int)"; + public virtual string LambdaConvert_ToUInt64(Type type, string operand) => $"cast({operand} as bigint)"; + public virtual string LambdaConvert_ToGuid(Type type, string operand) => $"cast({operand} as uniqueidentifier)"; + + public virtual string LambdaGuid_NewGuid => "newid()"; + public virtual string LambdaRandom_Next => "cast(rand()*1000000000 as int)"; + public virtual string LambdaRandom_NextDouble => "rand()"; + + public virtual string LambdaString_IsNullOrEmpty(string operand) => $"({operand} is null or {operand} = '')"; + public virtual string LambdaString_IsNullOrWhiteSpace(string operand) => $"({operand} is null or {operand} = '' or ltrim({operand}) = '')"; + public virtual string LambdaString_Length(string operand) => $"len({operand})"; + + public virtual string LambdaString_ToLower(string operand) => $"lower({operand})"; + public virtual string LambdaString_ToUpper(string operand) => $"upper({operand})"; + public virtual string LambdaString_Substring(string operand, string startIndex, string length) => string.IsNullOrEmpty(length) ? $"left({operand}, {startIndex})" : $"substring({operand}, {startIndex}, {length})"; + public virtual string LambdaString_IndexOf(string operand, string value, string startIndex) => string.IsNullOrEmpty(startIndex) ? $"(charindex({value}, {operand})-1)" : $"(charindex({value}, {operand}, {startIndex})-1)"; + public virtual string LambdaString_PadLeft(string operand, string length, string paddingChar) => string.IsNullOrEmpty(paddingChar) ? $"lpad({operand}, {length})" : $"lpad({operand}, {length}, {paddingChar})"; + public virtual string LambdaString_PadRight(string operand, string length, string paddingChar) => string.IsNullOrEmpty(paddingChar) ? $"rpad({operand}, {length})" : $"rpad({operand}, {length}, {paddingChar})"; + public virtual string LambdaString_Trim(string operand) => $"ltrim(rtrim({operand}))"; + public virtual string LambdaString_TrimStart(string operand) => $"ltrim({operand})"; + public virtual string LambdaString_TrimEnd(string operand) => $"rtrim({operand})"; + public virtual string LambdaString_Replace(string operand, string oldValue, string newValue) => $"replace({operand}, {oldValue}, {newValue})"; + public virtual string LambdaString_CompareTo(string operand, string value) => $"({operand} - {value})"; + public virtual string LambdaString_Equals(string operand, string value) => $"({operand} = {value})"; + + public virtual string LambdaDateTime_Now => "getdate()"; + public virtual string LambdaDateTime_UtcNow => "getutcdate()"; + public virtual string LambdaDateTime_Today => "convert(char(10),getdate(),120)"; + public virtual string LambdaDateTime_MinValue => "'1753/1/1 0:00:00'"; + public virtual string LambdaDateTime_MaxValue => "'9999/12/31 23:59:59'"; + public virtual string LambdaDateTime_Date(string operand) => $"convert(char(10),{operand},120)"; + public virtual string LambdaDateTime_TimeOfDay(string operand) => $"datediff(second, convert(char(10),{operand},120), {operand})"; + public virtual string LambdaDateTime_DayOfWeek(string operand) => $"(datepart(weekday, {operand})-1)"; + public virtual string LambdaDateTime_Day(string operand) => $"datepart(day, {operand})"; + public virtual string LambdaDateTime_DayOfYear(string operand) => $"datepart(dayofyear, {operand})"; + public virtual string LambdaDateTime_Month(string operand) => $"datepart(month, {operand})"; + public virtual string LambdaDateTime_Year(string operand) => $"datepart(year, {operand})"; + public virtual string LambdaDateTime_Hour(string operand) => $"datepart(hour, {operand})"; + public virtual string LambdaDateTime_Minute(string operand) => $"datepart(minute, {operand})"; + public virtual string LambdaDateTime_Second(string operand) => $"datepart(second, {operand})"; + public virtual string LambdaDateTime_Millisecond(string operand) => $"(datepart(millisecond, {operand})/1000)"; + public virtual string LambdaDateTime_Ticks(string operand) => $"(cast(datediff(second, '1970-1-1', {operand}) as bigint)*10000000+621355968000000000)"; + + public virtual string LambdaDateTime_DaysInMonth(string year, string month) => $"datepart(day, dateadd(day, -1, dateadd(month, 1, cast({year} as varchar) + '-' + cast({month} as varchar) + '-1')))"; + public virtual string LambdaDateTime_IsLeapYear(string year) => $"(({year})%4=0 AND ({year})%100<>0 OR ({year})%400=0)"; + public virtual string LambdaDateTime_Add(string operand, string value) => $"dateadd(second, {value}, {operand})"; + public virtual string LambdaDateTime_AddDays(string operand, string value) => $"dateadd(day, {value}, {operand})"; + public virtual string LambdaDateTime_AddHours(string operand, string value) => $"dateadd(hour, {value}, {operand})"; + public virtual string LambdaDateTime_AddMilliseconds(string operand, string value) => $"dateadd(second, ({value})/1000, {operand})"; + public virtual string LambdaDateTime_AddMinutes(string operand, string value) => $"dateadd(minute, {value}, {operand})"; + public virtual string LambdaDateTime_AddMonths(string operand, string value) => $"dateadd(month, {value}, {operand})"; + public virtual string LambdaDateTime_AddSeconds(string operand, string value) => $"dateadd(second, {value}, {operand})"; + public virtual string LambdaDateTime_AddTicks(string operand, string value) => $"dateadd(second, ({value})/10000000, {operand})"; + public virtual string LambdaDateTime_AddYears(string operand, string value) => $"dateadd(year, {value}, {operand})"; + public virtual string LambdaDateTime_Subtract(string operand, string value) => $"datediff(second, {value}, {operand})"; + public virtual string LambdaDateTime_SubtractTimeSpan(string operand, string value) => $"dateadd(second, ({value})*-1, {operand})"; + public virtual string LambdaDateTime_Equals(string operand, string value) => $"({operand} = {value})"; + public virtual string LambdaDateTime_CompareTo(string operand, string value) => $"datediff(second,{value},{operand})"; + public virtual string LambdaDateTime_ToString(string operand) => $"convert(varchar, {operand}, 121)"; + + public virtual string LambdaMath_Abs(string operand) => $"abs({operand})"; + public virtual string LambdaMath_Sign(string operand) => $"sign({operand})"; + public virtual string LambdaMath_Floor(string operand) => $"floor({operand})"; + public virtual string LambdaMath_Ceiling(string operand) => $"ceiling({ operand})"; + public virtual string LambdaMath_Round(string operand, string decimals) => $"round({operand}, {decimals})"; + public virtual string LambdaMath_Exp(string operand) => $"exp({operand})"; + public virtual string LambdaMath_Log(string operand) => $"log({operand})"; + public virtual string LambdaMath_Log10(string operand) => $"log10({operand})"; + public virtual string LambdaMath_Pow(string operand, string y) => $"power({operand}, {y})"; + public virtual string LambdaMath_Sqrt(string operand) => $"sqrt({operand})"; + public virtual string LambdaMath_Cos(string operand) => $"cos({operand})"; + public virtual string LambdaMath_Sin(string operand) => $"sin({operand})"; + public virtual string LambdaMath_Tan(string operand) => $"tan({operand})"; + public virtual string LambdaMath_Acos(string operand) => $"acos({operand})"; + public virtual string LambdaMath_Asin(string operand) => $"asin({operand})"; + public virtual string LambdaMath_Atan(string operand) => $"atan({operand})"; + public virtual string LambdaMath_Atan2(string operand, string x) => $"atan2({operand}, {x})"; + public virtual string LambdaMath_Truncate(string operand) => $"floor({operand})"; + } +} diff --git a/Providers/FreeSql.Provider.Custom/CustomAdo/CustomAdo.cs b/Providers/FreeSql.Provider.Custom/CustomAdo/CustomAdo.cs new file mode 100644 index 00000000..bb661ecd --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/CustomAdo/CustomAdo.cs @@ -0,0 +1,82 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using FreeSql.Internal.ObjectPool; +using System; +using System.Collections; +using System.Data.Common; +using System.Text; +using System.Threading; + +namespace FreeSql.Custom +{ + class CustomAdo : FreeSql.Internal.CommonProvider.AdoProvider + { + public CustomAdo() : base(DataType.Custom, null, null) { } + public CustomAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func connectionFactory) : base(DataType.Custom, masterConnectionString, slaveConnectionStrings) + { + base._util = util; + if (connectionFactory != null) + { + var pool = new FreeSql.Internal.CommonProvider.DbConnectionPool(DataType.SqlServer, connectionFactory); + MasterPool = pool; + _CreateCommandConnection = pool.TestConnection; + _CreateParameterCommand = CreateCommand(); + return; + } + throw new Exception("FreeSql.Provider.CustomAdapter 仅支持 UseConnectionFactory 方式构建 IFreeSql"); + } + CustomAdapter Adapter => (_util == null ? FreeSqlCustomAdapterGlobalExtensions.DefaultAdapter : _util._orm.GetCustomAdapter()); + + public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn) + { + if (param == null) return "NULL"; + if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false)) + param = Utils.GetDataReaderValue(mapType, param); + + if (param is bool || param is bool?) + return (bool)param ? 1 : 0; + else if (param is string) + return Adapter.UnicodeStringRawSql(param, mapColumn); + else if (param is char) + return string.Concat("'", param.ToString().Replace("'", "''").Replace('\0', ' '), "'"); + else if (param is Enum) + return ((Enum)param).ToInt64(); + else if (decimal.TryParse(string.Concat(param), out var trydec)) + return param; + else if (param is DateTime || param is DateTime?) + return Adapter.DateTimeRawSql(param); + else if (param is TimeSpan || param is TimeSpan?) + return Adapter.TimeSpanRawSql(param); + else if (param is byte[]) + return Adapter.ByteRawSql(param as byte[]); + else if (param is IEnumerable) + return AddslashesIEnumerable(param, mapType, mapColumn); + + return string.Concat("'", param.ToString().Replace("'", "''"), "'"); + } + + DbConnection _CreateCommandConnection; + DbCommand _CreateParameterCommand; + public override DbCommand CreateCommand() + { + if (_CreateCommandConnection != null) + { + var cmd = _CreateCommandConnection.CreateCommand(); + cmd.Connection = null; + return cmd; + } + throw new Exception("FreeSql.Provider.CustomAdapter 无法使用 CreateCommand"); + } + public DbParameter CreateParameter() + { + return _CreateParameterCommand.CreateParameter(); + } + + public override void ReturnConnection(IObjectPool pool, Object conn, Exception ex) + { + pool.Return(conn); + } + + public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => _util.GetDbParamtersByObject(sql, obj); + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Custom/CustomCodeFirst.cs b/Providers/FreeSql.Provider.Custom/CustomCodeFirst.cs new file mode 100644 index 00000000..c84e1641 --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/CustomCodeFirst.cs @@ -0,0 +1,95 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace FreeSql.Custom +{ + + class CustomCodeFirst : Internal.CommonProvider.CodeFirstProvider + { + public override bool IsAutoSyncStructure { get => false; set => base.IsAutoSyncStructure = false; } + public override bool IsNoneCommandParameter { get => true; set => base.IsNoneCommandParameter = true; } + public CustomCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression) + { + _utils = _commonUtils as CustomUtils; + } + + CustomUtils _utils; + object _dicCsToDbLock = new object(); + Dictionary> _dicCsToDb; + + public override DbInfoResult GetDbInfo(Type type) + { + if (_dicCsToDb == null) + { + lock (_dicCsToDbLock) + { + if (_dicCsToDb == null) + { + var reg = new Regex(@"\([^\)]+\)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + Func deleteBrackets = str => reg.Replace(str, ""); + + _dicCsToDb = new Dictionary>() { + { typeof(bool).FullName, CsToDb.New(DbType.Boolean, _utils.Adapter.MappingDbTypeBit,$"{_utils.Adapter.MappingDbTypeBit} NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(DbType.Boolean, _utils.Adapter.MappingDbTypeBit,_utils.Adapter.MappingDbTypeBit, null, true, null) }, + + { typeof(sbyte).FullName, CsToDb.New(DbType.SByte, _utils.Adapter.MappingDbTypeSmallInt, $"{_utils.Adapter.MappingDbTypeSmallInt} NOT NULL", false, false, 0) },{ typeof(sbyte?).FullName, CsToDb.New(DbType.SByte, _utils.Adapter.MappingDbTypeSmallInt, _utils.Adapter.MappingDbTypeSmallInt, false, true, null) }, + { typeof(short).FullName, CsToDb.New(DbType.Int16, _utils.Adapter.MappingDbTypeSmallInt,$"{_utils.Adapter.MappingDbTypeSmallInt} NOT NULL", false, false, 0) },{ typeof(short?).FullName, CsToDb.New(DbType.Int16, _utils.Adapter.MappingDbTypeSmallInt, _utils.Adapter.MappingDbTypeSmallInt, false, true, null) }, + { typeof(int).FullName, CsToDb.New(DbType.Int32, _utils.Adapter.MappingDbTypeInt, $"{_utils.Adapter.MappingDbTypeInt} NOT NULL", false, false, 0) },{ typeof(int?).FullName, CsToDb.New(DbType.Int32, _utils.Adapter.MappingDbTypeInt, _utils.Adapter.MappingDbTypeInt, false, true, null) }, + { typeof(long).FullName, CsToDb.New(DbType.Int64, _utils.Adapter.MappingDbTypeBigInt,$"{_utils.Adapter.MappingDbTypeBigInt} NOT NULL", false, false, 0) },{ typeof(long?).FullName, CsToDb.New(DbType.Int64, _utils.Adapter.MappingDbTypeBigInt,_utils.Adapter.MappingDbTypeBigInt, false, true, null) }, + + { typeof(byte).FullName, CsToDb.New(DbType.Byte, _utils.Adapter.MappingDbTypeTinyInt,$"{_utils.Adapter.MappingDbTypeTinyInt} NOT NULL", true, false, 0) },{ typeof(byte?).FullName, CsToDb.New(DbType.Byte, _utils.Adapter.MappingDbTypeTinyInt,_utils.Adapter.MappingDbTypeTinyInt, true, true, null) }, + { typeof(ushort).FullName, CsToDb.New(DbType.UInt16, _utils.Adapter.MappingDbTypeInt,$"{_utils.Adapter.MappingDbTypeInt} NOT NULL", true, false, 0) },{ typeof(ushort?).FullName, CsToDb.New(DbType.UInt16, _utils.Adapter.MappingDbTypeInt, _utils.Adapter.MappingDbTypeInt, true, true, null) }, + { typeof(uint).FullName, CsToDb.New(DbType.UInt32, _utils.Adapter.MappingDbTypeBigInt, $"{_utils.Adapter.MappingDbTypeBigInt} NOT NULL", true, false, 0) },{ typeof(uint?).FullName, CsToDb.New(DbType.UInt32, _utils.Adapter.MappingDbTypeBigInt, _utils.Adapter.MappingDbTypeBigInt, true, true, null) }, + { typeof(ulong).FullName, CsToDb.New(DbType.UInt64, _utils.Adapter.MappingDbTypeDecimal, $"{_utils.Adapter.MappingDbTypeDecimal}(20,0) NOT NULL", true, false, 0) },{ typeof(ulong?).FullName, CsToDb.New(DbType.UInt64, _utils.Adapter.MappingDbTypeDecimal, $"{_utils.Adapter.MappingDbTypeDecimal}(20,0)", true, true, null) }, + + { typeof(double).FullName, CsToDb.New(DbType.Double, _utils.Adapter.MappingDbTypeDouble, $"{_utils.Adapter.MappingDbTypeDouble} NOT NULL", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, _utils.Adapter.MappingDbTypeDouble, _utils.Adapter.MappingDbTypeDouble, false, true, null) }, + { typeof(float).FullName, CsToDb.New(DbType.Single, _utils.Adapter.MappingDbTypeReal,$"{_utils.Adapter.MappingDbTypeReal} NOT NULL", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, _utils.Adapter.MappingDbTypeReal,_utils.Adapter.MappingDbTypeReal, false, true, null) }, + { typeof(decimal).FullName, CsToDb.New(DbType.Decimal, _utils.Adapter.MappingDbTypeDecimal, $"{_utils.Adapter.MappingDbTypeDecimal}(10,2) NOT NULL", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, _utils.Adapter.MappingDbTypeDecimal, $"{_utils.Adapter.MappingDbTypeDecimal}(10,2)", false, true, null) }, + + { typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, _utils.Adapter.MappingDbTypeDateTime, $"{_utils.Adapter.MappingDbTypeDateTime} NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, _utils.Adapter.MappingDbTypeDateTime, _utils.Adapter.MappingDbTypeDateTime, false, true, null) }, + + { typeof(byte[]).FullName, CsToDb.New(DbType.Binary, _utils.Adapter.MappingDbTypeVarBinary, $"{_utils.Adapter.MappingDbTypeVarBinary}(255)", false, null, new byte[0]) }, + { typeof(string).FullName, CsToDb.New(DbType.String, _utils.Adapter.MappingDbTypeVarChar, $"{_utils.Adapter.MappingDbTypeVarChar}(255)", false, null, "") }, + { typeof(char).FullName, CsToDb.New(DbType.AnsiStringFixedLength, _utils.Adapter.MappingDbTypeChar, $"{_utils.Adapter.MappingDbTypeChar}(1)", false, null, '\0') }, + + { typeof(Guid).FullName, CsToDb.New(DbType.Guid, deleteBrackets(_utils.Adapter.MappingDbTypeUniqueIdentifier), $"{_utils.Adapter.MappingDbTypeUniqueIdentifier} NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(DbType.Guid, deleteBrackets(_utils.Adapter.MappingDbTypeUniqueIdentifier), _utils.Adapter.MappingDbTypeUniqueIdentifier, false, true, null) }, + }; + } + } + } + + if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue); + if (type.IsArray) return null; + var enumType = type.IsEnum ? type : null; + if (enumType == null && type.IsNullableType()) + { + var genericTypes = type.GetGenericArguments(); + if (genericTypes.Length == 1 && genericTypes.First().IsEnum) enumType = genericTypes.First(); + } + if (enumType != null) + { + var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ? + CsToDb.New(DbType.Int64, _utils.Adapter.MappingDbTypeBigInt, $"{_utils.Adapter.MappingDbTypeBigInt}{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()) : + CsToDb.New(DbType.Int32, _utils.Adapter.MappingDbTypeInt, $"{_utils.Adapter.MappingDbTypeInt}{(type.IsEnum ? " NOT NULL" : "")}", false, type.IsEnum ? false : true, enumType.CreateInstanceGetDefaultValue()); + if (_dicCsToDb.ContainsKey(type.FullName) == false) + { + lock (_dicCsToDbLock) + { + if (_dicCsToDb.ContainsKey(type.FullName) == false) + _dicCsToDb.Add(type.FullName, newItem); + } + } + return new DbInfoResult((int)newItem.type, newItem.dbtype, newItem.dbtypeFull, newItem.isnullable, newItem.defaultValue); + } + return null; + } + + protected override string GetComparisonDDLStatements(params TypeAndName[] objects) => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); + } +} \ No newline at end of file diff --git a/Providers/FreeSql.Provider.Custom/CustomExpression.cs b/Providers/FreeSql.Provider.Custom/CustomExpression.cs new file mode 100644 index 00000000..6919fcc5 --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/CustomExpression.cs @@ -0,0 +1,458 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace FreeSql.Custom +{ + class CustomExpression : CommonExpression + { + CustomUtils _utils; + public CustomExpression(CommonUtils common) : base(common) + { + _utils = common as CustomUtils; + } + + public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + switch (exp.NodeType) + { + case ExpressionType.Convert: + var operandExp = (exp as UnaryExpression)?.Operand; + var gentype = exp.Type.NullableTypeOrThis(); + if (gentype != operandExp.Type.NullableTypeOrThis()) + { + switch (gentype.ToString()) + { + case "System.Boolean": return _utils.Adapter.LambdaConvert_ToBoolean(operandExp.Type, getExp(operandExp)); + case "System.Byte": return _utils.Adapter.LambdaConvert_ToByte(operandExp.Type, getExp(operandExp)); + case "System.Char": return _utils.Adapter.LambdaConvert_ToChar(operandExp.Type, getExp(operandExp)); + case "System.DateTime": return _utils.Adapter.LambdaConvert_ToDateTime(operandExp.Type, getExp(operandExp)); + case "System.Decimal": return _utils.Adapter.LambdaConvert_ToDecimal(operandExp.Type, getExp(operandExp)); + case "System.Double": return _utils.Adapter.LambdaConvert_ToDouble(operandExp.Type, getExp(operandExp)); + case "System.Int16": return _utils.Adapter.LambdaConvert_ToInt16(operandExp.Type, getExp(operandExp)); + case "System.Int32": return _utils.Adapter.LambdaConvert_ToInt32(operandExp.Type, getExp(operandExp)); + case "System.Int64": return _utils.Adapter.LambdaConvert_ToInt64(operandExp.Type, getExp(operandExp)); + case "System.SByte": return _utils.Adapter.LambdaConvert_ToSByte(operandExp.Type, getExp(operandExp)); + case "System.Single": return _utils.Adapter.LambdaConvert_ToSingle(operandExp.Type, getExp(operandExp)); + case "System.String": return _utils.Adapter.LambdaConvert_ToString(operandExp.Type, getExp(operandExp)); + case "System.UInt16": return _utils.Adapter.LambdaConvert_ToUInt16(operandExp.Type, getExp(operandExp)); + case "System.UInt32": return _utils.Adapter.LambdaConvert_ToUInt32(operandExp.Type, getExp(operandExp)); + case "System.UInt64": return _utils.Adapter.LambdaConvert_ToUInt64(operandExp.Type, getExp(operandExp)); + case "System.Guid": return _utils.Adapter.LambdaConvert_ToGuid(operandExp.Type, getExp(operandExp)); + } + } + break; + case ExpressionType.Call: + var callExp = exp as MethodCallExpression; + + switch (callExp.Method.Name) + { + case "Parse": + case "TryParse": + switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) + { + case "System.Boolean": return _utils.Adapter.LambdaConvert_ToBoolean(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Byte": return _utils.Adapter.LambdaConvert_ToByte(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Char": return _utils.Adapter.LambdaConvert_ToChar(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.DateTime": return _utils.Adapter.LambdaConvert_ToDateTime(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Decimal": return _utils.Adapter.LambdaConvert_ToDecimal(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Double": return _utils.Adapter.LambdaConvert_ToDouble(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Int16": return _utils.Adapter.LambdaConvert_ToInt16(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Int32": return _utils.Adapter.LambdaConvert_ToInt32(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Int64": return _utils.Adapter.LambdaConvert_ToInt64(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.SByte": return _utils.Adapter.LambdaConvert_ToSByte(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Single": return _utils.Adapter.LambdaConvert_ToSingle(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.UInt16": return _utils.Adapter.LambdaConvert_ToUInt16(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.UInt32": return _utils.Adapter.LambdaConvert_ToUInt32(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.UInt64": return _utils.Adapter.LambdaConvert_ToUInt64(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + case "System.Guid": return _utils.Adapter.LambdaConvert_ToGuid(callExp.Method.DeclaringType, getExp(callExp.Arguments[0])); + } + return null; + case "NewGuid": + switch (callExp.Method.DeclaringType.NullableTypeOrThis().ToString()) + { + case "System.Guid": return _utils.Adapter.LambdaGuid_NewGuid; + } + return null; + case "Next": + if (callExp.Object?.Type == typeof(Random)) return _utils.Adapter.LambdaRandom_Next; + return null; + case "NextDouble": + if (callExp.Object?.Type == typeof(Random)) return _utils.Adapter.LambdaRandom_NextDouble; + return null; + case "Random": + if (callExp.Method.DeclaringType.IsNumberType()) return _utils.Adapter.LambdaRandom_NextDouble; + return null; + case "ToString": + if (callExp.Object != null) return callExp.Arguments.Count == 0 ? _utils.Adapter.LambdaConvert_ToString(callExp.Object.Type, getExp(callExp.Object)) : null; + return null; + } + + var objExp = callExp.Object; + var objType = objExp?.Type; + if (objType?.FullName == "System.Byte[]") return null; + + var argIndex = 0; + if (objType == null && callExp.Method.DeclaringType == typeof(Enumerable)) + { + objExp = callExp.Arguments.FirstOrDefault(); + objType = objExp?.Type; + argIndex++; + + if (objType == typeof(string)) + { + switch (callExp.Method.Name) + { + case "First": + case "FirstOrDefault": + return _utils.Adapter.LambdaString_Substring(getExp(callExp.Arguments[0]), "1", "1"); + } + } + } + if (objType == null) objType = callExp.Method.DeclaringType; + if (objType != null || objType.IsArrayOrList()) + { + if (argIndex >= callExp.Arguments.Count) break; + tsc.SetMapColumnTmp(null); + var args1 = getExp(callExp.Arguments[argIndex]); + var oldMapType = tsc.SetMapTypeReturnOld(tsc.mapTypeTmp); + var oldDbParams = tsc.SetDbParamsReturnOld(null); + var left = objExp == null ? null : getExp(objExp); + tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType); + tsc.SetDbParamsReturnOld(oldDbParams); + switch (callExp.Method.Name) + { + case "Contains": + //判断 in //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格 + return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})"; + } + } + break; + case ExpressionType.NewArrayInit: + var arrExp = exp as NewArrayExpression; + var arrSb = new StringBuilder(); + arrSb.Append("("); + for (var a = 0; a < arrExp.Expressions.Count; a++) + { + if (a > 0) arrSb.Append(","); + arrSb.Append(getExp(arrExp.Expressions[a])); + } + if (arrSb.Length == 1) arrSb.Append("NULL"); + return arrSb.Append(")").ToString(); + case ExpressionType.ListInit: + var listExp = exp as ListInitExpression; + var listSb = new StringBuilder(); + listSb.Append("("); + for (var a = 0; a < listExp.Initializers.Count; a++) + { + if (listExp.Initializers[a].Arguments.Any() == false) continue; + if (a > 0) listSb.Append(","); + listSb.Append(getExp(listExp.Initializers[a].Arguments.FirstOrDefault())); + } + if (listSb.Length == 1) listSb.Append("NULL"); + return listSb.Append(")").ToString(); + case ExpressionType.New: + var newExp = exp as NewExpression; + if (typeof(IList).IsAssignableFrom(newExp.Type)) + { + if (newExp.Arguments.Count == 0) return "(NULL)"; + if (typeof(IEnumerable).IsAssignableFrom(newExp.Arguments[0].Type) == false) return "(NULL)"; + return getExp(newExp.Arguments[0]); + } + return null; + } + return null; + } + + public override string ExpressionLambdaToSqlMemberAccessString(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Empty": return "''"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Length": return _utils.Adapter.LambdaString_Length(left); + } + return null; + } + public override string ExpressionLambdaToSqlMemberAccessDateTime(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Now": return _utils.Adapter.LambdaDateTime_Now; + case "UtcNow": return _utils.Adapter.LambdaDateTime_UtcNow; + case "Today": return _utils.Adapter.LambdaDateTime_Today; + case "MinValue": return _utils.Adapter.LambdaDateTime_MinValue; + case "MaxValue": return _utils.Adapter.LambdaDateTime_MaxValue; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Date": return _utils.Adapter.LambdaDateTime_Date(left); + case "TimeOfDay": return _utils.Adapter.LambdaDateTime_TimeOfDay(left); + case "DayOfWeek": return _utils.Adapter.LambdaDateTime_DayOfWeek(left); + case "Day": return _utils.Adapter.LambdaDateTime_Day(left); + case "DayOfYear": return _utils.Adapter.LambdaDateTime_DayOfYear(left); + case "Month": return _utils.Adapter.LambdaDateTime_Month(left); + case "Year": return _utils.Adapter.LambdaDateTime_Year(left); + case "Hour": return _utils.Adapter.LambdaDateTime_Hour(left); + case "Minute": return _utils.Adapter.LambdaDateTime_Minute(left); + case "Second": return _utils.Adapter.LambdaDateTime_Second(left); + case "Millisecond": return _utils.Adapter.LambdaDateTime_Millisecond(left); + case "Ticks": return _utils.Adapter.LambdaDateTime_Ticks(left); + } + return null; + } + + public override string ExpressionLambdaToSqlMemberAccessTimeSpan(MemberExpression exp, ExpTSC tsc) + { + if (exp.Expression == null) + { + switch (exp.Member.Name) + { + case "Zero": return "0"; + case "MinValue": return "-922337203685477580"; //微秒 Ticks / 10 + case "MaxValue": return "922337203685477580"; + } + return null; + } + var left = ExpressionLambdaToSql(exp.Expression, tsc); + switch (exp.Member.Name) + { + case "Days": return _utils.Adapter.LambdaMath_Floor($"({left})/{60 * 60 * 24}"); + case "Hours": return _utils.Adapter.LambdaMath_Floor($"({left})/{60 * 60}%24"); + case "Milliseconds": return $"({_utils.Adapter.CastSql(left, _utils.Adapter.MappingDbTypeBigInt)}*1000)"; + case "Minutes": return _utils.Adapter.LambdaMath_Floor($"({left})/60%60"); + case "Seconds": return $"(({left})%60)"; + case "Ticks": return $"({_utils.Adapter.CastSql(left, _utils.Adapter.MappingDbTypeBigInt)}*10000000)"; + case "TotalDays": return $"(({left})/{60 * 60 * 24})"; + case "TotalHours": return $"(({left})/{60 * 60})"; + case "TotalMilliseconds": return $"({_utils.Adapter.CastSql(left, _utils.Adapter.MappingDbTypeBigInt)}*1000)"; + case "TotalMinutes": return $"(({left})/60)"; + case "TotalSeconds": return $"({left})"; + } + return null; + } + + public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "IsNullOrEmpty": return _utils.Adapter.LambdaString_IsNullOrEmpty(getExp(exp.Arguments[0])); + case "IsNullOrWhiteSpace": return _utils.Adapter.LambdaString_IsNullOrWhiteSpace(getExp(exp.Arguments[0])); + case "Concat": return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), exp.Arguments.Select(a => a.Type).ToArray()); + } + } + else + { + var left = getExp(exp.Object); + switch (exp.Method.Name) + { + case "StartsWith": + case "EndsWith": + case "Contains": + var args0Value = getExp(exp.Arguments[0]); + if (args0Value == "NULL") return $"({left}) IS NULL"; + if (exp.Method.Name == "StartsWith") return $"({left}) LIKE {(args0Value.EndsWith("'") ? args0Value.Insert(args0Value.Length - 1, "%") : $"({_common.StringConcat(new string[] { args0Value, "'%'" }, new[] { typeof(int), typeof(string) })})")}"; + if (exp.Method.Name == "EndsWith") return $"({left}) LIKE {(args0Value.StartsWith("'") ? args0Value.Insert(1, "%") : $"({_common.StringConcat(new string[] { "'%'", args0Value }, new[] { typeof(string), typeof(int) })})")}"; + if (args0Value.StartsWith("'") && args0Value.EndsWith("'")) return $"({left}) LIKE {args0Value.Insert(1, "%").Insert(args0Value.Length, "%")}"; + return $"({left}) LIKE ({_common.StringConcat(new string[] { "'%'", args0Value, "'%'" }, new[] { typeof(string), typeof(int), typeof(string)})})"; + case "ToLower": return _utils.Adapter.LambdaString_ToLower(left); + case "ToUpper": return _utils.Adapter.LambdaString_ToUpper(left); + case "Substring": + var substrArgs1 = getExp(exp.Arguments[0]); + if (long.TryParse(substrArgs1, out var testtrylng1)) substrArgs1 = (testtrylng1 + 1).ToString(); + else substrArgs1 += "+1"; + if (exp.Arguments.Count == 1) return _utils.Adapter.LambdaString_Substring(left, substrArgs1, null); + return _utils.Adapter.LambdaString_Substring(left, substrArgs1, getExp(exp.Arguments[1])); + case "IndexOf": + var indexOfFindStr = getExp(exp.Arguments[0]); + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") + { + var locateArgs1 = getExp(exp.Arguments[1]); + if (long.TryParse(locateArgs1, out var testtrylng2)) locateArgs1 = (testtrylng2 + 1).ToString(); + else locateArgs1 += "+1"; + return _utils.Adapter.LambdaString_IndexOf(left, indexOfFindStr, locateArgs1); + } + return _utils.Adapter.LambdaString_IndexOf(left, indexOfFindStr, null); + case "PadLeft": + if (exp.Arguments.Count == 1) return _utils.Adapter.LambdaString_PadLeft(left, getExp(exp.Arguments[0]), null); + return _utils.Adapter.LambdaString_PadLeft(left, getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "PadRight": + if (exp.Arguments.Count == 1) return _utils.Adapter.LambdaString_PadRight(left, getExp(exp.Arguments[0]), null); + return _utils.Adapter.LambdaString_PadRight(left, getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "Trim": return _utils.Adapter.LambdaString_Trim(left); + case "TrimStart": return _utils.Adapter.LambdaString_TrimStart(left); + case "TrimEnd": return _utils.Adapter.LambdaString_TrimEnd(left); + case "Replace": return _utils.Adapter.LambdaString_Replace(left, getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "CompareTo": return _utils.Adapter.LambdaString_CompareTo(left, getExp(exp.Arguments[0])); + case "Equals": return _utils.Adapter.LambdaString_Equals(left, getExp(exp.Arguments[0])); + } + } + return null; + } + public override string ExpressionLambdaToSqlCallMath(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + switch (exp.Method.Name) + { + case "Abs": return _utils.Adapter.LambdaMath_Abs(getExp(exp.Arguments[0])); + case "Sign": return _utils.Adapter.LambdaMath_Sign(getExp(exp.Arguments[0])); + case "Floor": return _utils.Adapter.LambdaMath_Floor(getExp(exp.Arguments[0])); + case "Ceiling": return _utils.Adapter.LambdaMath_Ceiling(getExp(exp.Arguments[0])); + case "Round": + if (exp.Arguments.Count > 1 && exp.Arguments[1].Type.FullName == "System.Int32") return _utils.Adapter.LambdaMath_Round(getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + return _utils.Adapter.LambdaMath_Round(getExp(exp.Arguments[0]), "0"); + case "Exp": return _utils.Adapter.LambdaMath_Exp(getExp(exp.Arguments[0])); + case "Log": return _utils.Adapter.LambdaMath_Log(getExp(exp.Arguments[0])); + case "Log10": return _utils.Adapter.LambdaMath_Log10(getExp(exp.Arguments[0])); + case "Pow": return _utils.Adapter.LambdaMath_Pow(getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "Sqrt": return _utils.Adapter.LambdaMath_Sqrt(getExp(exp.Arguments[0])); + case "Cos": return _utils.Adapter.LambdaMath_Cos(getExp(exp.Arguments[0])); + case "Sin": return _utils.Adapter.LambdaMath_Sin(getExp(exp.Arguments[0])); + case "Tan": return _utils.Adapter.LambdaMath_Tan(getExp(exp.Arguments[0])); + case "Acos": return _utils.Adapter.LambdaMath_Acos(getExp(exp.Arguments[0])); + case "Asin": return _utils.Adapter.LambdaMath_Asin(getExp(exp.Arguments[0])); + case "Atan": return _utils.Adapter.LambdaMath_Atan(getExp(exp.Arguments[0])); + case "Atan2": return _utils.Adapter.LambdaMath_Atan2(getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "Truncate": return _utils.Adapter.LambdaMath_Truncate(getExp(exp.Arguments[0])); + } + return null; + } + public override string ExpressionLambdaToSqlCallDateTime(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "Compare": return _utils.Adapter.LambdaDateTime_CompareTo(getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "DaysInMonth": return _utils.Adapter.LambdaDateTime_DaysInMonth(getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + case "Equals": return _utils.Adapter.LambdaDateTime_Equals(getExp(exp.Arguments[0]), getExp(exp.Arguments[1])); + + case "IsLeapYear": return _utils.Adapter.LambdaDateTime_IsLeapYear(getExp(exp.Arguments[0])); + + case "Parse": return _utils.Adapter.LambdaConvert_ToDateTime(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ParseExact": + case "TryParse": + case "TryParseExact": return _utils.Adapter.LambdaConvert_ToDateTime(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + } + } + else + { + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); + switch (exp.Method.Name) + { + case "Add": return _utils.Adapter.LambdaDateTime_Add(left, args1); + case "AddDays": return _utils.Adapter.LambdaDateTime_AddDays(left, args1); + case "AddHours": return _utils.Adapter.LambdaDateTime_AddHours(left, args1); + case "AddMilliseconds": return _utils.Adapter.LambdaDateTime_AddMilliseconds(left, args1); + case "AddMinutes": return _utils.Adapter.LambdaDateTime_AddMinutes(left, args1); + case "AddMonths": return _utils.Adapter.LambdaDateTime_AddMonths(left, args1); + case "AddSeconds": return _utils.Adapter.LambdaDateTime_AddSeconds(left, args1); + case "AddTicks": return _utils.Adapter.LambdaDateTime_AddTicks(left, args1); + case "AddYears": return _utils.Adapter.LambdaDateTime_AddYears(left, args1); + case "Subtract": + switch ((exp.Arguments[0].Type.IsNullableType() ? exp.Arguments[0].Type.GetGenericArguments().FirstOrDefault() : exp.Arguments[0].Type).FullName) + { + case "System.DateTime": return _utils.Adapter.LambdaDateTime_Subtract(left, args1); + case "System.TimeSpan": return _utils.Adapter.LambdaDateTime_SubtractTimeSpan(left, args1); + } + break; + case "Equals": return _utils.Adapter.LambdaDateTime_Equals(left, args1); + case "CompareTo": return _utils.Adapter.LambdaDateTime_CompareTo(left, args1); + case "ToString": return exp.Arguments.Count == 0 ? _utils.Adapter.LambdaDateTime_ToString(left) : null; + } + } + return null; + } + + public virtual string LambdaDateSpan_Add(string operand, string value) => $"({operand}+{value})"; + public virtual string LambdaDateSpan_Subtract(string operand, string value) => $"({operand}-({value}))"; + public virtual string LambdaDateSpan_Equals(string oldvalue, string newvalue) => $"({oldvalue} = {newvalue})"; + public virtual string LambdaDateSpan_CompareTo(string oldvalue, string newvalue) => $"({oldvalue}-({newvalue}))"; + + public override string ExpressionLambdaToSqlCallTimeSpan(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "Compare": return $"({getExp(exp.Arguments[0])}-({getExp(exp.Arguments[1])}))"; + case "Equals": return $"({getExp(exp.Arguments[0])} = {getExp(exp.Arguments[1])})"; + case "FromDays": return $"(({getExp(exp.Arguments[0])})*{60 * 60 * 24})"; + case "FromHours": return $"(({getExp(exp.Arguments[0])})*{60 * 60})"; + case "FromMilliseconds": return $"(({getExp(exp.Arguments[0])})/1000)"; + case "FromMinutes": return $"(({getExp(exp.Arguments[0])})*60)"; + case "FromSeconds": return $"({getExp(exp.Arguments[0])})"; + case "FromTicks": return $"(({getExp(exp.Arguments[0])})/10000000)"; + case "Parse": return _utils.Adapter.LambdaConvert_ToInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ParseExact": + case "TryParse": + case "TryParseExact": return _utils.Adapter.LambdaConvert_ToInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + } + } + else + { + var left = getExp(exp.Object); + var args1 = exp.Arguments.Count == 0 ? null : getExp(exp.Arguments[0]); + switch (exp.Method.Name) + { + case "Add": return $"({left}+{args1})"; + case "Subtract": return $"({left}-({args1}))"; + case "Equals": return $"({left} = {args1})"; + case "CompareTo": return $"({left}-({args1}))"; + case "ToString": return _utils.Adapter.LambdaConvert_ToString(exp.Type, left); + } + } + return null; + } + public override string ExpressionLambdaToSqlCallConvert(MethodCallExpression exp, ExpTSC tsc) + { + Func getExp = exparg => ExpressionLambdaToSql(exparg, tsc); + if (exp.Object == null) + { + switch (exp.Method.Name) + { + case "ToBoolean": return _utils.Adapter.LambdaConvert_ToBoolean(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToByte": return _utils.Adapter.LambdaConvert_ToByte(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToChar": return _utils.Adapter.LambdaConvert_ToChar(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToDateTime": return _utils.Adapter.LambdaConvert_ToDateTime(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToDecimal": return _utils.Adapter.LambdaConvert_ToDecimal(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToDouble": return _utils.Adapter.LambdaConvert_ToDouble(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToInt16": return _utils.Adapter.LambdaConvert_ToInt16(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToInt32": return _utils.Adapter.LambdaConvert_ToInt32(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToInt64": return _utils.Adapter.LambdaConvert_ToInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToSByte": return _utils.Adapter.LambdaConvert_ToSByte(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToSingle": return _utils.Adapter.LambdaConvert_ToSingle(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToString": return _utils.Adapter.LambdaConvert_ToString(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToUInt16": return _utils.Adapter.LambdaConvert_ToUInt16(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToUInt32": return _utils.Adapter.LambdaConvert_ToUInt32(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + case "ToUInt64": return _utils.Adapter.LambdaConvert_ToUInt64(exp.Arguments[0].Type, getExp(exp.Arguments[0])); + } + } + return null; + } + } +} diff --git a/Providers/FreeSql.Provider.Custom/CustomProvider.cs b/Providers/FreeSql.Provider.Custom/CustomProvider.cs new file mode 100644 index 00000000..babc8869 --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/CustomProvider.cs @@ -0,0 +1,91 @@ +using FreeSql.Custom; +using FreeSql.Internal; +using FreeSql.Internal.CommonProvider; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Threading; + +namespace FreeSql.Custom +{ + + public class CustomProvider : BaseDbProvider, IFreeSql + { + public override ISelect CreateSelectProvider(object dywhere) => new CustomSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public override IInsert CreateInsertProvider() => new CustomInsert(this, this.InternalCommonUtils, this.InternalCommonExpression); + public override IUpdate CreateUpdateProvider(object dywhere) => new CustomUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public override IDelete CreateDeleteProvider(object dywhere) => new CustomDelete(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); + public override IInsertOrUpdate CreateInsertOrUpdateProvider() => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); + + public override IDbFirst DbFirst => throw new NotImplementedException("FreeSql.Provider.Custom 未实现该功能"); + + public CustomProvider(string masterConnectionString, string[] slaveConnectionString, Func connectionFactory = null) + { + this.InternalCommonUtils = new CustomUtils(this); + this.InternalCommonExpression = new CustomExpression(this.InternalCommonUtils); + + this.Ado = new CustomAdo(this.InternalCommonUtils, masterConnectionString, slaveConnectionString, connectionFactory); + this.Aop = new AopProvider(); + + this.CodeFirst = new CustomCodeFirst(this, this.InternalCommonUtils, this.InternalCommonExpression); + + var _utils = InternalCommonUtils as CustomUtils; + //处理 MaxLength + this.Aop.ConfigEntityProperty += new EventHandler((s, e) => + { + object[] attrs = null; + try + { + attrs = e.Property.GetCustomAttributes(false).ToArray(); //.net core 反射存在版本冲突问题,导致该方法异常 + } + catch { } + + var maxlenAttr = attrs.Where(a => { + return ((a as Attribute)?.TypeId as Type)?.Name == "MaxLengthAttribute"; + }).FirstOrDefault(); + if (maxlenAttr != null) + { + var lenProp = maxlenAttr.GetType().GetProperties().Where(a => a.PropertyType.IsNumberType()).FirstOrDefault(); + if (lenProp != null && int.TryParse(string.Concat(lenProp.GetValue(maxlenAttr, null)), out var tryval)) + { + if (tryval != 0) + { + switch (this.Ado.DataType) + { + case DataType.Sqlite: + e.ModifyResult.DbType = tryval > 0 ? $"{_utils.Adapter.MappingDbTypeVarChar}({tryval})" : _utils.Adapter.MappingDbTypeText; + break; + } + } + } + } + }); + } + + ~CustomProvider() => this.Dispose(); + int _disposeCounter; + public override void Dispose() + { + if (Interlocked.Increment(ref _disposeCounter) != 1) return; + try + { + (this.Ado as AdoProvider)?.Dispose(); + } + finally + { + FreeSqlCustomAdapterGlobalExtensions._dicCustomAdater.TryRemove(Ado.Identifier, out var tryada); + } + } + } +} + +public static class FreeSqlCustomAdapterGlobalExtensions +{ + internal static CustomAdapter DefaultAdapter = new CustomAdapter(); + internal static ConcurrentDictionary _dicCustomAdater = new ConcurrentDictionary(); + public static void SetCustomAdapter(this IFreeSql that, CustomAdapter adapter) => _dicCustomAdater.AddOrUpdate(that.Ado.Identifier, adapter, (fsql, old) => adapter); + internal static CustomAdapter GetCustomAdapter(this IFreeSql that) => _dicCustomAdater.TryGetValue(that.Ado.Identifier, out var tryada) ? tryada : DefaultAdapter; +} diff --git a/Providers/FreeSql.Provider.Custom/CustomUtils.cs b/Providers/FreeSql.Provider.Custom/CustomUtils.cs new file mode 100644 index 00000000..ebea701f --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/CustomUtils.cs @@ -0,0 +1,85 @@ +using FreeSql.Internal; +using FreeSql.Internal.Model; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Globalization; + +namespace FreeSql.Custom +{ + + public class CustomUtils : CommonUtils + { + public CustomUtils(IFreeSql orm) : base(orm) { } + public CustomAdapter Adapter => _orm.GetCustomAdapter(); + + public override DbParameter AppendParamter(List _params, string parameterName, ColumnInfo col, Type type, object value) + { + if (string.IsNullOrEmpty(parameterName)) parameterName = $"p_{_params?.Count}"; + if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1); + var ret = (_orm.Ado as CustomAdo)?.CreateParameter(); + ret.ParameterName = QuoteParamterName(parameterName); + ret.Value = value; + var tp = _orm.CodeFirst.GetDbInfo(type)?.type; + if (tp != null) ret.DbType = (DbType)tp.Value; + _params?.Add(ret); + return ret; + } + + public override DbParameter[] GetDbParamtersByObject(string sql, object obj) => + Utils.GetDbParamtersByObject(sql, obj, null, (name, type, value) => + { + if (value?.Equals(DateTime.MinValue) == true) value = new DateTime(1970, 1, 1); + var ret = (_orm.Ado as CustomAdo)?.CreateParameter(); + ret.ParameterName = QuoteParamterName(name); + ret.Value = value; + var tp = _orm.CodeFirst.GetDbInfo(type)?.type; + if (tp != null) ret.DbType = (DbType)tp.Value; + return ret; + }); + + static FreeSql.Custom.CustomAdo _customAdo = new FreeSql.Custom.CustomAdo(); + public override string FormatSql(string sql, params object[] args) => _customAdo.Addslashes(sql, args); + public override string QuoteSqlName(params string[] name) + { + if (name.Length == 1) + { + var nametrim = name[0].Trim(); + if (nametrim.StartsWith("(") && nametrim.EndsWith(")")) + return nametrim; //原生SQL + if (nametrim.StartsWith(Adapter.QuoteSqlNameLeft.ToString()) && nametrim.EndsWith(Adapter.QuoteSqlNameRight.ToString())) + return nametrim; + return $"{Adapter.QuoteSqlNameLeft}{nametrim.TrimStart(Adapter.QuoteSqlNameLeft).TrimEnd(Adapter.QuoteSqlNameRight).Replace(".", $"{Adapter.QuoteSqlNameRight}.{Adapter.QuoteSqlNameLeft}")}{Adapter.QuoteSqlNameRight}"; + } + return $"{Adapter.QuoteSqlNameLeft}{string.Join($"{Adapter.QuoteSqlNameRight}.{Adapter.QuoteSqlNameLeft}", name)}{Adapter.QuoteSqlNameRight}"; + } + public override string TrimQuoteSqlName(string name) + { + var nametrim = name.Trim(); + if (nametrim.StartsWith("(") && nametrim.EndsWith(")")) + return nametrim; //原生SQL + //return $"{nametrim.TrimStart('[').TrimEnd(']').Replace("].[", ".").Replace(".[", ".")}"; + return $"{nametrim.TrimStart(Adapter.QuoteSqlNameLeft).TrimEnd(Adapter.QuoteSqlNameRight).Replace($"{Adapter.QuoteSqlNameRight}.{Adapter.QuoteSqlNameLeft}", ".").Replace($".{Adapter.QuoteSqlNameLeft}", ".")}"; + } + public override string[] SplitTableName(string name) => GetSplitTableNames(name, Adapter.QuoteSqlNameLeft, Adapter.QuoteSqlNameRight, 2); + public override string QuoteParamterName(string name) => $"@{name}"; + public override string IsNull(string sql, object value) => Adapter.IsNullSql(sql, value); + public override string StringConcat(string[] objs, Type[] types) => Adapter.ConcatSql(objs, types); + public override string Mod(string left, string right, Type leftType, Type rightType) => Adapter.Mod(left, right, leftType, rightType); + public override string Div(string left, string right, Type leftType, Type rightType) => Adapter.Div(left, right, leftType, rightType); + public override string Now => Adapter.LambdaDateTime_Now; + public override string NowUtc => Adapter.LambdaDateTime_UtcNow; + + public override string QuoteWriteParamterAdapter(Type type, string paramterName) => paramterName; + protected override string QuoteReadColumnAdapter(Type type, Type mapType, string columnName) => Adapter.FieldSql(type, columnName); + + public override string GetNoneParamaterSqlValue(List specialParams, string specialParamFlag, ColumnInfo col, Type type, object value) + { + if (value == null) return "NULL"; + if (type.IsNumberType()) return string.Format(CultureInfo.InvariantCulture, "{0}", value); + if (type == typeof(byte[])) return Adapter.ByteRawSql(value); + return FormatSql("{0}", value, 1); + } + } +} diff --git a/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj b/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj new file mode 100644 index 00000000..0ace9f15 --- /dev/null +++ b/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.0;net45;net40 + 2.3.0-preview20210206 + true + FreeSql;ncc;YeXiangQin + FreeSql 数据库自定义适配,访问所有数据库 + https://github.com/2881099/FreeSql + https://github.com/2881099/FreeSql + git + MIT + FreeSql;ORM + $(AssemblyName) + logo.png + $(AssemblyName) + true + true + true + key.snk + false + + + + + + + + + + + + net40 + + + + diff --git a/Providers/FreeSql.Provider.Custom/key.snk b/Providers/FreeSql.Provider.Custom/key.snk new file mode 100644 index 0000000000000000000000000000000000000000..5f765b67fd32bc55f13b3b58a8ed4013ecbcaf79 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096og&rfBldXKZ#rSznjOQLY1GpK(dTy+< zZhzCZ6#U^1q5~Jqsw77*tm6KRkOzg<+vSBlz@89Ygo8H}Nqys;o$a%+)+2+q&daQT zeno_6Y=!^ZXU%D`FCYM`A;wyP68-!Af$^9AZ7Eak+8R0V-%OZ(Z*(ANj6;=Uq)W<6 z_-~okpdsx@pA-S;b}@dU%y^}WR5QbG@pv$VI+sC=SX2Cw(H{~o$a4D5;Gv9AIX$1< z`E!^f9XuOdzm@;OC%~+JZu+c~r1FPMn^M z%WA{i9bNConJTj@g0UCU4st*8bc<&F`3^irHOgdTZY9UjUpF?4ZQim!7SJr_z$1_N z?FA7YnMFtCno8>J(`{Wprq3;uRIDXJF%W74uOd1LyEUE+YuaC@7dhkjx|faHW0irmO%!`S*`KSN&pHVvu^ "SELECT SCOPE_IDENTITY()"; + //可以重写更多的设置 +} + +static IFreeSql fsql = new FreeSql.FreeSqlBuilder() + .UseConnectionString(FreeSql.DataType.Custom, () => new SqlConnection(@"Data Source=...")) + .Build(); //be sure to define as singleton mode + +fsql.SetCustomAdapter(new Mssql2000Adapter()); +``` + +适配好新的 CustomAdapter 后,请在 FreeSqlBuilder.Build 之后调用 IFreeSql.SetCustomAdapter 方法生效。