diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index da7ace6b..bdd16ff9 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -538,5 +538,14 @@ + + + 批量注入 Repository,可以参考代码自行调整 + + + + + + diff --git a/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest1.cs b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest1.cs index b8f068cc..28f1ca01 100644 --- a/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest1.cs +++ b/FreeSql.Tests/FreeSql.Tests/ClickHouse/ClickHouseTest1.cs @@ -36,18 +36,14 @@ namespace FreeSql.Tests.MySql public int? Points { get; set; } } [FreeSql.DataAnnotations.Table(Name = "ClickHouseTest")] - public class TestClickHouse : IEnumerable + public class TestClickHouse { [FreeSql.DataAnnotations.Column(IsPrimary = true, IsIdentity = true)] [Now] public long Id { get; set; } public string Name { get; set; } - public IEnumerator GetEnumerator() - { - yield return Id; - yield return Name; - } + public Decimal Money { get; set; } } class NowAttribute: Attribute { } @@ -231,6 +227,41 @@ namespace FreeSql.Tests.MySql Debug.WriteLine("更新用时:" + stopwatch.ElapsedMilliseconds.ToString()); } + [Fact] + public async void TestInsertUpdateData() + { + //g.clickHouse.CodeFirst.SyncStructure(); + Stopwatch stopwatch = new Stopwatch(); + var fsql = g.clickHouse; + var repository=fsql.GetRepository(); + await repository.DeleteAsync(o=>o.Id>0); + List tables = new List(); + for (int i = 1; i < 3; i++) + { + tables.Add(new CollectDataEntity + { + Id = new Random().Next(), + CollectTime = DateTime.Now, + DataFlag = "1", + EquipmentCode = "11", + UnitStr = "111", + PropertyCode = "1111", + NumericValue=1111.1119999912500M + }); + } + + var insert = repository.Orm.Insert(tables); + insert.ExecuteAffrows(); + var list = repository.Orm.Select().ToList(); + //var list = repository.Insert(tables); + //var list = repository.Select.ToList(); + //list.ForEach(o=>o.EquipmentCode = "666"); + //stopwatch.Start(); + //await repository.UpdateAsync(list); + //stopwatch.Stop(); + Debug.WriteLine("更新用时:" + stopwatch.ElapsedMilliseconds.ToString()); + + } } } diff --git a/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity.cs b/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity.cs index 07abf7ff..0e8548e1 100644 --- a/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity.cs +++ b/FreeSql.Tests/FreeSql.Tests/ClickHouse/CollectDataEntity.cs @@ -113,10 +113,11 @@ namespace XY.Model.Business /// 数值或状态是否变更 /// public bool IsValueOrStateChanged { get; set; } - + /// /// 采集数值 /// + [Column(StringLength = 18)] public decimal? NumericValue { get; set; } /// diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 06ac1dc3..9c849a52 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3172,6 +3172,177 @@ + + + 测试数据库是否连接正确,本方法执行如下命令: + 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> + + + + + + + + 可自定义解析表达式 @@ -4045,6 +4216,12 @@ 超时 + + + 获取资源 + + + 使用完毕后,归还资源 @@ -4115,6 +4292,12 @@ 资源对象 + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + 归还对象给对象池的时候触发 diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs index da491174..8f006685 100644 --- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs +++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseCodeFirst.cs @@ -35,6 +35,7 @@ namespace FreeSql.ClickHouse { typeof(double).FullName, CsToDb.New(DbType.Double, "Float64", "Float64", false, false, 0) },{ typeof(double?).FullName, CsToDb.New(DbType.Double, "Float64", "Nullable(Float64)", false, true, null) }, { typeof(float).FullName, CsToDb.New(DbType.Single, "Float32","Float32", false, false, 0) },{ typeof(float?).FullName, CsToDb.New(DbType.Single, "Float32","Nullable(Float32)", false, true, null) }, + { typeof(decimal).FullName, CsToDb.New(DbType.Decimal, "Decimal128(19)","Decimal128(19)", false, false, 0) },{ typeof(decimal?).FullName, CsToDb.New(DbType.Decimal, "Nullable(Decimal128(19))","Nullable(Decimal128(19))", false, true, null) }, { typeof(DateTime).FullName, CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "DateTime('Asia/Shanghai')", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(DbType.DateTime, "DateTime('Asia/Shanghai')", "Nullable(DateTime('Asia/Shanghai'))", false, true, null) }, @@ -114,13 +115,14 @@ namespace FreeSql.ClickHouse foreach (var uk in tb.Indexes) { sb.Append(" \r\n "); - sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))).Append("("); + sb.Append("INDEX ").Append(_commonUtils.QuoteSqlName(ReplaceIndexName(uk.Name, tbname[1]))); foreach (var tbcol in uk.Columns) { + sb.Append(" "); sb.Append(_commonUtils.QuoteSqlName(tbcol.Column.Attribute.Name)); - sb.Append("TYPE set(8192) GRANULARITY 5, "); + sb.Append("TYPE set(8192) GRANULARITY 5, "); } - sb.Remove(sb.Length - 2, 2).Append("),"); + sb.Remove(sb.Length - 2, 2); } sb.Remove(sb.Length - 1, 1); sb.Append("\r\n) "); diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs index c0c6da19..92e57371 100644 --- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs +++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseDbFirst.cs @@ -67,6 +67,9 @@ namespace FreeSql.ClickHouse case "Float32": case "float": case "nullable(float32)": return DbType.Single; + case "decimal": + case "decimal128": + case "nullable(decimal128)": return DbType.Decimal; case "date": case "nullable(date)": return DbType.Date; case "datetime": @@ -103,6 +106,7 @@ namespace FreeSql.ClickHouse { (int)DbType.Double, new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") }, { (int)DbType.Single, new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") }, + { (int)DbType.Decimal, new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") }, { (int)DbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, { (int)DbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, diff --git a/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs b/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs index ba173483..dc5ab194 100644 --- a/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs +++ b/Providers/FreeSql.Provider.ClickHouse/ClickHouseExpression.cs @@ -35,7 +35,7 @@ namespace FreeSql.ClickHouse case "System.Byte": return $"cast({getExp(operandExp)} as Int8)"; case "System.Char": return $"substr(cast({getExp(operandExp)} as String), 1, 1)"; case "System.DateTime": return $"cast({getExp(operandExp)} as DateTime)"; - case "System.Decimal": return $"cast({getExp(operandExp)} as Float64)"; + case "System.Decimal": return $"cast({getExp(operandExp)} as Decimal128(19))"; case "System.Double": return $"cast({getExp(operandExp)} as Float64)"; case "System.Int16": return $"cast({getExp(operandExp)} as Int16)"; case "System.Int32": return $"cast({getExp(operandExp)} as Int32)"; @@ -63,7 +63,7 @@ namespace FreeSql.ClickHouse case "System.Byte": return $"cast({getExp(callExp.Arguments[0])} as Int8)"; case "System.Char": return $"substr(cast({getExp(callExp.Arguments[0])} as String), 1, 1)"; case "System.DateTime": return $"cast({getExp(callExp.Arguments[0])} as DateTime)"; - case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as Float64)"; + case "System.Decimal": return $"cast({getExp(callExp.Arguments[0])} as Decimal128(19))"; case "System.Double": return $"cast({getExp(callExp.Arguments[0])} as Float64)"; case "System.Int16": return $"cast({getExp(callExp.Arguments[0])} as Int16)"; case "System.Int32": return $"cast({getExp(callExp.Arguments[0])} as Int32)"; @@ -556,7 +556,7 @@ namespace FreeSql.ClickHouse case "ToByte": return $"cast({getExp(exp.Arguments[0])} as Int8)"; case "ToChar": return $"substr(cast({getExp(exp.Arguments[0])} as String), 1, 1)"; case "ToDateTime": return $"cast({getExp(exp.Arguments[0])} as DateTime)"; - case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as Float64)"; + case "ToDecimal": return $"cast({getExp(exp.Arguments[0])} as Decimal128(19))"; case "ToDouble": return $"cast({getExp(exp.Arguments[0])} as Float64)"; case "ToInt16": case "ToInt32": diff --git a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs index 5666ca8c..ce6b2f9c 100644 --- a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs +++ b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseInsert.cs @@ -55,7 +55,6 @@ namespace FreeSql.ClickHouse.Curd { try { - Debug.WriteLine($"开始执行时间:{DateTime.Now}"); before = new Aop.CurdBeforeEventArgs(_table.Type, _table, Aop.CurdType.Insert, null, _params); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); using var bulkCopyInterface = new ClickHouseBulkCopy(_orm.Ado.MasterPool.Get().Value as ClickHouseConnection) @@ -170,9 +169,9 @@ namespace FreeSql.ClickHouse.Curd #if net40 #else - public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken); - public override Task ExecuteIdentityAsync(CancellationToken cancellationToken = default) => base.SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken); - public override Task> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken); + public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => SplitExecuteAffrowsAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken); + public override Task ExecuteIdentityAsync(CancellationToken cancellationToken = default) => SplitExecuteIdentityAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken); + public override Task> ExecuteInsertedAsync(CancellationToken cancellationToken = default) => SplitExecuteInsertedAsync(_batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, _batchValuesLimit > 0 ? _batchValuesLimit : int.MaxValue, cancellationToken); async protected override Task RawExecuteIdentityAsync(CancellationToken cancellationToken = default) { @@ -255,7 +254,8 @@ namespace FreeSql.ClickHouse.Curd if (ss.Length == 1) { _batchProgress?.Invoke(new BatchProgressStatus(_source, 1, 1)); - ret = await this.RawExecuteAffrowsAsync(cancellationToken); + await this.RawExecuteAffrowsAsync(cancellationToken); + ret = _source.Count; ClearData(); return ret; } @@ -269,7 +269,8 @@ namespace FreeSql.ClickHouse.Curd { _source = ss[a]; _batchProgress?.Invoke(new BatchProgressStatus(_source, a + 1, ss.Length)); - ret += await this.RawExecuteAffrowsAsync(cancellationToken); + await this.RawExecuteAffrowsAsync(cancellationToken); + ret += _source.Count; } } catch (Exception ex) diff --git a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseUpdate.cs b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseUpdate.cs index 703ff6af..9e69b3c9 100644 --- a/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseUpdate.cs +++ b/Providers/FreeSql.Provider.ClickHouse/Curd/ClickHouseUpdate.cs @@ -293,18 +293,20 @@ namespace FreeSql.ClickHouse.Curd #if net40 #else - public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => base.SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); - public override Task> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => base.SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + public override Task ExecuteAffrowsAsync(CancellationToken cancellationToken = default) => SplitExecuteAffrowsAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); + public override Task> ExecuteUpdatedAsync(CancellationToken cancellationToken = default) => SplitExecuteUpdatedAsync(_batchRowsLimit > 0 ? _batchRowsLimit : 500, _batchParameterLimit > 0 ? _batchParameterLimit : 3000, cancellationToken); protected override Task> RawExecuteUpdatedAsync(CancellationToken cancellationToken = default) => throw new NotImplementedException("FreeSql.ClickHouse.Custom 未实现该功能 未实现该功能"); async protected override Task SplitExecuteAffrowsAsync(int valuesLimit, int parameterLimit, CancellationToken cancellationToken = default) + { var ss = SplitSource(valuesLimit, parameterLimit); var ret = 0; if (ss.Length <= 1) { if (_source?.Any() == true) _batchProgress?.Invoke(new BatchProgressStatus(_source, 1, 1)); - ret = await this.RawExecuteAffrowsAsync(cancellationToken); + await this.RawExecuteAffrowsAsync(cancellationToken); + ret = _source.Count; ClearData(); return ret; } @@ -319,7 +321,8 @@ namespace FreeSql.ClickHouse.Curd { _source = ss[a]; _batchProgress?.Invoke(new BatchProgressStatus(_source, a + 1, ss.Length)); - ret += await this.RawExecuteAffrowsAsync(cancellationToken); + await this.RawExecuteAffrowsAsync(cancellationToken); + ret += _source.Count; } } catch (Exception ex)