diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.TDengine/FreeSql.Tests.Provider.TDengine.csproj b/FreeSql.Tests/FreeSql.Tests.Provider.TDengine/FreeSql.Tests.Provider.TDengine.csproj
new file mode 100644
index 000000000..8b5797ec9
--- /dev/null
+++ b/FreeSql.Tests/FreeSql.Tests.Provider.TDengine/FreeSql.Tests.Provider.TDengine.csproj
@@ -0,0 +1,24 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/FreeSql.Tests/FreeSql.Tests.Provider.TDengine/UnitTest1.cs b/FreeSql.Tests/FreeSql.Tests.Provider.TDengine/UnitTest1.cs
new file mode 100644
index 000000000..69995c959
--- /dev/null
+++ b/FreeSql.Tests/FreeSql.Tests.Provider.TDengine/UnitTest1.cs
@@ -0,0 +1,16 @@
+namespace FreeSql.Tests.Provider.TDengine
+{
+ public class Tests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Test1()
+ {
+ Assert.Pass();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FreeSql.sln b/FreeSql.sln
index d0cb3099f..f1f4a392c 100644
--- a/FreeSql.sln
+++ b/FreeSql.sln
@@ -133,6 +133,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests.Provider.Duck
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FreeSql.Provider.TDengine", "Providers\FreeSql.Provider.TDengine\FreeSql.Provider.TDengine.csproj", "{329BA8B3-4139-4CCE-AFEC-4BE9B7BED317}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FreeSql.Tests.Provider.TDengine", "FreeSql.Tests\FreeSql.Tests.Provider.TDengine\FreeSql.Tests.Provider.TDengine.csproj", "{1F313BE0-5069-4B5E-BEE7-138954D293F9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -803,6 +805,18 @@ Global
{329BA8B3-4139-4CCE-AFEC-4BE9B7BED317}.Release|x64.Build.0 = Release|Any CPU
{329BA8B3-4139-4CCE-AFEC-4BE9B7BED317}.Release|x86.ActiveCfg = Release|Any CPU
{329BA8B3-4139-4CCE-AFEC-4BE9B7BED317}.Release|x86.Build.0 = Release|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Debug|x64.Build.0 = Debug|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Debug|x86.Build.0 = Debug|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Release|x64.ActiveCfg = Release|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Release|x64.Build.0 = Release|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Release|x86.ActiveCfg = Release|Any CPU
+ {1F313BE0-5069-4B5E-BEE7-138954D293F9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -848,8 +862,8 @@ Global
{329BA8B3-4139-4CCE-AFEC-4BE9B7BED317} = {2A381C57-2697-427B-9F10-55DA11FD02E4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- RESX_NeutralResourcesLanguage = en-US
- RESX_PrefixTranslations = True
SolutionGuid = {089687FD-5D25-40AB-BA8A-A10D1E137F98}
+ RESX_PrefixTranslations = True
+ RESX_NeutralResourcesLanguage = en-US
EndGlobalSection
EndGlobal
diff --git a/FreeSql/DataType.cs b/FreeSql/DataType.cs
index 0409f7062..5fe899ee2 100644
--- a/FreeSql/DataType.cs
+++ b/FreeSql/DataType.cs
@@ -65,6 +65,8 @@ namespace FreeSql
CustomOracle, CustomSqlServer, CustomMySql, CustomPostgreSQL,
- DuckDB
+ DuckDB,
+
+ TDengine
}
}
diff --git a/Providers/FreeSql.Provider.TDengine/Attributes/TDengineSTableAttribute.cs b/Providers/FreeSql.Provider.TDengine/Attributes/STableAttribute.cs
similarity index 83%
rename from Providers/FreeSql.Provider.TDengine/Attributes/TDengineSTableAttribute.cs
rename to Providers/FreeSql.Provider.TDengine/Attributes/STableAttribute.cs
index 9b5370751..3c33c5de2 100644
--- a/Providers/FreeSql.Provider.TDengine/Attributes/TDengineSTableAttribute.cs
+++ b/Providers/FreeSql.Provider.TDengine/Attributes/STableAttribute.cs
@@ -10,7 +10,7 @@ namespace FreeSql.DataAnnotations
/// TDengine 超级表
///
[AttributeUsage(AttributeTargets.Class)]
- public class TDengineSTableAttribute : Attribute
+ public class STableAttribute : Attribute
{
}
}
\ No newline at end of file
diff --git a/Providers/FreeSql.Provider.TDengine/TDengineAdo/TDengineAdo.cs b/Providers/FreeSql.Provider.TDengine/TDengineAdo/TDengineAdo.cs
new file mode 100644
index 000000000..3cd2ae701
--- /dev/null
+++ b/Providers/FreeSql.Provider.TDengine/TDengineAdo/TDengineAdo.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FreeSql.Internal;
+using FreeSql.Internal.CommonProvider;
+using FreeSql.Internal.Model;
+using FreeSql.Internal.ObjectPool;
+
+namespace FreeSql.Provider.TDengine.TDengineAdo
+{
+ internal class TDengineAdo : AdoProvider
+ {
+ public TDengineAdo(CommonUtils util, string masterConnectionString, string[] slaveConnectionStrings, Func connectionFactory) : base(DataType.TDengine, masterConnectionString, slaveConnectionStrings)
+ {
+
+ }
+
+ public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override DbCommand CreateCommand()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override DbParameter[] GetDbParamtersByObject(string sql, object obj)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void ReturnConnection(IObjectPool pool, Object conn, Exception ex)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Providers/FreeSql.Provider.TDengine/TDengineAdo/TDengineConnectionPool.cs b/Providers/FreeSql.Provider.TDengine/TDengineAdo/TDengineConnectionPool.cs
new file mode 100644
index 000000000..c4cb96ac1
--- /dev/null
+++ b/Providers/FreeSql.Provider.TDengine/TDengineAdo/TDengineConnectionPool.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using FreeSql.Internal.ObjectPool;
+using TDengine.Data.Client;
+
+namespace FreeSql.Provider.TDengine.TDengineAdo
+{
+ internal class TDengineConnectionPool : ObjectPool
+ {
+ internal Action AvailableHandler;
+
+ internal Action UnavailableHandler;
+
+
+ public TDengineConnectionPool(string name, string connectionString, Action availableHandler,
+ Action unavailableHandler) : base(null)
+ {
+ this.AvailableHandler = availableHandler;
+ this.UnavailableHandler = unavailableHandler;
+ var policy = new TDengineConnectionPoolPolicy
+ {
+ InternalPool = this,
+ Name = name
+ };
+ this.Policy = policy;
+ policy.ConnectionString = connectionString;
+ }
+ }
+
+ internal class TDengineConnectionPoolPolicy : IPolicy
+ {
+ internal TDengineConnectionPool InternalPool;
+ public string Name { get; set; } = $"TDengine Connection {CoreStrings.S_ObjectPool}";
+ public int PoolSize { get; set; } = 50;
+ public TimeSpan SyncGetTimeout { get; set; } = TimeSpan.FromSeconds(10);
+ public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromSeconds(20);
+ public int AsyncGetCapacity { get; set; } = 10000;
+ public bool IsThrowGetTimeoutException { get; set; } = true;
+ public bool IsAutoDisposeWithSystem { get; set; } = true;
+ public int CheckAvailableInterval { get; set; } = 2;
+ public int Weight { get; set; } = 1;
+
+ static readonly ConcurrentDictionary DicConnStrIncr =
+ new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase);
+
+ private string _connectionString;
+
+ public string ConnectionString
+ {
+ get => _connectionString;
+ set
+ {
+ _connectionString = value ?? "";
+
+ var minPoolSize = 0;
+ var pattern = @"Min(imum)?\s*pool\s*size\s*=\s*(\d+)";
+ var m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
+ if (m.Success)
+ {
+ minPoolSize = int.Parse(m.Groups[2].Value);
+ _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
+ }
+
+ pattern = @"Max(imum)?\s*pool\s*size\s*=\s*(\d+)";
+ m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
+ if (m.Success == false || int.TryParse(m.Groups[2].Value, out var poolsize) == false || poolsize <= 0)
+ poolsize = Math.Max(50, minPoolSize);
+ var connStrIncr =
+ DicConnStrIncr.AddOrUpdate(_connectionString, 1, (oldkey, oldval) => Math.Min(5, oldval + 1));
+ PoolSize = poolsize + connStrIncr;
+ _connectionString = m.Success
+ ? Regex.Replace(_connectionString, pattern, $"Maximum pool size={PoolSize}",
+ RegexOptions.IgnoreCase)
+ : $"{_connectionString};Maximum pool size={PoolSize}";
+
+ pattern = @"Connection\s*LifeTime\s*=\s*(\d+)";
+ m = Regex.Match(_connectionString, pattern, RegexOptions.IgnoreCase);
+ if (m.Success)
+ {
+ IdleTimeout = TimeSpan.FromSeconds(int.Parse(m.Groups[1].Value));
+ _connectionString = Regex.Replace(_connectionString, pattern, "", RegexOptions.IgnoreCase);
+ }
+
+ FreeSql.Internal.CommonUtils.PrevReheatConnectionPool(InternalPool, minPoolSize);
+ }
+ }
+
+ public DbConnection OnCreate()
+ {
+ var conn = new TDengineConnection(_connectionString);
+ return conn;
+ }
+
+ public void OnDestroy(DbConnection obj)
+ {
+ if (obj.State != ConnectionState.Closed) obj.Close();
+ obj.Dispose();
+ }
+
+ public void OnGetTimeout()
+ {
+
+ }
+
+ public void OnGet(Object obj)
+ {
+ if (InternalPool.IsAvailable)
+ {
+ if (obj.Value == null)
+ {
+ InternalPool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy);
+ throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
+ }
+
+ if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && obj.Value.Ping() == false)
+ {
+
+ try
+ {
+ obj.Value.Open();
+ }
+ catch (Exception ex)
+ {
+ if (InternalPool.SetUnavailable(ex, obj.LastGetTimeCopy) == true)
+ throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}");
+ throw ex;
+ }
+ }
+ }
+ }
+
+#if net40
+#else
+ public async Task OnGetAsync(Object obj)
+ {
+ if (InternalPool.IsAvailable)
+ {
+ if (obj.Value == null)
+ {
+ InternalPool.SetUnavailable(new Exception(CoreStrings.S_ConnectionStringError), obj.LastGetTimeCopy);
+ throw new Exception(CoreStrings.S_ConnectionStringError_Check(this.Name));
+ }
+
+ if (obj.Value.State != ConnectionState.Open || DateTime.Now.Subtract(obj.LastReturnTime).TotalSeconds > 60 && (await obj.Value.PingAsync()) == false)
+ {
+
+ try
+ {
+ await obj.Value.OpenAsync();
+ }
+ catch (Exception ex)
+ {
+ if (InternalPool.SetUnavailable(ex, obj.LastGetTimeCopy) == true)
+ throw new Exception($"【{this.Name}】Block access and wait for recovery: {ex.Message}");
+ throw ex;
+ }
+ }
+ }
+ }
+#endif
+ public void OnReturn(Object obj)
+ {
+ }
+
+ public bool OnCheckAvailable(Object obj)
+ {
+ if (obj.Value == null) return false;
+ if (obj.Value.State == ConnectionState.Closed) obj.Value.Open();
+ return obj.Value.Ping(true);
+ }
+
+ public void OnAvailable()
+ {
+ InternalPool.AvailableHandler?.Invoke();
+ }
+
+ public void OnUnavailable()
+ {
+ InternalPool.UnavailableHandler?.Invoke();
+ }
+ }
+
+ static class DbConnectionExtensions
+ {
+ static DbCommand PingCommand(DbConnection conn)
+ {
+ var cmd = conn.CreateCommand();
+ cmd.CommandTimeout = 5;
+ cmd.CommandText = "select 1";
+ return cmd;
+ }
+
+ public static bool Ping(this DbConnection that, bool isThrow = false)
+ {
+ try
+ {
+ PingCommand(that).ExecuteNonQuery();
+ return true;
+ }
+ catch
+ {
+ if (that.State != ConnectionState.Closed)
+ try
+ {
+ that.Close();
+ }
+ catch
+ {
+ // ignored
+ }
+
+ if (isThrow) throw;
+ return false;
+ }
+ }
+
+#if net40
+#else
+ public static async Task PingAsync(this DbConnection that, bool isThrow = false)
+ {
+ try
+ {
+ await PingCommand(that).ExecuteNonQueryAsync();
+ return true;
+ }
+ catch
+ {
+ if (that.State != ConnectionState.Closed)
+ try
+ {
+ that.Close();
+ }
+ catch
+ {
+ }
+
+ if (isThrow) throw;
+ return false;
+ }
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Providers/FreeSql.Provider.TDengine/TDengineCodeFirst.cs b/Providers/FreeSql.Provider.TDengine/TDengineCodeFirst.cs
new file mode 100644
index 000000000..c50111a3f
--- /dev/null
+++ b/Providers/FreeSql.Provider.TDengine/TDengineCodeFirst.cs
@@ -0,0 +1,28 @@
+using FreeSql.Internal;
+using FreeSql.Internal.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FreeSql.Provider.TDengine
+{
+ internal class TDengineCodeFirst : Internal.CommonProvider.CodeFirstProvider
+ {
+ public TDengineCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression commonExpression) : base(orm, commonUtils, commonExpression)
+ {
+
+ }
+
+ public override DbInfoResult GetDbInfo(Type type)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override string GetComparisonDDLStatements(params TypeSchemaAndName[] objects)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Providers/FreeSql.Provider.TDengine/TDengineDbFirst.cs b/Providers/FreeSql.Provider.TDengine/TDengineDbFirst.cs
new file mode 100644
index 000000000..f69f10d26
--- /dev/null
+++ b/Providers/FreeSql.Provider.TDengine/TDengineDbFirst.cs
@@ -0,0 +1,7 @@
+namespace FreeSql.Provider.TDengine
+{
+ public class TDengineDbFirst
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/Providers/FreeSql.Provider.TDengine/TDengineProvider.cs b/Providers/FreeSql.Provider.TDengine/TDengineProvider.cs
new file mode 100644
index 000000000..cc48bb540
--- /dev/null
+++ b/Providers/FreeSql.Provider.TDengine/TDengineProvider.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using FreeSql.Internal.CommonProvider;
+
+namespace FreeSql.Provider.TDengine
+{
+ internal class TDengineProvider : BaseDbProvider, IFreeSql
+ {
+ public override ISelect CreateSelectProvider(object dywhere)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override IInsert CreateInsertProvider()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override IUpdate CreateUpdateProvider(object dywhere)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override IDelete CreateDeleteProvider(object dywhere)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override IInsertOrUpdate CreateInsertOrUpdateProvider()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Dispose()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file