达梦数据库适配ServiceStack框架

注:达梦的驱动版本请使用2023第四季度及以后版本驱动才可以

ServiceStack介绍

ServiceStack官网: https://github.com/ServiceStack/ServiceStack

ServiceStack是一个开源的十分流行的WebService框架,引用其官网的介绍:"Service Stack is a high-performance .NET web services platform that simplifies the development of high-performance REST (JSON, XML, JSV, HTML, MsgPack, ProtoBuf, CSV) and WCF SOAP Web Services."

"ServiceStack是一个高性能的.NET Web Service 平台,他能够简化开发高性能的REST (支持JSON,XML,JSV,HTML,MsgPack,ProtoBuf,CSV等消息格式)以及WCF SOAP风格的WebService"。

代码段

sql语句

sql 复制代码
CREATE TABLE "SYSDBA"."TestTab"
(
"Id" VARCHAR(200) NOT NULL,
"Name" VARCHAR(200),
NOT CLUSTER PRIMARY KEY("Id")) STORAGE(ON "MAIN", CLUSTERBTR) ;

Program.cs

cpp 复制代码
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Web;
using ServiceStack.DataAnnotations;
using ServiceStack.OrmLite;

namespace Yidea.Resource.Host
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            var encoding = Encoding.GetEncoding("gb18030");

            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

            var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();


            var dbFactory = new OrmLiteConnectionFactory("Server=192.15.1.17:5236; UserId=SYSDBA; PWD=SYSDBA; SCHEMA=SYSDBA;", GetSqlDialect());

            var Db = dbFactory.CreateDbConnection();
            Db.Open();
                 var aa = Db.Select<TestTab>(o => o.Id == "zzzzzz").First();
                aa.Name = "xiugai";
               Db.Save(aa);


/*          
           var company = Db.Select<Company>().ToList();
           foreach (var item in company)
            {
                item.Name = item.Name + item.Id;
           }
          Db.SaveAll(company);

            //var tab = new TestTab() { Id = "bb", Name = "bbbbbbb" };
            //Db.Save(tab);

            List<TestTab> list = Db.Select<TestTab>().ToList();
            foreach (var item in list)
            {
                item.Name = item.Id + item.Name;
            }
            Db.SaveAll(list);*/


        }

        [Alias("vCompany")]
        public class Company
        {
            [PrimaryKey]
            [AutoIncrement]
            public int Id { get; set; }
            public string Code { get; set; }
            public string Name { get; set; }
            [Alias("EId")]
            public int EnterpriseId { get; set; }
            [Alias("Pid")]
            public int? SuperiorCompanyId { get; set; }
            [Alias("IsDisabled")]
            public bool Disabled { get; set; }
            [Alias("IdPath")]
            public string Hierarchy { get; set; }
        }


        private static IOrmLiteDialectProvider GetSqlDialect()
        {
            var sqlProvider = new DmaProvider();
            sqlProvider.GetStringConverter().UseUnicode = true;
            sqlProvider.GetDateTimeConverter().DateStyle = DateTimeKind.Local;
            return sqlProvider;
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseUrls("http://*:12345")
                .UseKestrel(opt =>
                {
                    opt.AddServerHeader = false;
                    opt.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(5);
                })
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .ConfigureAppConfiguration((builderContext, config) =>
                {
                    IHostingEnvironment env = builderContext.HostingEnvironment;

                    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                    config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
                })
                .UseNLog();
    }
}

DmaProvider.cs

cpp 复制代码
using Dm;
using ServiceStack.DataAnnotations;
using ServiceStack;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.SqlServer.Converters;
using ServiceStack.OrmLite.SqlServer;
using ServiceStack.Text;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ServiceStack.Data;
using System.Collections;

namespace Yidea.Resource.Host
{
    public class DmaProvider : OrmLiteDialectProviderBase<DmaProvider>
    {
        public static DmaProvider Instance = new();

        public DmaProvider()
        {
            base.AutoIncrementDefinition = "IDENTITY(1,1)";
            base.SelectIdentitySql = "SELECT SCOPE_IDENTITY()";

            base.InitColumnTypeMap();

            RowVersionConverter = new SqlServerRowVersionConverter();

            base.RegisterConverter<string>(new SqlServerStringConverter());
            base.RegisterConverter<bool>(new SqlServerBoolConverter());

            base.RegisterConverter<sbyte>(new SqlServerSByteConverter());
            base.RegisterConverter<ushort>(new SqlServerUInt16Converter());
            base.RegisterConverter<uint>(new SqlServerUInt32Converter());
            base.RegisterConverter<ulong>(new SqlServerUInt64Converter());

            base.RegisterConverter<float>(new SqlServerFloatConverter());
            base.RegisterConverter<double>(new SqlServerDoubleConverter());
            base.RegisterConverter<decimal>(new SqlServerDecimalConverter());

            base.RegisterConverter<DateTime>(new SqlServerDateTimeConverter());

            base.RegisterConverter<Guid>(new SqlServerGuidConverter());

            base.RegisterConverter<byte[]>(new SqlServerByteArrayConverter());

            this.Variables = new Dictionary<string, string>
            {
                { OrmLiteVariables.SystemUtc, "SYSUTCDATETIME()" },
                { OrmLiteVariables.MaxText, "VARCHAR(MAX)" },
                { OrmLiteVariables.MaxTextUnicode, "NVARCHAR(MAX)" },
                { OrmLiteVariables.True, SqlBool(true) },
                { OrmLiteVariables.False, SqlBool(false) },
            };
        }

        public override string GetQuotedValue(string paramValue)
        {
            return (StringConverter.UseUnicode ? "N'" : "'") + paramValue.Replace("'", "''") + "'";
        }

        public override IDbConnection CreateConnection(string connectionString, Dictionary<string, string> options)
        {
            DmConnection conn = new DmConnection();
            conn.ConnectionString = connectionString;
            return conn;
        }

        public override SqlExpression<T> SqlExpression<T>() => new SqlServerExpression<T>(this);

       // public override IDbDataParameter CreateParam() => new SqlParameter();
        public override IDbDataParameter CreateParam() => new DmParameter();

        private const string DefaultSchema = "dbo";
        public override string ToTableNamesStatement(string schema)
        {
            var sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'";
            return sql + " AND TABLE_SCHEMA = {0}".SqlFmt(this, schema ?? DefaultSchema);
        }

        public override string ToTableNamesWithRowCountsStatement(bool live, string schema)
        {
            var schemaSql = " AND s.Name = {0}".SqlFmt(this, schema ?? DefaultSchema);

            var sql = @"SELECT t.NAME, p.rows FROM sys.tables t INNER JOIN sys.schemas s ON t.schema_id = s.schema_id 
                               INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id 
                               INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
                         WHERE t.is_ms_shipped = 0 " + schemaSql + " GROUP BY t.NAME, p.Rows";
            return sql;
        }

        //public override List<string> GetSchemas(IDbCommand dbCmd)
        //{
        //    var sql = "SELECT DISTINCT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'";
        //    return dbCmd.SqlColumn<string>(sql);
        //}

        //public override Dictionary<string, List<string>> GetSchemaTables(IDbCommand dbCmd)
        //{
        //    var sql = "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'";
        //    return dbCmd.Lookup<string, string>(sql);
        //}

        public override bool DoesSchemaExist(IDbCommand dbCmd, string schemaName)
        {
            var sql = $"SELECT count(*) FROM sys.schemas WHERE name = '{schemaName.SqlParam()}'";
            var result = dbCmd.ExecLongScalar(sql);
            return result > 0;
        }

        //public override async Task<bool> DoesSchemaExistAsync(IDbCommand dbCmd, string schemaName, CancellationToken token = default)
        //{
        //    var sql = $"SELECT count(*) FROM sys.schemas WHERE name = '{schemaName.SqlParam()}'";
        //    var result = await dbCmd.ExecLongScalarAsync(sql, token);
        //    return result > 0;
        //}

        public override string ToCreateSchemaStatement(string schemaName)
        {
            var sql = $"CREATE SCHEMA [{GetSchemaName(schemaName)}]";
            return sql;
        }

