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 preCreate  = modelType.FirstAttribute <PreCreateTableAttribute>();
            var postCreate = modelType.FirstAttribute <PostCreateTableAttribute>();
            var preDrop    = modelType.FirstAttribute <PreDropTableAttribute>();
            var postDrop   = modelType.FirstAttribute <PostDropTableAttribute>();

            modelDef = new ModelDefinition
            {
                ModelType          = modelType,
                Name               = modelType.Name,
                Alias              = modelAliasAttr?.Name,
                Schema             = schemaAttr?.Name,
                PreCreateTableSql  = preCreate?.Sql,
                PostCreateTableSql = postCreate?.Sql,
                PreDropTableSql    = preDrop?.Sql,
                PostDropTableSql   = postDrop?.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.HasAttribute <PrimaryKeyAttribute>());

            var hasIdField = CheckForIdField(objProperties);

            var i = 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 customSelectAttr  = propertyInfo.FirstAttribute <CustomSelectAttribute>();
                var decimalAttribute  = propertyInfo.FirstAttribute <DecimalLengthAttribute>();
                var belongToAttribute = propertyInfo.FirstAttribute <BelongToAttribute>();
                var isFirst           = i++ == 0;

                var isAutoId = propertyInfo.HasAttribute <AutoIdAttribute>();

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

                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(typeof(RequiredAttribute).Name)) ||
                                 isNullableType;

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

                Type treatAsType = null;
                if (propertyType.IsEnumFlags() || propertyType.HasAttribute <EnumAsIntAttribute>())
                {
                    treatAsType = Enum.GetUnderlyingType(propertyType);
                }

                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 referenceAttr     = propertyInfo.FirstAttribute <ReferenceAttribute>();
                var fkAttr            = propertyInfo.FirstAttribute <ForeignKeyAttribute>();
                var customFieldAttr   = propertyInfo.FirstAttribute <CustomFieldAttribute>();
                var chkConstraintAttr = propertyInfo.FirstAttribute <CheckConstraintAttribute>();

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

                var isIgnored = propertyInfo.HasAttribute <IgnoreAttribute>() ||
                                fieldDefinition.IsReference;
                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);
        }
        internal static ModelDefinition GetModelDefinition(this Type modelType)
        {
            ModelDefinition modelDef;

            if (typeModelDefinitionMap.TryGetValue(modelType, out modelDef))
            {
                return(modelDef);
            }

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

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

            var preCreate  = modelType.FirstAttribute <PreCreateTableAttribute>();
            var postCreate = modelType.FirstAttribute <PostCreateTableAttribute>();
            var preDrop    = modelType.FirstAttribute <PreDropTableAttribute>();
            var postDrop   = modelType.FirstAttribute <PostDropTableAttribute>();

            modelDef = new ModelDefinition
            {
                ModelType          = modelType,
                Name               = modelType.Name,
                Alias              = modelAliasAttr != null ? modelAliasAttr.Name : null,
                Schema             = schemaAttr != null ? schemaAttr.Name : null,
                PreCreateTableSql  = preCreate != null ? preCreate.Sql : null,
                PostCreateTableSql = postCreate != null ? postCreate.Sql : null,
                PreDropTableSql    = preDrop != null ? preDrop.Sql : null,
                PostDropTableSql   = postDrop != null ? postDrop.Sql : null,
            };

            modelDef.CompositeIndexes.AddRange(
                modelType.GetCustomAttributes(typeof(CompositeIndexAttribute), true).ToList()
                .ConvertAll(x => (CompositeIndexAttribute)x));

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

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

            var hasIdField = CheckForIdField(objProperties);

            var i = 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 decimalAttribute  = propertyInfo.FirstAttribute <DecimalLengthAttribute>();
                var belongToAttribute = propertyInfo.FirstAttribute <BelongToAttribute>();
                var isFirst           = i++ == 0;

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

                var isRowVersion = propertyInfo.Name == ModelDefinition.RowVersionName &&
                                   propertyInfo.PropertyType == typeof(ulong);

                var isNullableType = IsNullableType(propertyInfo.PropertyType);

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

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

                Type treatAsType = null;
                if (propertyType.IsEnumFlags())
                {
                    treatAsType = Enum.GetUnderlyingType(propertyType);
                }

                if (propertyType == typeof(TimeSpan))
                {
                    treatAsType = typeof(long);
                }

                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         referenceAttr      = propertyInfo.FirstAttribute <ReferenceAttribute>();
                var         foreignKeyAttr     = propertyInfo.FirstAttribute <ForeignKeyAttribute>();
                var         customFieldAttr    = propertyInfo.FirstAttribute <CustomFieldAttribute>();
                List <Type> autoIncrementTypes = new List <Type>()
                {
                    typeof(long), typeof(Int32), typeof(Int64)
                };
                var fieldDefinition = new FieldDefinition
                {
                    Name           = propertyInfo.Name,
                    Alias          = aliasAttr != null ? aliasAttr.Name : null,
                    FieldType      = propertyType,
                    TreatAsType    = treatAsType,
                    PropertyInfo   = propertyInfo,
                    IsNullable     = isNullable,
                    IsPrimaryKey   = isPrimaryKey,
                    AutoIncrement  = (isPrimaryKey && propertyInfo.HasAttributeNamed(typeof(AutoIncrementAttribute).Name)) || (propertyInfo.Name == "Id" && autoIncrementTypes.Contains(propertyInfo.PropertyType)) || (propertyInfo.Name == "AutoIncrmId" && !modelType.Name.EndsWith("History") && propertyInfo.HasAttributeNamed(typeof(AutoIncrementAttribute).Name) && autoIncrementTypes.Contains(propertyInfo.PropertyType)),
                    IsIndexed      = isIndex,
                    IsUnique       = isUnique,
                    IsClustered    = indexAttr != null && indexAttr.Clustered,
                    IsNonClustered = indexAttr != null && indexAttr.NonClustered,
                    IsRowVersion   = isRowVersion,
                    FieldLength    = stringLengthAttr != null
                        ? stringLengthAttr.MaximumLength
                        : (int?)null,
                    DefaultValue = defaultValueAttr != null ? defaultValueAttr.DefaultValue : null,
                    ForeignKey   = foreignKeyAttr == null
                        ? referencesAttr != null ? new ForeignKeyConstraint(referencesAttr.Type) : null
                        : new ForeignKeyConstraint(foreignKeyAttr.Type,
                                                   foreignKeyAttr.OnDelete,
                                                   foreignKeyAttr.OnUpdate,
                                                   foreignKeyAttr.ForeignKeyName),
                    IsReference           = referenceAttr != null && propertyType.IsClass,
                    GetValueFn            = propertyInfo.GetPropertyGetterFn(),
                    SetValueFn            = propertyInfo.GetPropertySetterFn(),
                    Sequence              = sequenceAttr != null ? sequenceAttr.Name : string.Empty,
                    IsComputed            = computeAttr != null,
                    ComputeExpression     = computeAttr != null ? computeAttr.Expression : string.Empty,
                    Scale                 = decimalAttribute != null ? decimalAttribute.Scale : (int?)null,
                    BelongToModelName     = belongToAttribute != null?belongToAttribute.BelongToTableType.GetModelDefinition().ModelName : null,
                    CustomFieldDefinition = customFieldAttr != null ? customFieldAttr.Sql                                                : null,
                    IsRefType             = propertyType.IsRefType(),
                };

                var isIgnored = propertyInfo.HasAttributeNamed(typeof(IgnoreAttribute).Name) ||
                                fieldDefinition.IsReference;
                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);
                newCache[modelType] = modelDef;
            } while (!ReferenceEquals(
                         Interlocked.CompareExchange(ref typeModelDefinitionMap, newCache, snapshot), snapshot));

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

            return(modelDef);
        }
        internal static ModelDefinition GetModelDefinition(this Type modelType)
        {
            ModelDefinition modelDef;

            if (typeModelDefinitionMap.TryGetValue(modelType, out modelDef))
                return modelDef;

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

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

            var preCreate = modelType.FirstAttribute<PreCreateTableAttribute>();
            var postCreate = modelType.FirstAttribute<PostCreateTableAttribute>();
            var preDrop = modelType.FirstAttribute<PreDropTableAttribute>();
            var postDrop = modelType.FirstAttribute<PostDropTableAttribute>();

            modelDef = new ModelDefinition
            {
                ModelType = modelType,
                Name = modelType.Name,
                Alias = modelAliasAttr != null ? modelAliasAttr.Name : null,
                Schema = schemaAttr != null ? schemaAttr.Name : null,
                PreCreateTableSql = preCreate != null ? preCreate.Sql : null,
                PostCreateTableSql = postCreate != null ? postCreate.Sql : null,
                PreDropTableSql = preDrop != null ? preDrop.Sql : null,
                PostDropTableSql = postDrop != null ? postDrop.Sql : null,
            };

            modelDef.CompositeIndexes.AddRange(
                modelType.GetCustomAttributes(typeof(CompositeIndexAttribute), true).ToList()
                .ConvertAll(x => (CompositeIndexAttribute)x));

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

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

            var hasIdField = CheckForIdField(objProperties);

            var i = 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 decimalAttribute = propertyInfo.FirstAttribute<DecimalLengthAttribute>();
                var belongToAttribute = propertyInfo.FirstAttribute<BelongToAttribute>();
                var isFirst = i++ == 0;

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

                var isRowVersion = propertyInfo.Name == ModelDefinition.RowVersionName
                    && propertyInfo.PropertyType == typeof(ulong);

                var isNullableType = IsNullableType(propertyInfo.PropertyType);

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

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

                Type treatAsType = null;
                if (propertyType.IsEnumFlags() || propertyType.HasAttribute<EnumAsIntAttribute>())
                    treatAsType = Enum.GetUnderlyingType(propertyType);

                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 referenceAttr = propertyInfo.FirstAttribute<ReferenceAttribute>();
                var foreignKeyAttr = propertyInfo.FirstAttribute<ForeignKeyAttribute>();
                var customFieldAttr = propertyInfo.FirstAttribute<CustomFieldAttribute>();

                var fieldDefinition = new FieldDefinition
                {
                    Name = propertyInfo.Name,
                    Alias = aliasAttr != null ? aliasAttr.Name : null,
                    FieldType = propertyType,
                    FieldTypeDefaultValue = propertyType.GetDefaultValue(),
                    TreatAsType = treatAsType,
                    PropertyInfo = propertyInfo,
                    IsNullable = isNullable,
                    IsPrimaryKey = isPrimaryKey,
                    AutoIncrement =
                        isPrimaryKey &&
                        propertyInfo.HasAttributeNamed(typeof(AutoIncrementAttribute).Name),
                    IsIndexed = !isPrimaryKey && isIndex,
                    IsUnique = isUnique,
                    IsClustered = indexAttr != null && indexAttr.Clustered,
                    IsNonClustered = indexAttr != null && indexAttr.NonClustered,
                    IsRowVersion = isRowVersion,
                    FieldLength = stringLengthAttr != null
                        ? stringLengthAttr.MaximumLength
                        : (int?)null,
                    DefaultValue = defaultValueAttr != null ? defaultValueAttr.DefaultValue : null,
                    ForeignKey = foreignKeyAttr == null
                        ? referencesAttr != null ? new ForeignKeyConstraint(referencesAttr.Type) : null
                        : new ForeignKeyConstraint(foreignKeyAttr.Type,
                                                    foreignKeyAttr.OnDelete,
                                                    foreignKeyAttr.OnUpdate,
                                                    foreignKeyAttr.ForeignKeyName),
                    IsReference = referenceAttr != null && propertyType.IsClass,
                    GetValueFn = propertyInfo.GetPropertyGetterFn(),
                    SetValueFn = propertyInfo.GetPropertySetterFn(),
                    Sequence = sequenceAttr != null ? sequenceAttr.Name : string.Empty,
                    IsComputed = computeAttr != null,
                    ComputeExpression = computeAttr != null ? computeAttr.Expression : string.Empty,
                    Scale = decimalAttribute != null ? decimalAttribute.Scale : (int?)null,
                    BelongToModelName = belongToAttribute != null ? belongToAttribute.BelongToTableType.GetModelDefinition().ModelName : null,
                    CustomFieldDefinition = customFieldAttr != null ? customFieldAttr.Sql : null,
                    IsRefType = propertyType.IsRefType(),
                };
                
                var isIgnored = propertyInfo.HasAttributeNamed(typeof(IgnoreAttribute).Name)
                    || fieldDefinition.IsReference;
                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);
                newCache[modelType] = modelDef;

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

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

            return modelDef;
        }
예제 #4
0
        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;

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