创建TDengine相关实现

This commit is contained in:
d4ilys
2024-09-03 10:01:26 +08:00
parent 8276925f2e
commit fc9e5b46ec
10 changed files with 428 additions and 4 deletions

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
namespace FreeSql.Tests.Provider.TDengine
{
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
Assert.Pass();
}
}
}

View File

@@ -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

View File

@@ -65,6 +65,8 @@ namespace FreeSql
CustomOracle, CustomSqlServer, CustomMySql, CustomPostgreSQL,
DuckDB
DuckDB,
TDengine
}
}

View File

@@ -10,7 +10,7 @@ namespace FreeSql.DataAnnotations
/// TDengine 超级表
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class TDengineSTableAttribute : Attribute
public class STableAttribute : Attribute
{
}
}

View File

@@ -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<DbConnection> 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<DbConnection> pool, Object<DbConnection> conn, Exception ex)
{
throw new NotImplementedException();
}
}
}

View File

@@ -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<DbConnection>
{
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<DbConnection>
{
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<string, int> DicConnStrIncr =
new ConcurrentDictionary<string, int>(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<DbConnection> 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<DbConnection> 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<DbConnection> obj)
{
}
public bool OnCheckAvailable(Object<DbConnection> 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<bool> 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
}
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,7 @@
namespace FreeSql.Provider.TDengine
{
public class TDengineDbFirst
{
}
}

View File

@@ -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<TMark> : BaseDbProvider, IFreeSql<TMark>
{
public override ISelect<T1> CreateSelectProvider<T1>(object dywhere)
{
throw new NotImplementedException();
}
public override IInsert<T1> CreateInsertProvider<T1>()
{
throw new NotImplementedException();
}
public override IUpdate<T1> CreateUpdateProvider<T1>(object dywhere)
{
throw new NotImplementedException();
}
public override IDelete<T1> CreateDeleteProvider<T1>(object dywhere)
{
throw new NotImplementedException();
}
public override IInsertOrUpdate<T1> CreateInsertOrUpdateProvider<T1>()
{
throw new NotImplementedException();
}
public override void Dispose()
{
throw new NotImplementedException();
}
}
}