        //public override string ToCreateSavePoint(string name) => $"SAVE TRANSACTION {name}";
        //public override string ToReleaseSavePoint(string name) => null;
        //public override string ToRollbackSavePoint(string name) => $"ROLLBACK TRANSACTION {name}";

        public override bool DoesTableExist(IDbCommand dbCmd, string tableName, string schema = null)
        {
            var sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {0}"
                .SqlFmt(this, tableName);

            if (schema != null)
                sql += " AND TABLE_SCHEMA = {0}".SqlFmt(this, schema);
            else
                sql += " AND TABLE_SCHEMA <> 'Security'";

            var result = dbCmd.ExecLongScalar(sql);

            return result > 0;
        }

        //public override async Task<bool> DoesTableExistAsync(IDbCommand dbCmd, string tableName, string schema = null, CancellationToken token = default)
        //{
        //    var sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = {0}"
        //        .SqlFmt(this, tableName);

        //    if (schema != null)
        //        sql += " AND TABLE_SCHEMA = {0}".SqlFmt(this, schema);
        //    else
        //        sql += " AND TABLE_SCHEMA <> 'Security'";

        //    var result = await dbCmd.ExecLongScalarAsync(sql, token);

        //    return result > 0;
        //}

        public override bool DoesColumnExist(IDbConnection db, string columnName, string tableName, string schema = null)
        {
            var sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @tableName AND COLUMN_NAME = @columnName"
                .SqlFmt(this, tableName, columnName);

            if (schema != null)
                sql += " AND TABLE_SCHEMA = @schema";

            var result = db.SqlScalar<long>(sql, new { tableName, columnName, schema });

            return result > 0;
        }

        //public override async Task<bool> DoesColumnExistAsync(IDbConnection db, string columnName, string tableName, string schema = null,
        //    CancellationToken token = default)
        //{
        //    var sql = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @tableName AND COLUMN_NAME = @columnName"
        //        .SqlFmt(this, tableName, columnName);

        //    if (schema != null)
        //        sql += " AND TABLE_SCHEMA = @schema";

        //    var result = await db.SqlScalarAsync<long>(sql, new { tableName, columnName, schema }, token: token);

        //    return result > 0;
        //}

        //public override string GetForeignKeyOnDeleteClause(ForeignKeyConstraint foreignKey)
        //{
        //    return "RESTRICT" == (foreignKey.OnDelete ?? "").ToUpper()
        //        ? ""
        //        : base.GetForeignKeyOnDeleteClause(foreignKey);
        //}

        //public override string GetForeignKeyOnUpdateClause(ForeignKeyConstraint foreignKey)
        //{
        //    return "RESTRICT" == (foreignKey.OnUpdate ?? "").ToUpper()
        //        ? ""
        //        : base.GetForeignKeyOnUpdateClause(foreignKey);
        //}

        public override string GetDropForeignKeyConstraints(ModelDefinition modelDef)
        {
            //TODO: find out if this should go in base class?

            var sb = StringBuilderCache.Allocate();
            foreach (var fieldDef in modelDef.FieldDefinitions)
            {
                if (fieldDef.ForeignKey != null)
                {
                    var foreignKeyName = fieldDef.ForeignKey.GetForeignKeyName(
                        modelDef,
                        OrmLiteUtils.GetModelDefinition(fieldDef.ForeignKey.ReferenceType),
                        NamingStrategy,
                        fieldDef);

                    var tableName = GetQuotedTableName(modelDef);
                    sb.AppendLine($"IF EXISTS (SELECT name FROM sys.foreign_keys WHERE name = '{foreignKeyName}')");
                    sb.AppendLine("BEGIN");
                    sb.AppendLine($"  ALTER TABLE {tableName} DROP {foreignKeyName};");
                    sb.AppendLine("END");
                }
            }

            return StringBuilderCache.ReturnAndFree(sb);
        }

        //public override string ToAddColumnStatement(string schema, string table, FieldDefinition fieldDef) =>
        //    $"ALTER TABLE {GetQuotedTableName(table, schema)} ADD {GetColumnDefinition(fieldDef)};";

        //public override string ToAlterColumnStatement(string schema, string table, FieldDefinition fieldDef) =>
        //    $"ALTER TABLE {GetQuotedTableName(table, schema)} ALTER COLUMN {GetColumnDefinition(fieldDef)};";

        //public override string ToChangeColumnNameStatement(string schema, string table, FieldDefinition fieldDef, string oldColumn)
        //{
        //    var modelName = NamingStrategy.GetTableName(table);
        //    var objectName = $"{modelName}.{oldColumn}";
        //    return $"EXEC sp_rename {GetQuotedValue(objectName)}, {GetQuotedValue(fieldDef.FieldName)}, {GetQuotedValue("COLUMN")};";
        //}

        //public override string ToRenameColumnStatement(string schema, string table, string oldColumn, string newColumn)
        //{
        //    var modelName = NamingStrategy.GetTableName(table);
        //    var objectName = $"{modelName}.{GetQuotedColumnName(oldColumn)}";
        //    return $"EXEC sp_rename {GetQuotedValue(objectName)}, {GetQuotedColumnName(newColumn)}, 'COLUMN';";
        //}

        protected virtual string GetAutoIncrementDefinition(FieldDefinition fieldDef)
        {
            return AutoIncrementDefinition;
        }

        public override string GetAutoIdDefaultValue(FieldDefinition fieldDef)
        {
            return fieldDef.FieldType == typeof(Guid)
                ? "newid()"
                : null;
        }

        public override string GetColumnDefinition(FieldDefinition fieldDef)
        {
            // https://msdn.microsoft.com/en-us/library/ms182776.aspx
            if (fieldDef.IsRowVersion)
                return $"{fieldDef.FieldName} rowversion NOT NULL";

            var fieldDefinition = ResolveFragment(fieldDef.CustomFieldDefinition) ??
                GetColumnTypeDefinition(fieldDef.ColumnType, fieldDef.FieldLength, fieldDef.Scale);

            var sql = StringBuilderCache.Allocate();
            sql.Append($"{GetQuotedColumnName(fieldDef.FieldName)} {fieldDefinition}");

            if (fieldDef.FieldType == typeof(string))
            {
                // https://msdn.microsoft.com/en-us/library/ms184391.aspx
                var collation = fieldDef.PropertyInfo?.FirstAttribute<SqlServerCollateAttribute>()?.Collation;
                if (!string.IsNullOrEmpty(collation))
                {
                    sql.Append($" COLLATE {collation}");
                }
            }

            if (fieldDef.IsPrimaryKey)
            {
                sql.Append(" PRIMARY KEY");

                if (fieldDef.IsNonClustered)
                    sql.Append(" NONCLUSTERED");

                if (fieldDef.AutoIncrement)
                {
                    sql.Append(" ").Append(GetAutoIncrementDefinition(fieldDef));
                }
            }
            else
            {
                sql.Append(fieldDef.IsNullable ? " NULL" : " NOT NULL");
            }

            if (fieldDef.IsUniqueConstraint)
            {
                sql.Append(" UNIQUE");
            }

            var defaultValue = GetDefaultValue(fieldDef);
            if (!string.IsNullOrEmpty(defaultValue))
            {
                sql.AppendFormat(DefaultValueFormat, defaultValue);
            }

            return StringBuilderCache.ReturnAndFree(sql);
        }

        //public override void BulkInsert<T>(IDbConnection db, IEnumerable<T> objs, BulkInsertConfig config = null)
        //{
        //    config ??= new();
        //    //if (config.Mode == BulkInsertMode.Sql)
        //    //{
        //    //    base.BulkInsert(db, objs, config);
        //    //    return;
        //    //}

        //    var sqlConn = (SqlConnection)db.ToDbConnection();
        //    using var bulkCopy = new SqlBulkCopy(sqlConn);
        //    var modelDef = ModelDefinition<T>.Definition;

        //    bulkCopy.BatchSize = config.BatchSize;
        //    bulkCopy.DestinationTableName = modelDef.ModelName;

