达梦数据库适配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;
        }
    }
}
相关推荐
阿猿收手吧!7 分钟前
【Redis】Redis入门以及什么是分布式系统{Redis引入+分布式系统介绍}
数据库·redis·缓存
奈葵11 分钟前
Spring Boot/MVC
java·数据库·spring boot
leegong2311119 分钟前
Oracle、PostgreSQL该学哪一个?
数据库·postgresql·oracle
中东大鹅24 分钟前
MongoDB基本操作
数据库·分布式·mongodb·hbase
夜光小兔纸1 小时前
Oracle 普通用户连接hang住处理方法
运维·数据库·oracle
兩尛2 小时前
订单状态定时处理、来单提醒和客户催单(day10)
java·前端·数据库
web2u2 小时前
MySQL 中如何进行 SQL 调优?
java·数据库·后端·sql·mysql·缓存
Elastic 中国社区官方博客3 小时前
使用 Elasticsearch 导航检索增强生成图表
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
小金的学习笔记3 小时前
RedisTemplate和Redisson的使用和区别
数据库·redis·缓存
新知图书4 小时前
MySQL用户授权、收回权限与查看权限
数据库·mysql·安全