        //    var table = new DataTable();
        //    var fieldDefs = GetInsertFieldDefinitions(modelDef, insertFields: config.InsertFields);
        //    foreach (var fieldDef in fieldDefs)
        //    {
        //        if (ShouldSkipInsert(fieldDef) && !fieldDef.AutoId)
        //            continue;

        //        var columnName = NamingStrategy.GetColumnName(fieldDef.FieldName);
        //        bulkCopy.ColumnMappings.Add(columnName, columnName);

        //        var converter = GetConverterBestMatch(fieldDef);
        //        var colType = converter.DbType switch
        //        {
        //            DbType.String => typeof(string),
        //            DbType.Int32 => typeof(int),
        //            DbType.Int64 => typeof(long),
        //            _ => Nullable.GetUnderlyingType(fieldDef.FieldType) ?? fieldDef.FieldType
        //        };

        //        table.Columns.Add(columnName, colType);
        //    }

        //    foreach (var obj in objs)
        //    {
        //        var row = table.NewRow();
        //        foreach (var fieldDef in fieldDefs)
        //        {
        //            if (ShouldSkipInsert(fieldDef) && !fieldDef.AutoId)
        //                continue;

        //            var value = fieldDef.AutoId
        //                ? GetInsertDefaultValue(fieldDef)
        //                : fieldDef.GetValue(obj);

        //            var converter = GetConverterBestMatch(fieldDef);
        //            var dbValue = converter.ToDbValue(fieldDef.FieldType, value);
        //            var columnName = NamingStrategy.GetColumnName(fieldDef.FieldName);
        //            dbValue ??= DBNull.Value;
        //            row[columnName] = dbValue;
        //        }
        //        table.Rows.Add(row);
        //    }

        //    bulkCopy.WriteToServer(table);
        //}

        public override string ToInsertRowStatement(IDbCommand cmd, object objWithProperties, ICollection<string> insertFields = null)
        {
            var sbColumnNames = StringBuilderCache.Allocate();
            var sbColumnValues = StringBuilderCacheAlt.Allocate();
            var sbReturningColumns = StringBuilderCacheAlt.Allocate();
            var tableType = objWithProperties.GetType();
            var modelDef = GetModel(tableType);

            var fieldDefs = GetInsertFieldDefinitions(modelDef, insertFields);
            foreach (var fieldDef in fieldDefs)
            {
                if (ShouldReturnOnInsert(modelDef, fieldDef))
                {
                    if (sbReturningColumns.Length > 0)
                        sbReturningColumns.Append(",");
                    sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
                }

                if (ShouldSkipInsert(fieldDef) && !fieldDef.AutoId)
                    continue;

                if (sbColumnNames.Length > 0)
                    sbColumnNames.Append(",");
                if (sbColumnValues.Length > 0)
                    sbColumnValues.Append(",");

                try
                {
                    sbColumnNames.Append(GetQuotedColumnName(fieldDef.FieldName));
                    sbColumnValues.Append(this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName)));

                    AddParameter(cmd, fieldDef);
                }
                catch (Exception ex)
                {
                    Log.Error("ERROR in ToInsertRowStatement(): " + ex.Message, ex);
                    throw;
                }
            }

            foreach (var fieldDef in modelDef.AutoIdFields) // need to include any AutoId fields that weren't included 
            {
                if (fieldDefs.Contains(fieldDef))
                    continue;

                if (sbReturningColumns.Length > 0)
                    sbReturningColumns.Append(",");
                sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
            }

            var strReturning = StringBuilderCacheAlt.ReturnAndFree(sbReturningColumns);
            strReturning = strReturning.Length > 0 ? "OUTPUT " + strReturning + " " : "";
            var sql = sbColumnNames.Length > 0
                ? $"INSERT INTO {GetQuotedTableName(modelDef)} ({StringBuilderCache.ReturnAndFree(sbColumnNames)}) " +
                  strReturning +
                  $"VALUES ({StringBuilderCacheAlt.ReturnAndFree(sbColumnValues)})"
                : $"INSERT INTO {GetQuotedTableName(modelDef)} {strReturning} DEFAULT VALUES";

            return sql;
        }

        protected string Sequence(string schema, string sequence)
        {
            if (schema == null)
                return GetQuotedName(sequence);

            var escapedSchema = NamingStrategy.GetSchemaName(schema)
                .Replace(".", "\".\"");

            return GetQuotedName(escapedSchema)
                   + "."
                   + GetQuotedName(sequence);
        }

        protected override bool ShouldSkipInsert(FieldDefinition fieldDef) =>
            fieldDef.ShouldSkipInsert() || fieldDef.AutoId;

        protected virtual bool ShouldReturnOnInsert(ModelDefinition modelDef, FieldDefinition fieldDef) =>
            fieldDef.ReturnOnInsert || (fieldDef.IsPrimaryKey && fieldDef.AutoIncrement && HasInsertReturnValues(modelDef)) || fieldDef.AutoId;

        public override bool HasInsertReturnValues(ModelDefinition modelDef) =>
            modelDef.FieldDefinitions.Any(x => x.ReturnOnInsert || (x.AutoId && x.FieldType == typeof(Guid)));

        protected virtual bool SupportsSequences(FieldDefinition fieldDef) => false;

        //public override void EnableIdentityInsert<T>(IDbCommand cmd)
        //{
        //    var tableName = cmd.GetDialectProvider().GetQuotedTableName(ModelDefinition<T>.Definition);
        //    cmd.ExecNonQuery($"SET IDENTITY_INSERT {tableName} ON");
        //}

        //public override Task EnableIdentityInsertAsync<T>(IDbCommand cmd, CancellationToken token = default)
        //{
        //    var tableName = cmd.GetDialectProvider().GetQuotedTableName(ModelDefinition<T>.Definition);
        //    return cmd.ExecNonQueryAsync($"SET IDENTITY_INSERT {tableName} ON", null, token);
        //}

        //public override void DisableIdentityInsert<T>(IDbCommand cmd)
        //{
        //    var tableName = cmd.GetDialectProvider().GetQuotedTableName(ModelDefinition<T>.Definition);
        //    cmd.ExecNonQuery($"SET IDENTITY_INSERT {tableName} OFF");
        //}

        //public override Task DisableIdentityInsertAsync<T>(IDbCommand cmd, CancellationToken token = default)
        //{
        //    var tableName = cmd.GetDialectProvider().GetQuotedTableName(ModelDefinition<T>.Definition);
        //    return cmd.ExecNonQueryAsync($"SET IDENTITY_INSERT {tableName} OFF", null, token);
        //}

        public override bool PrepareParameterizedUpdateStatement<T>(IDbCommand cmd, ICollection<string> updateFields = null)
        {
            var sql = StringBuilderCache.Allocate();
            var sqlFilter = StringBuilderCacheAlt.Allocate();
            var modelDef = ModelDefinition<T>.Definition;
            var hadRowVersion = false;
            var updateAllFields = updateFields == null || updateFields.Count == 0;

            cmd.Parameters.Clear();

            foreach (var fieldDef in modelDef.FieldDefinitions)
            {
                if (fieldDef.ShouldSkipUpdate())
                    continue;

                try
                {
                    if ((fieldDef.IsPrimaryKey || fieldDef.IsRowVersion) && updateAllFields)
                    {
                        if (sqlFilter.Length > 0)
                            sqlFilter.Append(" AND ");

                        AppendFieldCondition(sqlFilter, fieldDef, cmd);

                        if (fieldDef.IsRowVersion)
                            hadRowVersion = true;

                        continue;
                    }

                    if (!updateAllFields && !updateFields.Contains(fieldDef.Name, StringComparer.OrdinalIgnoreCase))
                        continue;

                    if (sql.Length > 0)
                        sql.Append(", ");

                    sql
                        .Append(GetQuotedColumnName(fieldDef.FieldName))
                        .Append("=")
                        .Append(this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName)));

                    AddParameter(cmd, fieldDef);
                }
                catch (Exception ex)
                {
                    OrmLiteUtils.HandleException(ex, "ERROR in PrepareParameterizedUpdateStatement(): " + ex.Message);
                }
            }

            if (sql.Length > 0)
            {
                var strFilter = StringBuilderCacheAlt.ReturnAndFree(sqlFilter);
                cmd.CommandText = $"UPDATE {GetQuotedTableName(modelDef)} " +
                                  $"SET {StringBuilderCache.ReturnAndFree(sql)} {(strFilter.Length > 0 ? "WHERE " + strFilter : "")}";
                cmd.CommandText = cmd.CommandText.ReplaceAll("@", ":");
            }
            else
            {
                cmd.CommandText = "";
            }

            return hadRowVersion;
        }
        public void SetValue(object instance, object value)
        {
            if (instance is IDictionary d)
            {
                d[Name] = value;
                return;
            }

            this.SetValueFn?.Invoke(instance, value);
        }


        public string Name { get; set; }
        public SetMemberDelegate SetValueFn { get; set; }
        public override void SetParameterValues<T>(IDbCommand dbCmd, object obj)
        {
            var modelDef = GetModel(typeof(T));
            var fieldMap = GetFieldDefinitionMap(modelDef);

            foreach (IDataParameter p in dbCmd.Parameters)
            {
                var fieldName = this.ToFieldName(p.ParameterName);
                fieldMap.TryGetValue(fieldName, out var fieldDef);

                if (fieldDef == null)
                {
                    if (ParamNameFilter != null)
                    {
                        fieldDef = modelDef.GetFieldDefinition(name =>
                            string.Equals(ParamNameFilter(name), fieldName, StringComparison.OrdinalIgnoreCase));
                    }

                    if (fieldDef == null)
                        throw new ArgumentException($"Field Definition '{fieldName}' was not found");
                }

                if (fieldDef.AutoId && p.Value != null)
                {
                    var existingId = fieldDef.GetValue(obj);
                    if (existingId is Guid existingGuid && existingGuid != default(Guid))
                    {
                        p.Value = existingGuid; // Use existing value if not default
                    }

                    //fieldDef.SetValue(obj, p.Value); //Auto populate default values
                    continue;
                }

                SetParameterValue(fieldDef, p, obj);
            }
            dbCmd.CommandText = dbCmd.CommandText.ReplaceAll("@", ":");
            var rowsUpdated = dbCmd.ExecNonQuery();
        }


        public virtual void SetParameterValue(FieldDefinition fieldDef, IDataParameter p, object obj)
        {
            var value = GetValueOrDbNull(fieldDef, obj);
            p.Value = value;

            SetParameterSize(fieldDef, p);
        }

        protected virtual void SetParameterSize(FieldDefinition fieldDef, IDataParameter p)
        {
            if (p.Value is string s && p is IDbDataParameter dataParam && dataParam.Size > 0 && s.Length > dataParam.Size)
            {
                // db param Size set in StringConverter
                dataParam.Size = s.Length;
            }
        }

        protected virtual object GetValue(FieldDefinition fieldDef, object obj)
        {
            return GetFieldValue(fieldDef, fieldDef.GetValue(obj));
        }

        public object GetFieldValue(FieldDefinition fieldDef, object value)
        {
            if (value == null)
                return null;

            var converter = GetConverterBestMatch(fieldDef);
            try
            {
                return converter.ToDbValue(fieldDef.FieldType, value);
            }
            catch (Exception ex)
            {
                Log.Error($"Error in {converter.GetType().Name}.ToDbValue() for field '{fieldDef.Name}' of Type '{fieldDef.FieldType}' with value '{value.GetType().Name}'", ex);
                throw;
            }
        }

        public object GetFieldValue(Type fieldType, object value)
        {
            if (value == null)
                return null;

            var converter = GetConverterBestMatch(fieldType);
            try
            {
                return converter.ToDbValue(fieldType, value);
            }
            catch (Exception ex)
            {
                Log.Error($"Error in {converter.GetType().Name}.ToDbValue() for field of Type '{fieldType}' with value '{value.GetType().Name}'", ex);
                throw;
            }
        }

        protected virtual object GetValueOrDbNull(FieldDefinition fieldDef, object obj)
        {
            var value = GetValue(fieldDef, obj);
            if (value == null)
                return DBNull.Value;

            return value;
        }

        protected virtual object GetQuotedValueOrDbNull<T>(FieldDefinition fieldDef, object obj)
        {
            var value = fieldDef.GetValue(obj);

            if (value == null)
                return DBNull.Value;

            var unquotedVal = GetQuotedValue(value, fieldDef.FieldType)
                .TrimStart('\'').TrimEnd('\''); ;

            if (string.IsNullOrEmpty(unquotedVal))
                return DBNull.Value;

            return unquotedVal;
        }

        public override string ToUpdateStatement<T>(IDbCommand dbCmd, T item, ICollection<string> updateFields = null)
        {
            return base.ToUpdateStatement(dbCmd, item, updateFields);
        }
        public override void PrepareParameterizedInsertStatement<T>(IDbCommand cmd, ICollection<string> insertFields = null)
        {
            var sbColumnNames = StringBuilderCache.Allocate();
            var sbColumnValues = StringBuilderCacheAlt.Allocate();
            var sbReturningColumns = StringBuilderCacheAlt.Allocate();
            var modelDef = OrmLiteUtils.GetModelDefinition(typeof(T));

            cmd.Parameters.Clear();

            var fieldDefs = GetInsertFieldDefinitions(modelDef, insertFields);
            foreach (var fieldDef in fieldDefs)
            {
                if (ShouldReturnOnInsert(modelDef, fieldDef))
                {
                    if (sbReturningColumns.Length > 0)
                        sbReturningColumns.Append(",");
                    sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
                }

                if ((ShouldSkipInsert(fieldDef) && !fieldDef.AutoId))
                    continue;

                if (sbColumnNames.Length > 0)
                    sbColumnNames.Append(",");
                if (sbColumnValues.Length > 0)
                    sbColumnValues.Append(",");

                try
                {
                    sbColumnNames.Append(GetQuotedColumnName(fieldDef.FieldName));

                    if (SupportsSequences(fieldDef))
                    {
                        sbColumnValues.Append("NEXT VALUE FOR " + Sequence(NamingStrategy.GetSchemaName(modelDef), fieldDef.Sequence));
                    }
                    else
                    {
                        sbColumnValues.Append(this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName)));
                        AddParameter(cmd, fieldDef);
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("ERROR in PrepareParameterizedInsertStatement(): " + ex.Message, ex);
                    throw;
                }
            }

            foreach (var fieldDef in modelDef.AutoIdFields) // need to include any AutoId fields that weren't included 
            {
                if (fieldDefs.Contains(fieldDef))
                    continue;

                if (sbReturningColumns.Length > 0)
                    sbReturningColumns.Append(",");
                sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
            }

            var strReturning = StringBuilderCacheAlt.ReturnAndFree(sbReturningColumns);
            strReturning = strReturning.Length > 0 ? "OUTPUT " + strReturning + " " : "";
            cmd.CommandText = sbColumnNames.Length > 0
                ? $"INSERT INTO {GetQuotedTableName(modelDef)} ({StringBuilderCache.ReturnAndFree(sbColumnNames)}) {strReturning}" +
                  $"VALUES ({StringBuilderCacheAlt.ReturnAndFree(sbColumnValues)})"
                : $"INSERT INTO {GetQuotedTableName(modelDef)}{strReturning} DEFAULT VALUES";
            cmd.CommandText = cmd.CommandText.ReplaceAll("@", ":");
        }
        //public override void PrepareParameterizedInsertStatement<T>(IDbCommand cmd, ICollection<string> insertFields = null,
        //    Func<FieldDefinition, bool> shouldInclude = null)
        //{
        //    var sbColumnNames = StringBuilderCache.Allocate();
        //    var sbColumnValues = StringBuilderCacheAlt.Allocate();
        //    var sbReturningColumns = StringBuilderCacheAlt.Allocate();
        //    var modelDef = OrmLiteUtils.GetModelDefinition(typeof(T));

        //    cmd.Parameters.Clear();

        //    var fieldDefs = GetInsertFieldDefinitions(modelDef, insertFields);
        //    foreach (var fieldDef in fieldDefs)
        //    {
        //        if (ShouldReturnOnInsert(modelDef, fieldDef))
        //        {
        //            if (sbReturningColumns.Length > 0)
        //                sbReturningColumns.Append(",");
        //            sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
        //        }

        //        if ((ShouldSkipInsert(fieldDef) && !fieldDef.AutoId)
        //            && shouldInclude?.Invoke(fieldDef) != true)
        //            continue;

        //        if (sbColumnNames.Length > 0)
        //            sbColumnNames.Append(",");
        //        if (sbColumnValues.Length > 0)
        //            sbColumnValues.Append(",");

        //        try
        //        {
        //            sbColumnNames.Append(GetQuotedColumnName(fieldDef.FieldName));

        //            if (SupportsSequences(fieldDef))
        //            {
        //                sbColumnValues.Append("NEXT VALUE FOR " + Sequence(NamingStrategy.GetSchemaName(modelDef), fieldDef.Sequence));
        //            }
        //            else
        //            {
        //                sbColumnValues.Append(this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName), fieldDef.CustomInsert));
        //                AddParameter(cmd, fieldDef);
        //            }
        //        }
        //        catch (Exception ex)
        //        {
        //            Log.Error("ERROR in PrepareParameterizedInsertStatement(): " + ex.Message, ex);
        //            throw;
        //        }
        //    }

        //    foreach (var fieldDef in modelDef.AutoIdFields) // need to include any AutoId fields that weren't included 
        //    {
        //        if (fieldDefs.Contains(fieldDef))
        //            continue;

        //        if (sbReturningColumns.Length > 0)
        //            sbReturningColumns.Append(",");
        //        sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
        //    }

        //    var strReturning = StringBuilderCacheAlt.ReturnAndFree(sbReturningColumns);
        //    strReturning = strReturning.Length > 0 ? "OUTPUT " + strReturning + " " : "";
        //    cmd.CommandText = sbColumnNames.Length > 0
        //        ? $"INSERT INTO {GetQuotedTableName(modelDef)} ({StringBuilderCache.ReturnAndFree(sbColumnNames)}) {strReturning}" +
        //          $"VALUES ({StringBuilderCacheAlt.ReturnAndFree(sbColumnValues)})"
        //        : $"INSERT INTO {GetQuotedTableName(modelDef)}{strReturning} DEFAULT VALUES";
        //}
        //public override void PrepareParameterizedInsertStatement<T>(IDbCommand cmd, ICollection<string> insertFields = null,
        //    Func<FieldDefinition, bool> shouldInclude = null)
        //{
        //    var sbColumnNames = StringBuilderCache.Allocate();
        //    var sbColumnValues = StringBuilderCacheAlt.Allocate();
        //    var sbReturningColumns = StringBuilderCacheAlt.Allocate();
        //    var modelDef = OrmLiteUtils.GetModelDefinition(typeof(T));

        //    cmd.Parameters.Clear();

        //    var fieldDefs = GetInsertFieldDefinitions(modelDef, insertFields);
        //    foreach (var fieldDef in fieldDefs)
        //    {
        //        if (ShouldReturnOnInsert(modelDef, fieldDef))
        //        {
        //            if (sbReturningColumns.Length > 0)
        //                sbReturningColumns.Append(",");
        //            sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
        //        }

        //        if ((ShouldSkipInsert(fieldDef) && !fieldDef.AutoId)
        //            && shouldInclude?.Invoke(fieldDef) != true)
        //            continue;

        //        if (sbColumnNames.Length > 0)
        //            sbColumnNames.Append(",");
        //        if (sbColumnValues.Length > 0)
        //            sbColumnValues.Append(",");

        //        try
        //        {
        //            sbColumnNames.Append(GetQuotedColumnName(fieldDef.FieldName));

        //            if (SupportsSequences(fieldDef))
        //            {
        //                sbColumnValues.Append("NEXT VALUE FOR " + Sequence(NamingStrategy.GetSchemaName(modelDef), fieldDef.Sequence));
        //            }
        //            else
        //            {
        //                sbColumnValues.Append(this.GetParam(SanitizeFieldNameForParamName(fieldDef.FieldName), fieldDef.CustomInsert));
        //                AddParameter(cmd, fieldDef);
        //            }
        //        }
        //        catch (Exception ex)
        //        {
        //            Log.Error("ERROR in PrepareParameterizedInsertStatement(): " + ex.Message, ex);
        //            throw;
        //        }
        //    }

        //    foreach (var fieldDef in modelDef.AutoIdFields) // need to include any AutoId fields that weren't included 
        //    {
        //        if (fieldDefs.Contains(fieldDef))
        //            continue;

        //        if (sbReturningColumns.Length > 0)
        //            sbReturningColumns.Append(",");
        //        sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
        //    }

        //    var strReturning = StringBuilderCacheAlt.ReturnAndFree(sbReturningColumns);
        //    strReturning = strReturning.Length > 0 ? "OUTPUT " + strReturning + " " : "";
        //    cmd.CommandText = sbColumnNames.Length > 0
        //        ? $"INSERT INTO {GetQuotedTableName(modelDef)} ({StringBuilderCache.ReturnAndFree(sbColumnNames)}) {strReturning}" +
        //          $"VALUES ({StringBuilderCacheAlt.ReturnAndFree(sbColumnValues)})"
        //        : $"INSERT INTO {GetQuotedTableName(modelDef)}{strReturning} DEFAULT VALUES";
        //}

        public override void PrepareInsertRowStatement<T>(IDbCommand dbCmd, Dictionary<string, object> args)
        {
            var sbColumnNames = StringBuilderCache.Allocate();
            var sbColumnValues = StringBuilderCacheAlt.Allocate();
            var sbReturningColumns = StringBuilderCacheAlt.Allocate();
            var modelDef = OrmLiteUtils.GetModelDefinition(typeof(T));

            dbCmd.Parameters.Clear();

            //foreach (var entry in args)
            //{
            //    var fieldDef = modelDef.AssertFieldDefinition(entry.Key);

            //    if (ShouldReturnOnInsert(modelDef, fieldDef))
            //    {
            //        if (sbReturningColumns.Length > 0)
            //            sbReturningColumns.Append(",");
            //        sbReturningColumns.Append("INSERTED." + GetQuotedColumnName(fieldDef.FieldName));
            //    }

            //    if (ShouldSkipInsert(fieldDef) && !fieldDef.AutoId)
            //        continue;

            //    var value = entry.Value;

            //    if (sbColumnNames.Length > 0)
            //        sbColumnNames.Append(",");
            //    if (sbColumnValues.Length > 0)
            //        sbColumnValues.Append(",");

            //    try
            //    {
            //        sbColumnNames.Append(GetQuotedColumnName(fieldDef.FieldName));
            //        sbColumnValues.Append(this.GetInsertParam(dbCmd, value, fieldDef));
            //    }
            //    catch (Exception ex)
            //    {
            //        Log.Error("ERROR in PrepareInsertRowStatement(): " + ex.Message, ex);
            //        throw;
            //    }
            //}

            //var strReturning = StringBuilderCacheAlt.ReturnAndFree(sbReturningColumns);
            //strReturning = strReturning.Length > 0 ? "OUTPUT " + strReturning + " " : "";
            //dbCmd.CommandText = sbColumnNames.Length > 0
            //    ? $"INSERT INTO {GetQuotedTableName(modelDef)} ({StringBuilderCache.ReturnAndFree(sbColumnNames)}) {strReturning}" +
            //      $"VALUES ({StringBuilderCacheAlt.ReturnAndFree(sbColumnValues)})"
            //    : $"INSERT INTO {GetQuotedTableName(modelDef)} {strReturning}DEFAULT VALUES";
        }
        public enum QueryType
        {
            Select,
            Single,
            Scalar,
        }
        public override FieldDefinition[] GetInsertFieldDefinitions(ModelDefinition modelDef, ICollection<string> insertFields)
        {
            return base.GetInsertFieldDefinitions(modelDef, insertFields);
        }
        protected override object GetInsertDefaultValue(FieldDefinition fieldDef)
        {
            return base.GetInsertDefaultValue(fieldDef);
        }
        public override string ToInsertStatement<T>(IDbCommand dbCmd, T item, ICollection<string> insertFields = null)
        {
            return base.ToInsertStatement(dbCmd, item, insertFields);
        }
        public override string ToSelectStatement(Type tableType, string sqlFilter, params object[] filterParams)
        {
            var aa = base.ToSelectStatement(tableType, sqlFilter, filterParams).ReplaceAll("@", ":");
            return aa;
        }
        public override string ToSelectStatement(ModelDefinition modelDef, string selectExpression, string bodyExpression, string orderByExpression = null, int? offset = null, int? rows = null)
        {
            var aa = base.ToSelectStatement(modelDef, selectExpression, bodyExpression, orderByExpression, offset, rows).ReplaceAll("@", ":");
            return aa;
        }
        //public override string ToSelectStatement(QueryType queryType, ModelDefinition modelDef,
        //    string selectExpression,
        //    string bodyExpression,
        //    string orderByExpression = null,
        //    int? offset = null,
        //    int? rows = null,
        //    ISet<string> tags = null)
        //{
        //    var sb = StringBuilderCache.Allocate();
        //    ApplyTags(sb, tags);

        //    sb.Append(selectExpression)
        //    .Append(bodyExpression);

        //    if (!offset.HasValue && !rows.HasValue || (queryType != QueryType.Select && rows != 1))
        //        return StringBuilderCache.ReturnAndFree(sb) + orderByExpression;

        //    if (offset is < 0)
        //        throw new ArgumentException($"Skip value:'{offset.Value}' must be>=0");

        //    if (rows is < 0)
        //        throw new ArgumentException($"Rows value:'{rows.Value}' must be>=0");

        //    var skip = offset ?? 0;
        //    var take = rows ?? int.MaxValue;

        //    var selectType = selectExpression.StartsWithIgnoreCase("SELECT DISTINCT") ? "SELECT DISTINCT" : "SELECT";

        //    //avoid Windowing function if unnecessary
        //    if (skip == 0)
        //    {
        //        var sql = StringBuilderCache.ReturnAndFree(sb) + orderByExpression;
        //        return SqlTop(sql, take, selectType);
        //    }

        //    // Required because ordering is done by Windowing function
        //    if (string.IsNullOrEmpty(orderByExpression))
        //    {
        //        if (modelDef.PrimaryKey == null)
        //            throw new ApplicationException("Malformed model, no PrimaryKey defined");

        //        orderByExpression = $"ORDER BY {this.GetQuotedColumnName(modelDef, modelDef.PrimaryKey)}";
        //    }

        //    var row = take == int.MaxValue ? take : skip + take;

        //    var ret = $"SELECT * FROM (SELECT {selectExpression.Substring(selectType.Length)}, ROW_NUMBER() OVER ({orderByExpression}) As RowNum {bodyExpression}) AS RowConstrainedResult WHERE RowNum > {skip} AND RowNum <= {row}";

        //    return ret;
        //}

        protected static string SqlTop(string sql, int take, string selectType = null)
        {
            selectType ??= sql.StartsWithIgnoreCase("SELECT DISTINCT") ? "SELECT DISTINCT" : "SELECT";

            if (take == int.MaxValue)
                return sql;

            if (sql.Length < "SELECT".Length)
                return sql;

            return sql.Substring(0, sql.IndexOf(selectType)) + selectType + " TOP " + take + sql.Substring(sql.IndexOf(selectType) + selectType.Length);
        }

        //SELECT without RowNum and prefer aliases to be able to use in SELECT IN () Reference Queries
        public static string UseAliasesOrStripTablePrefixes(string selectExpression)
        {
            if (selectExpression.IndexOf('.') < 0)
                return selectExpression;

            var sb = StringBuilderCache.Allocate();
            var selectToken = selectExpression.SplitOnFirst(' ');
            var tokens = selectToken[1].Split(',');
            foreach (var token in tokens)
            {
                if (sb.Length > 0)
                    sb.Append(", ");

                var field = token.Trim();

                var aliasParts = field.SplitOnLast(' ');
                if (aliasParts.Length > 1)
                {
                    sb.Append(" " + aliasParts[aliasParts.Length - 1]);
                    continue;
                }

                var parts = field.SplitOnLast('.');
                if (parts.Length > 1)
                {
                    sb.Append(" " + parts[parts.Length - 1]);
                }
                else
                {
                    sb.Append(" " + field);
                }
            }

            var sqlSelect = selectToken[0] + " " + StringBuilderCache.ReturnAndFree(sb).Trim();
            return sqlSelect;
        }

        public override string GetLoadChildrenSubSelect<From>(SqlExpression<From> expr)
        {
            if (!expr.OrderByExpression.IsNullOrEmpty() && expr.Rows == null)
            {
                var modelDef = expr.ModelDef;
                expr.Select(this.GetQuotedColumnName(modelDef, modelDef.PrimaryKey))
                    .ClearLimits()
                    .OrderBy(""); //Invalid in Sub Selects

                var subSql = expr.ToSelectStatement();

                return subSql;
            }

            return base.GetLoadChildrenSubSelect(expr);
        }

        public override string SqlCurrency(string fieldOrValue, string currencySymbol) =>
            SqlConcat(new[] { "'" + currencySymbol + "'", $"CONVERT(VARCHAR, CONVERT(MONEY, {fieldOrValue}), 1)" });

        public override string SqlBool(bool value) => value ? "1" : "0";

        public override string SqlLimit(int? offset = null, int? rows = null) => rows == null && offset == null
            ? ""
            : rows != null
                ? "OFFSET " + offset.GetValueOrDefault() + " ROWS FETCH NEXT " + rows + " ROWS ONLY"
                : "OFFSET " + offset.GetValueOrDefault(int.MaxValue) + " ROWS";

        public override string SqlCast(object fieldOrValue, string castAs) =>
            castAs == Sql.VARCHAR
                ? $"CAST({fieldOrValue} AS VARCHAR(MAX))"
                : $"CAST({fieldOrValue} AS {castAs})";

        //public override string SqlRandom => "NEWID()";

        //public override void EnableForeignKeysCheck(IDbCommand cmd) => cmd.ExecNonQuery("EXEC sp_msforeachtable \"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all\"");
        //public override Task EnableForeignKeysCheckAsync(IDbCommand cmd, CancellationToken token = default) =>
        //    cmd.ExecNonQueryAsync("EXEC sp_msforeachtable \"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all\"", null, token);
        //public override void DisableForeignKeysCheck(IDbCommand cmd) => cmd.ExecNonQuery("EXEC sp_msforeachtable \"ALTER TABLE ? NOCHECK CONSTRAINT all\"");
        //public override Task DisableForeignKeysCheckAsync(IDbCommand cmd, CancellationToken token = default) =>
        //    cmd.ExecNonQueryAsync("EXEC sp_msforeachtable \"ALTER TABLE ? NOCHECK CONSTRAINT all\"", null, token);

        protected DbConnection Unwrap(IDbConnection db) => (DbConnection)db.ToDbConnection();

        protected DbCommand Unwrap(IDbCommand cmd) => (DbCommand)cmd.ToDbCommand();

        protected DbDataReader Unwrap(IDataReader reader) => (DbDataReader)reader;

#if ASYNC
        public override Task OpenAsync(IDbConnection db, CancellationToken token = default)
            => Unwrap(db).OpenAsync(token);

        public override Task<IDataReader> ExecuteReaderAsync(IDbCommand cmd, CancellationToken token = default)
            => Unwrap(cmd).ExecuteReaderAsync(token).Then(x => (IDataReader)x);

        public override Task<int> ExecuteNonQueryAsync(IDbCommand cmd, CancellationToken token = default)
            => Unwrap(cmd).ExecuteNonQueryAsync(token);

        public override Task<object> ExecuteScalarAsync(IDbCommand cmd, CancellationToken token = default)
            => Unwrap(cmd).ExecuteScalarAsync(token);

        public override Task<bool> ReadAsync(IDataReader reader, CancellationToken token = default)
            => Unwrap(reader).ReadAsync(token);

        public override async Task<List<T>> ReaderEach<T>(IDataReader reader, Func<T> fn, CancellationToken token = default)
        {
            try
            {
                var to = new List<T>();
                while (await ReadAsync(reader, token).ConfigureAwait(false))
                {
                    var row = fn();
                    to.Add(row);
                }
                return to;
            }
            finally
            {
                reader.Dispose();
            }
        }

        public override async Task<Return> ReaderEach<Return>(IDataReader reader, Action fn, Return source, CancellationToken token = default)
        {
            try
            {
                while (await ReadAsync(reader, token).ConfigureAwait(false))
                {
                    fn();
                }
                return source;
            }
            finally
            {
                reader.Dispose();
            }
        }

        public override async Task<T> ReaderRead<T>(IDataReader reader, Func<T> fn, CancellationToken token = default)
        {
            try
            {
                if (await ReadAsync(reader, token).ConfigureAwait(false))
                    return fn();

                return default(T);
            }
            finally
            {
                reader.Dispose();
            }
        }
#endif

        //public override void InitConnection(IDbConnection dbConn)
        //{
        //    if (dbConn is OrmLiteConnection ormLiteConn && dbConn.ToDbConnection() is SqlConnection sqlConn)
        //        ormLiteConn.ConnectionId = sqlConn.ClientConnectionId;

        //    foreach (var command in ConnectionCommands)
        //    {
        //        using var cmd = dbConn.CreateCommand();
        //        cmd.ExecNonQuery(command);
        //    }

        //    OnOpenConnection?.Invoke(dbConn);
        //}
    }


    public enum BulkInsertMode
    {
        Optimized,
        Csv,
        Sql,
    }

    public class BulkInsertConfig
    {
        public int BatchSize { get; set; } = 1000;
        public BulkInsertMode Mode { get; set; } = BulkInsertMode.Csv;
        public ICollection<string>? InsertFields { get; set; } = null;
    }

    //public static class OrmLiteConfigExtensions
    //{
    //    private static Dictionary<Type, ModelDefinition> typeModelDefinitionMap = new Dictionary<Type, ModelDefinition>();
    //    internal static bool CheckForIdField(IEnumerable<PropertyInfo> objProperties)
    //    {
    //        // Not using Linq.Where() and manually iterating through objProperties just to avoid dependencies on System.Xml??
    //        foreach (var objProperty in objProperties)
    //        {
    //            if (objProperty.Name != OrmLiteConfig.IdField) continue;
    //            return true;
    //        }
    //        return false;
    //    }
    //    internal static ModelDefinition GetModelDefinition(this Type modelType)
    //    {
    //        if (typeModelDefinitionMap.TryGetValue(modelType, out var modelDef))
    //            return modelDef;

    //        if (modelType.IsValueType || modelType == typeof(string))
    //            return null;

    //        var modelAliasAttr = modelType.FirstAttribute<AliasAttribute>();
    //        var schemaAttr = modelType.FirstAttribute<SchemaAttribute>();

    //        var preCreates = modelType.AllAttributes<PreCreateTableAttribute>();
    //        var postCreates = modelType.AllAttributes<PostCreateTableAttribute>();
    //        var preDrops = modelType.AllAttributes<PreDropTableAttribute>();
    //        var postDrops = modelType.AllAttributes<PostDropTableAttribute>();

    //        string JoinSql(List<string> statements)
    //        {
    //            if (statements.Count == 0)
    //                return null;
    //            var sb = StringBuilderCache.Allocate();
    //            foreach (var sql in statements)
    //            {
    //                if (sb.Length > 0)
    //                    sb.AppendLine(";");
    //                sb.Append(sql);
    //            }
    //            var to = StringBuilderCache.ReturnAndFree(sb);
    //            return to;
    //        }

    //        modelDef = new ModelDefinition
    //        {
    //            ModelType = modelType,
    //            Name = modelType.Name,
    //            Alias = modelAliasAttr?.Name,
    //            Schema = schemaAttr?.Name,
    //            PreCreateTableSql = JoinSql(preCreates.Map(x => x.Sql)),
    //            PostCreateTableSql = JoinSql(postCreates.Map(x => x.Sql)),
    //            PreDropTableSql = JoinSql(preDrops.Map(x => x.Sql)),
    //            PostDropTableSql = JoinSql(postDrops.Map(x => x.Sql)),
    //        };

    //        modelDef.CompositeIndexes.AddRange(
    //            modelType.AllAttributes<CompositeIndexAttribute>().ToList());

    //        modelDef.UniqueConstraints.AddRange(
    //            modelType.AllAttributes<UniqueConstraintAttribute>().ToList());

    //        var objProperties = modelType.GetProperties(
    //            BindingFlags.Public | BindingFlags.Instance).ToList();

    //        var hasPkAttr = objProperties.Any(p => p.HasAttributeCached<PrimaryKeyAttribute>());

    //        var hasIdField = CheckForIdField(objProperties);

    //        var i = 0;
    //        var propertyInfoIdx = 0;
    //        foreach (var propertyInfo in objProperties)
    //        {
    //            if (propertyInfo.GetIndexParameters().Length > 0)
    //                continue; //Is Indexer

    //            var sequenceAttr = propertyInfo.FirstAttribute<SequenceAttribute>();
    //            var computeAttr = propertyInfo.FirstAttribute<ComputeAttribute>();
    //            var computedAttr = propertyInfo.FirstAttribute<ComputedAttribute>();
    //            var persistedAttr = propertyInfo.FirstAttribute<PersistedAttribute>();
    //            var customSelectAttr = propertyInfo.FirstAttribute<CustomSelectAttribute>();
    //            var decimalAttribute = propertyInfo.FirstAttribute<DecimalLengthAttribute>();
    //            var belongToAttribute = propertyInfo.FirstAttribute<BelongToAttribute>();
    //            var referenceAttr = propertyInfo.FirstAttribute<ReferenceAttribute>();
    //            var referenceFieldAttr = propertyInfo.FirstAttribute<ReferenceFieldAttribute>();

    //            var isRowVersion = propertyInfo.Name == ModelDefinition.RowVersionName
    //                               && (propertyInfo.PropertyType == typeof(ulong) || propertyInfo.PropertyType == typeof(byte[]));

    //            var isNullableType = propertyInfo.PropertyType.IsNullableType();

    //            var isNullable = (!propertyInfo.PropertyType.IsValueType
    //                              && !propertyInfo.HasAttributeNamed(nameof(RequiredAttribute)))
    //                             || isNullableType;

    //            var propertyType = isNullableType
    //                ? Nullable.GetUnderlyingType(propertyInfo.PropertyType)
    //                : propertyInfo.PropertyType;


    //            Type treatAsType = null;

    //            if (propertyType.IsEnum)
    //            {
    //                var enumKind = Converters.EnumConverter.GetEnumKind(propertyType);
    //                if (enumKind == EnumKind.Int)
    //                    treatAsType = Enum.GetUnderlyingType(propertyType);
    //                else if (enumKind == EnumKind.Char)
    //                    treatAsType = typeof(char);
    //            }

    //            var isReference = referenceAttr != null || referenceFieldAttr != null;
    //            var isIgnored = propertyInfo.HasAttributeCached<IgnoreAttribute>() || isReference || computedAttr != null;

    //            var isFirst = !isIgnored && i++ == 0;

    //            var isAutoId = propertyInfo.HasAttributeCached<AutoIdAttribute>();

    //            var isPrimaryKey = (!hasPkAttr && (propertyInfo.Name == OrmLiteConfig.IdField || (!hasIdField && isFirst)))
    //                               || propertyInfo.HasAttributeNamed(nameof(PrimaryKeyAttribute))
    //                               || isAutoId;

    //            var isAutoIncrement = isPrimaryKey && propertyInfo.HasAttributeCached<AutoIncrementAttribute>();

    //            if (isAutoIncrement && propertyInfo.PropertyType == typeof(Guid))
    //                throw new NotSupportedException($"[AutoIncrement] is only valid for integer properties for {modelType.Name}.{propertyInfo.Name} Guid property use [AutoId] instead");

    //            if (isAutoId && (propertyInfo.PropertyType == typeof(int) || propertyInfo.PropertyType == typeof(long)))
    //                throw new NotSupportedException($"[AutoId] is only valid for Guid properties for {modelType.Name}.{propertyInfo.Name} integer property use [AutoIncrement] instead");

    //            var aliasAttr = propertyInfo.FirstAttribute<AliasAttribute>();

    //            var indexAttr = propertyInfo.FirstAttribute<IndexAttribute>();
    //            var isIndex = indexAttr != null;
    //            var isUnique = isIndex && indexAttr.Unique;

    //            var stringLengthAttr = propertyInfo.CalculateStringLength(decimalAttribute);

    //            var defaultValueAttr = propertyInfo.FirstAttribute<DefaultAttribute>();

    //            var referencesAttr = propertyInfo.FirstAttribute<ReferencesAttribute>();
    //            var fkAttr = propertyInfo.FirstAttribute<ForeignKeyAttribute>();
    //            var customFieldAttr = propertyInfo.FirstAttribute<CustomFieldAttribute>();
    //            var chkConstraintAttr = propertyInfo.FirstAttribute<CheckConstraintAttribute>();

    //            var order = propertyInfoIdx++;
    //            if (customFieldAttr != null) order = customFieldAttr.Order;

    //            var fieldDefinition = new FieldDefinition
    //            {
    //                ModelDef = modelDef,
    //                Name = propertyInfo.Name,
    //                Alias = aliasAttr?.Name,
    //                FieldType = propertyType,
    //                FieldTypeDefaultValue = isNullable ? null : propertyType.GetDefaultValue(),
    //                TreatAsType = treatAsType,
    //                PropertyInfo = propertyInfo,
    //                IsNullable = isNullable,
    //                IsPrimaryKey = isPrimaryKey,
    //                AutoIncrement = isPrimaryKey && isAutoIncrement,
    //                AutoId = isAutoId,
    //                IsIndexed = !isPrimaryKey && isIndex,
    //                IsUniqueIndex = isUnique,
    //                IsClustered = indexAttr?.Clustered == true,
    //                IsNonClustered = indexAttr?.NonClustered == true,
    //                IndexName = indexAttr?.Name,
    //                IsRowVersion = isRowVersion,
    //                IgnoreOnInsert = propertyInfo.HasAttributeCached<IgnoreOnInsertAttribute>(),
    //                IgnoreOnUpdate = propertyInfo.HasAttributeCached<IgnoreOnUpdateAttribute>(),
    //                ReturnOnInsert = propertyInfo.HasAttributeCached<ReturnOnInsertAttribute>(),
    //                FieldLength = stringLengthAttr?.MaximumLength,
    //                DefaultValue = defaultValueAttr?.DefaultValue,
    //                CheckConstraint = chkConstraintAttr?.Constraint,
    //                IsUniqueConstraint = propertyInfo.HasAttributeCached<UniqueAttribute>(),
    //                ForeignKey = fkAttr == null
    //                    ? referencesAttr != null ? new ForeignKeyConstraint(referencesAttr.Type) : null
    //                    : new ForeignKeyConstraint(fkAttr.Type, fkAttr.OnDelete, fkAttr.OnUpdate, fkAttr.ForeignKeyName),
    //                IsReference = isReference,
    //                GetValueFn = propertyInfo.CreateGetter(),
    //                SetValueFn = propertyInfo.CreateSetter(),
    //                Sequence = sequenceAttr?.Name,
    //                IsComputed = computeAttr != null || computedAttr != null || customSelectAttr != null,
    //                IsPersisted = persistedAttr != null,
    //                ComputeExpression = computeAttr != null ? computeAttr.Expression : string.Empty,
    //                CustomSelect = customSelectAttr?.Sql,
    //                CustomInsert = propertyInfo.FirstAttribute<CustomInsertAttribute>()?.Sql,
    //                CustomUpdate = propertyInfo.FirstAttribute<CustomUpdateAttribute>()?.Sql,
    //                Scale = decimalAttribute?.Scale,
    //                BelongToModelName = belongToAttribute?.BelongToTableType.GetModelDefinition().ModelName,
    //                CustomFieldDefinition = customFieldAttr?.Sql,
    //                IsRefType = propertyType.IsRefType(),
    //                Order = order
    //            };

    //            if (referenceFieldAttr != null)
    //            {
    //                fieldDefinition.FieldReference = new FieldReference(fieldDefinition)
    //                {
    //                    RefModel = referenceFieldAttr.Model,
    //                    RefId = referenceFieldAttr.Id,
    //                    RefField = referenceFieldAttr.Field ?? propertyInfo.Name,
    //                };
    //            }

    //            if (isIgnored)
    //                modelDef.IgnoredFieldDefinitions.Add(fieldDefinition);
    //            else
    //                modelDef.FieldDefinitions.Add(fieldDefinition);

    //            if (isRowVersion)
    //                modelDef.RowVersion = fieldDefinition;
    //        }

    //        modelDef.AfterInit();

    //        Dictionary<Type, ModelDefinition> snapshot, newCache;
    //        do
    //        {
    //            snapshot = typeModelDefinitionMap;
    //            newCache = new Dictionary<Type, ModelDefinition>(typeModelDefinitionMap) { [modelType] = modelDef };

    //        } while (!ReferenceEquals(
    //                     Interlocked.CompareExchange(ref typeModelDefinitionMap, newCache, snapshot), snapshot));

    //        LicenseUtils.AssertValidUsage(LicenseFeature.OrmLite, QuotaType.Tables, typeModelDefinitionMap.Count);

    //        return modelDef;
    //    }

    //}

    /// <summary>
    /// Compute attribute.
    /// Use to indicate that a property is a Calculated Field.
    /// Use [Persisted] attribute to persist column
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class ComputeAttribute : AttributeBase
    {
        public string Expression { get; set; }

        public ComputeAttribute() : this(string.Empty) { }

        public ComputeAttribute(string expression)
        {
            Expression = expression;
        }
    }

    /// <summary>
    /// Ignore calculated C# Property from being persisted in RDBMS Table
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class ComputedAttribute : AttributeBase { }

    /// <summary>
    /// Whether to persist calculated column
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class PersistedAttribute : AttributeBase { }
    /// <summary>
    /// Populate with a field from a foreign table in AutoQuery and Load* APIs
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class ReferenceFieldAttribute : AttributeBase
    {
        /// <summary>
        /// Foreign Key Table name
        /// </summary>
        public Type Model { get; set; }

        /// <summary>
        /// The Field name on current Model to use for the Foreign Key Table Lookup 
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// Specify Field to reference (if different from property name)
        /// </summary>
        public string Field { get; set; }

        public ReferenceFieldAttribute() { }
        public ReferenceFieldAttribute(Type model, string id)
        {
            Model = model;
            Id = id;
        }

        public ReferenceFieldAttribute(Type model, string id, string field)
        {
            Model = model;
            Id = id;
            Field = field;
        }
    }
}
相关推荐
我是黄大仙8 分钟前
利用飞书多维表格自动发布版本
运维·服务器·数据库·飞书
曾经的三心草8 分钟前
Mysql之约束与事件
android·数据库·mysql·事件·约束
WuMingf_15 分钟前
redis
数据库·redis
张某布响丸辣23 分钟前
SQL中的时间类型:深入解析与应用
java·数据库·sql·mysql·oracle
P.H. Infinity2 小时前
【RabbitMQ】10-抽取MQ工具
数据库·分布式·rabbitmq
zgscwxd2 小时前
thinkphp6 --数据库操作 增删改查
数据库·thinkphp6
代码小鑫2 小时前
A031-基于SpringBoot的健身房管理系统设计与实现
java·开发语言·数据库·spring boot·后端
天天要nx2 小时前
D64【python 接口自动化学习】- python基础之数据库
数据库·python
精进攻城狮@3 小时前
Redis(value的数据类型)
数据库·redis