public static string GetQuotedColumnName(this IOrmLiteDialectProvider dialect,
     ModelDefinition tableDef, string fieldName)
 {
     return dialect.GetQuotedTableName(tableDef) +
         "." +
         dialect.GetQuotedColumnName(fieldName);
 }
 public static string GetQuotedColumnName(this IOrmLiteDialectProvider dialect,
     ModelDefinition modelDef, FieldDefinition fieldDef)
 {
     return dialect.GetQuotedTableName(modelDef.ModelName) +
         "." +
         dialect.GetQuotedColumnName(fieldDef.FieldName);
 }
        internal static ModelDefinition GetModelDefinition(this Type modelType)
        {
            lock (typeModelDefinitionMap)
            {
                ModelDefinition modelDef;
                if (!typeModelDefinitionMap.TryGetValue(modelType, out modelDef))
                {
                    var modelAliasAttr = modelType.FirstAttribute<AliasAttribute>();
                    modelDef = new ModelDefinition {
                        ModelType = modelType,
                        Name = modelType.Name,
                        Alias = modelAliasAttr != null ? modelAliasAttr.Name : null,
                    };

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

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

                    var hasIdField = CheckForIdField(objProperties);

                    var i = 0;
                    foreach (var propertyInfo in objProperties)
                    {
                        var isFirst = i++ == 0;

                        var isPrimaryKey = propertyInfo.Name == IdField
                                           || (!hasIdField && isFirst);

                        var isNullableType = IsNullableType(propertyInfo.PropertyType);

                        var isNullable = (!propertyInfo.PropertyType.IsValueType
                                            && propertyInfo.FirstAttribute<RequiredAttribute>() == null)
                                         || isNullableType;

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

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

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

                        var stringLengthAttr = propertyInfo.FirstAttribute<StringLengthAttribute>();

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

                        var referencesAttr = propertyInfo.FirstAttribute<ReferencesAttribute>();

                        var fieldDefinition = new FieldDefinition {
                            Name = propertyInfo.Name,
                            Alias = aliasAttr != null ? aliasAttr.Name : null,
                            FieldType = propertyType,
                            PropertyInfo = propertyInfo,
                            IsNullable = isNullable,
                            IsPrimaryKey = isPrimaryKey,
                            AutoIncrement = isPrimaryKey && propertyInfo.FirstAttribute<AutoIncrementAttribute>() != null,
                            IsIndexed = isIndex,
                            IsUnique = isUnique,
                            FieldLength = stringLengthAttr != null ? stringLengthAttr.MaximumLength : (int?)null,
                            DefaultValue = defaultValueAttr != null ? defaultValueAttr.DefaultValue : null,
                            ReferencesType = referencesAttr != null ? referencesAttr.Type : null,
                            ConvertValueFn = OrmLiteConfig.DialectProvider.ConvertDbValue,
                            QuoteValueFn = OrmLiteConfig.DialectProvider.GetQuotedValue,
                            PropertyInvoker = OrmLiteConfig.PropertyInvoker,
                        };

                        modelDef.FieldDefinitions.Add(fieldDefinition);
                    }

                    typeModelDefinitionMap[modelType] = modelDef;
                }

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

			if (_typeModelDefinitionMap.TryGetValue(modelType, out modelDefinition))
				return modelDefinition;

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

			AliasAttribute aliasAttribute = modelType.FirstAttribute<AliasAttribute>();
			SchemaAttribute schemaAttribute = modelType.FirstAttribute<SchemaAttribute>();
			modelDefinition = new ModelDefinition
			{
				ModelType = modelType,
				Name = modelType.Name,
				Alias = aliasAttribute != null ? aliasAttribute.Name : null,
				Schema = schemaAttribute != null ? schemaAttribute.Name : null
			};

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

			List<PropertyInfo> objProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();

			bool hasIdField = CheckForIdField(objProperties);

			for (int i = 0; i < objProperties.Count; i++)
			{
				PropertyInfo propertyInfo = objProperties[i];

				SequenceAttribute sequenceAttribute = propertyInfo.FirstAttribute<SequenceAttribute>();
				ComputeAttribute computeAttribute = propertyInfo.FirstAttribute<ComputeAttribute>();
				DecimalLengthAttribute decimalAttribute = propertyInfo.FirstAttribute<DecimalLengthAttribute>();
				BelongToAttribute belongToAttribute = propertyInfo.FirstAttribute<BelongToAttribute>();
				bool isPrimaryKey = propertyInfo.Name == OrmLiteConfig.IdField || (!hasIdField && i == 0) || propertyInfo.HasAttributeNamed(typeof(PrimaryKeyAttribute));
				bool isNullableType = IsNullableType(propertyInfo.PropertyType);
				bool isNullable = (!propertyInfo.PropertyType.IsValueType && !propertyInfo.HasAttributeNamed(typeof(RequiredAttribute))) || isNullableType;
				Type propertyType = isNullableType ? Nullable.GetUnderlyingType(propertyInfo.PropertyType) : propertyInfo.PropertyType;
				aliasAttribute = propertyInfo.FirstAttribute<AliasAttribute>();
				IndexAttribute indexAttribute = propertyInfo.FirstAttribute<IndexAttribute>();
				bool isIndex = indexAttribute != null;
				bool isUnique = isIndex && indexAttribute.Unique;
				StringLengthAttribute stringLengthAttribute = propertyInfo.CalculateStringLength(decimalAttribute);
				DefaultAttribute defaultValueAttribute = propertyInfo.FirstAttribute<DefaultAttribute>();
				ReferencesAttribute referencesAttribute = propertyInfo.FirstAttribute<ReferencesAttribute>();
				//var referenceAttr = propertyInfo.FirstAttribute<ReferenceAttribute>();
				ForeignKeyAttribute foreignKeyAttribute = propertyInfo.FirstAttribute<ForeignKeyAttribute>();

				FieldDefinition fieldDefinition = new FieldDefinition();

				fieldDefinition.Name = propertyInfo.Name;
				fieldDefinition.Alias = aliasAttribute != null ? aliasAttribute.Name : null;
				fieldDefinition.FieldType = propertyType;
				fieldDefinition.PropertyInfo = propertyInfo;
				fieldDefinition.IsNullable = isNullable;
				fieldDefinition.IsPrimaryKey = isPrimaryKey;
				fieldDefinition.AutoIncrement = isPrimaryKey && propertyInfo.HasAttributeNamed(typeof(AutoIncrementAttribute));
				fieldDefinition.IsIndexed = isIndex;
				fieldDefinition.IsUnique = isUnique;
				fieldDefinition.FieldLength = stringLengthAttribute != null ? stringLengthAttribute.MaximumLength : (int?)null;
				fieldDefinition.DefaultValue = defaultValueAttribute != null ? defaultValueAttribute.DefaultValue : null;
				fieldDefinition.ForeignKey = foreignKeyAttribute == null ? referencesAttribute != null ? new ForeignKeyConstraint(referencesAttribute.Type) : null : new ForeignKeyConstraint(foreignKeyAttribute.Type, foreignKeyAttribute.OnDelete, foreignKeyAttribute.OnUpdate, foreignKeyAttribute.ForeignKeyName);
				//fieldDefinition.IsReference = referenceAttr != null && propertyType.IsClass;
				fieldDefinition.GetValueFn = propertyInfo.GetPropertyGetterFn();
				fieldDefinition.SetValueFn = propertyInfo.GetPropertySetterFn();
				fieldDefinition.Sequence = sequenceAttribute != null ? sequenceAttribute.Name : string.Empty;
				fieldDefinition.IsComputed = computeAttribute != null;
				fieldDefinition.ComputeExpression = computeAttribute != null ? computeAttribute.Expression : string.Empty;
				fieldDefinition.Scale = decimalAttribute != null ? decimalAttribute.Scale : (int?)null;
				fieldDefinition.BelongToModelName = belongToAttribute != null ? belongToAttribute.BelongToTableType.GetModelDefinition().ModelName : null;

				var isIgnored = propertyInfo.HasAttributeNamed(typeof(IgnoreAttribute)); // || fieldDefinition.IsReference;
				if (isIgnored)
				{
					modelDefinition.IgnoredFieldDefinitions.Add(fieldDefinition);
				}
				else
				{
					modelDefinition.FieldDefinitions.Add(fieldDefinition);
				}
			}

			//modelDefinition.SqlSelectAllFromTable = "SELECT {0} FROM {1} ".Fmt(OrmLiteConfig.DialectProvider.GetColumnNames(modelDefinition), OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDefinition));

			Dictionary<Type, ModelDefinition> snapshot, newCache;
			do
			{
				snapshot = _typeModelDefinitionMap;
				newCache = new Dictionary<Type, ModelDefinition>(_typeModelDefinitionMap);
				newCache[modelType] = modelDefinition;
			}
			while (!ReferenceEquals(Interlocked.CompareExchange(ref _typeModelDefinitionMap, newCache, snapshot), snapshot));

			return modelDefinition;
		}
Ejemplo n.º 5
0
        protected virtual SqlExpression <T> InternalJoin(string joinType, Expression joinExpr, ModelDefinition sourceDef, ModelDefinition targetDef, JoinFormatDelegate joinFormat = null)
        {
            PrefixFieldWithTableName = true;

            // Changes how Sql Expressions are generated.
            useFieldName = true;
            this.Sep     = " ";

            if (!tableDefs.Contains(sourceDef))
            {
                tableDefs.Add(sourceDef);
            }
            if (!tableDefs.Contains(targetDef))
            {
                tableDefs.Add(targetDef);
            }

            var isCrossJoin = "CROSS JOIN".Equals(joinType);

            var sqlExpr = joinExpr != null
                ? InternalCreateSqlFromExpression(joinExpr, isCrossJoin)
                : InternalCreateSqlFromDefinitions(sourceDef, targetDef, isCrossJoin);

            var joinDef = tableDefs.Contains(targetDef) && !tableDefs.Contains(sourceDef)
                ? sourceDef
                : targetDef;

            FromExpression += joinFormat != null
                ? $" {joinType} {joinFormat(DialectProvider, joinDef, sqlExpr)}"
                : $" {joinType} {SqlTable(joinDef)} {sqlExpr}";

            return(this);
        }
        public static FieldDefinition GetSelfRefFieldDefIfExists(this ModelDefinition modelDef, ModelDefinition refModelDef, FieldDefinition fieldDef)
        {
            var refField = (fieldDef == null ? null
                 : modelDef.FieldDefinitions.FirstOrDefault(x => x.ForeignKey != null && x.ForeignKey.ReferenceType == refModelDef.ModelType &&
                                                            fieldDef.IsSelfRefField(x)))
                           ?? modelDef.FieldDefinitions.FirstOrDefault(x => x.ForeignKey != null && x.ForeignKey.ReferenceType == refModelDef.ModelType)
                           ?? modelDef.FieldDefinitions.FirstOrDefault(refModelDef.IsRefField);

            return(refField);
        }
Ejemplo n.º 7
0
        public static Tuple <FieldDefinition, int, IOrmLiteConverter>[] GetIndexFieldsCache(this IDataReader reader,
                                                                                            ModelDefinition modelDefinition,
                                                                                            IOrmLiteDialectProvider dialect,
                                                                                            HashSet <string> onlyFields = null,
                                                                                            int startPos = 0,
                                                                                            int?endPos   = null)
        {
            var end      = endPos.GetValueOrDefault(reader.FieldCount);
            var cacheKey = (startPos == 0 && end == reader.FieldCount && onlyFields == null)
                            ? new IndexFieldsCacheKey(reader, modelDefinition, dialect)
                            : null;

            Tuple <FieldDefinition, int, IOrmLiteConverter>[] value;
            if (cacheKey != null)
            {
                lock (indexFieldsCache)
                {
                    if (indexFieldsCache.TryGetValue(cacheKey, out value))
                    {
                        return(value);
                    }
                }
            }

            var cache              = new List <Tuple <FieldDefinition, int, IOrmLiteConverter> >();
            var ignoredFields      = modelDefinition.IgnoredFieldDefinitions;
            var remainingFieldDefs = modelDefinition.FieldDefinitionsArray
                                     .Where(x => !ignoredFields.Contains(x) && x.SetValueFn != null).ToList();

            var mappedReaderColumns = new bool[end];

            for (var i = startPos; i < end; i++)
            {
                var columnName = reader.GetName(i);
                var fieldDef   = modelDefinition.GetFieldDefinition(columnName);
                if (fieldDef == null)
                {
                    foreach (var def in modelDefinition.FieldDefinitionsArray)
                    {
                        if (string.Equals(dialect.NamingStrategy.GetColumnName(def.FieldName), columnName,
                                          StringComparison.OrdinalIgnoreCase))
                        {
                            fieldDef = def;
                            break;
                        }
                    }
                }

                if (fieldDef != null && !ignoredFields.Contains(fieldDef) && fieldDef.SetValueFn != null)
                {
                    remainingFieldDefs.Remove(fieldDef);
                    mappedReaderColumns[i] = true;
                    cache.Add(Tuple.Create(fieldDef, i, dialect.GetConverterBestMatch(fieldDef)));
                }
            }

            if (remainingFieldDefs.Count > 0 && onlyFields == null)
            {
                var dbFieldMap = new Dictionary <string, int>(StringComparer.OrdinalIgnoreCase);
                for (var i = startPos; i < end; i++)
                {
                    if (!mappedReaderColumns[i])
                    {
                        var fieldName = reader.GetName(i);
                        dbFieldMap[fieldName] = i;
                    }
                }

                if (dbFieldMap.Count > 0)
                {
                    foreach (var fieldDef in remainingFieldDefs)
                    {
                        var index = FindColumnIndex(dialect, fieldDef, dbFieldMap);
                        if (index != NotFound)
                        {
                            cache.Add(Tuple.Create(fieldDef, index, dialect.GetConverterBestMatch(fieldDef)));
                        }
                    }
                }
            }

            var result = cache.ToArray();

            if (cacheKey != null)
            {
                lock (indexFieldsCache)
                {
                    if (indexFieldsCache.TryGetValue(cacheKey, out value))
                    {
                        return(value);
                    }
                    if (indexFieldsCache.Count < maxCachedIndexFields)
                    {
                        indexFieldsCache.Add(cacheKey, result);
                    }
                }
            }

            return(result);
        }
Ejemplo n.º 8
0
        public string GetForeignKeyName(ModelDefinition modelDef, ModelDefinition refModelDef, INamingStrategy NamingStrategy, FieldDefinition fieldDef)
        {
            if (ForeignKeyName.IsNullOrEmpty())
            {
                var modelName = modelDef.IsInSchema
                    ? modelDef.Schema + "_" + NamingStrategy.GetTableName(modelDef.ModelName)
                    : NamingStrategy.GetTableName(modelDef.ModelName);

                var refModelName = refModelDef.IsInSchema
                    ? refModelDef.Schema + "_" + NamingStrategy.GetTableName(refModelDef.ModelName)
                    : NamingStrategy.GetTableName(refModelDef.ModelName);

                var fkName = string.Format("FK_{0}_{1}_{2}", modelName, refModelName, fieldDef.FieldName);
                return NamingStrategy.ApplyNameRestrictions(fkName);
            }
            else { return ForeignKeyName; }
        }
Ejemplo n.º 9
0
        internal static void SetRefSelfChildResults <Parent>(List <Parent> parents, FieldDefinition fieldDef, ModelDefinition refModelDef, FieldDefinition refSelf, IList childResults)
        {
            var map = new Dictionary <object, object>();

            foreach (var result in childResults)
            {
                var pkValue = refModelDef.PrimaryKey.GetValue(result);
                map[pkValue] = result;
            }

            foreach (var result in parents)
            {
                object childResult;
                var    fkValue = refSelf.GetValue(result);
                if (fkValue != null && map.TryGetValue(fkValue, out childResult))
                {
                    fieldDef.SetValueFn(result, childResult);
                }
            }
        }
Ejemplo n.º 10
0
 public static string GetColumnNames(this ModelDefinition modelDef, IOrmLiteDialectProvider dialect)
 {
     return(dialect.GetColumnNames(modelDef));
 }
        internal static async Task <object> GetRowVersionAsync(this IDbCommand dbCmd, ModelDefinition modelDef, object id, CancellationToken token)
        {
            var sql        = dbCmd.RowVersionSql(modelDef, id);
            var rowVersion = await dbCmd.ScalarAsync <object>(sql, token);

            return(dbCmd.GetDialectProvider().FromDbRowVersion(modelDef.RowVersion.FieldType, rowVersion));
        }
Ejemplo n.º 12
0
        protected virtual SqlExpression <T> InternalJoin(string joinType, Expression joinExpr, ModelDefinition sourceDef, ModelDefinition targetDef, TableOptions options = null)
        {
            PrefixFieldWithTableName = true;

            Reset();

            var joinFormat = options?.JoinFormat;

            if (options?.Alias != null) //Set joinAlias
            {
                options.ParamName = joinExpr is LambdaExpression l && l.Parameters.Count == 2
                    ? l.Parameters[1].Name
                    : null;
                if (options.ParamName != null)
                {
                    joinFormat       = null;
                    options.ModelDef = targetDef;
                    joinAlias        = options;
                }
            }


            if (!tableDefs.Contains(sourceDef))
            {
                tableDefs.Add(sourceDef);
            }
            if (!tableDefs.Contains(targetDef))
            {
                tableDefs.Add(targetDef);
            }

            var isCrossJoin = "CROSS JOIN" == joinType;

            var sqlExpr = joinExpr != null
                ? InternalCreateSqlFromExpression(joinExpr, isCrossJoin)
                : InternalCreateSqlFromDefinitions(sourceDef, targetDef, isCrossJoin);

            var joinDef = tableDefs.Contains(targetDef) && !tableDefs.Contains(sourceDef)
                ? sourceDef
                : targetDef;

            FromExpression += joinFormat != null
                ? $" {joinType} {joinFormat(DialectProvider, joinDef, sqlExpr)}"
                : joinAlias != null
                    ? $" {joinType} {SqlTable(joinDef)} {DialectProvider.GetQuotedName(joinAlias.Alias)} {sqlExpr}"
                    : $" {joinType} {SqlTable(joinDef)} {sqlExpr}";


            if (joinAlias != null) //Unset joinAlias
            {
                joinAlias = null;
                if (options != null)
                {
                    options.ParamName = null;
                    options.ModelDef  = null;
                }
            }

            return(this);
        }
Ejemplo n.º 13
0
 public SqlExpressionVisitor()
 {
     modelDef = typeof(T).GetModelDefinition();
 }
        internal static ModelDefinition GetModelDefinition(this Type modelType)
        {
            ModelDefinition modelDef;
                        
            if (typeModelDefinitionMap.TryGetValue(modelType, out modelDef))
                return modelDef;

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

            var fromAttr= modelType.FirstAttribute<SelectFromAttribute>();
            string tableAlias; //=null;
            string modelName=null;
            Type fromType=null;
            StringBuilder join = new StringBuilder();
            
            if (fromAttr!=null){
                tableAlias= fromAttr.Alias;
                fromType=  fromAttr.From;
                modelName= fromType.GetModelDefinition().ModelName;
            }
            else if (modelType.BaseType!=typeof(Object)){
                fromType= modelType.BaseType;
                modelName=fromType.GetModelDefinition().ModelName;
                tableAlias= modelName;
            }
            else
                tableAlias=modelAliasAttr != null ? modelAliasAttr.Name : modelType.Name;
            
            //if(fromType!=null){
                
                var joinAttrList = modelType.GetCustomAttributes(typeof(JoinToAttribute), true).ToList()
                    .ConvertAll(x => (JoinToAttribute)x).OrderBy(x=>x.Order).ToList();
                
                foreach(var ja in joinAttrList){
                    string parentField;
                    string childField;

                    FieldDefinition fd;
                    if(ja.Parent==null && fromType==null)
                        fd=null;
                    else 
                        fd= ((ja.Parent!=null)? ja.Parent:fromType).
                        GetModelDefinition().FieldDefinitions.FirstOrDefault(x=>x.Name==ja.ParentProperty);

                    if(fd!=default(FieldDefinition) ) 
                        parentField= fd.FieldName;
                    else
                    {
                        parentField= modelType.GetFieldName(ja.ParentProperty);
                    }
                    
                    fd= ja.Child.GetModelDefinition().FieldDefinitions.FirstOrDefault(x=>x.Name==ja.ChildProperty);
                    if(fd!=default(FieldDefinition) ) 
                        childField= fd.FieldName;
                    else
                        childField= ja.ChildProperty;
                    
                    join.AppendFormat("\n{0} Join {1} {2} on {3}={4}",ja.JoinType,
                        OrmLiteConfig.DialectProvider.GetQuotedName( ja.Child.GetModelDefinition().ModelName),
                        OrmLiteConfig.DialectProvider.GetQuotedName(ja.ChildAlias), 
                        (ja.ParentAlias.IsNullOrEmpty() && tableAlias.IsNullOrEmpty())?
                                  string.Format("{0}",OrmLiteConfig.DialectProvider.GetQuotedName(parentField))
                                  :string.Format("{0}.{1}",
                            OrmLiteConfig.DialectProvider.GetQuotedName(ja.ParentAlias??tableAlias),
                            OrmLiteConfig.DialectProvider.GetQuotedName(parentField)),


                        string.Format("{0}.{1}",
                            OrmLiteConfig.DialectProvider.GetQuotedName(ja.ChildAlias),
                            OrmLiteConfig.DialectProvider.GetQuotedName(childField))
                        );
                }
            //}
            

            var schemaAttr = modelType.FirstAttribute<SchemaAttribute>();
            modelDef = new ModelDefinition
            {
                ModelType = modelType,
                Name = modelName?? modelType.Name,
                Alias = modelAliasAttr != null ? modelAliasAttr.Name : null,
                Schema = schemaAttr != null ? schemaAttr.Name : null,
                TableAlias= tableAlias,
                Join = join.Length==0? null: join.ToString()
            };

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

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

            var hasIdField = CheckForIdField(objProperties);

            var i = 0;
            foreach (var propertyInfo in objProperties)
            {
                if (propertyInfo.FirstAttribute<IgnoreAttribute>()!=null) continue;
                var sequenceAttr = propertyInfo.FirstAttribute<SequenceAttribute>();
                var computeAttr= propertyInfo.FirstAttribute<ComputeAttribute>();
                var pkAttribute = propertyInfo.FirstAttribute<PrimaryKeyAttribute>();
                var decimalAttribute = propertyInfo.FirstAttribute<DecimalLengthAttribute>();
                var isFirst = i++ == 0;

                var isPrimaryKey = propertyInfo.Name ==OrmLiteConfig.IdField || (!hasIdField && isFirst)
                    || pkAttribute != null;

                var isNullableType = IsNullableType(propertyInfo.PropertyType);

                var isNullable = (!propertyInfo.PropertyType.IsValueType
                                   && propertyInfo.FirstAttribute<RequiredAttribute>() == null)
                                 || isNullableType;

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

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

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

                var stringLengthAttr = propertyInfo.FirstAttribute<StringLengthAttribute>();

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

                var referencesAttr = propertyInfo.FirstAttribute<ReferencesAttribute>();
                
                if(decimalAttribute != null  && stringLengthAttr==null)
                    stringLengthAttr= new StringLengthAttribute(decimalAttribute.Precision);
                
                var belongsToAttr= propertyInfo.FirstAttribute<BelongsToAttribute>();
                
                string fieldAlias=null;
                string belongsToAlias=null;
                string alias=null;
                
                if (belongsToAttr!=null){
                    belongsToAlias= belongsToAttr.ParentAlias;
                    fieldAlias = propertyInfo.Name;
                    var fd= belongsToAttr.Parent.GetModelDefinition().FieldDefinitions.
                        FirstOrDefault(x=>x.Name==(belongsToAttr.PropertyName?? propertyInfo.Name));
                    if(fd!=default(FieldDefinition) ) 
                        alias= fd.FieldName;
                    else alias= propertyInfo.Name ;
                }
                else if(fromType != null && fromType != modelType){
                    var fd= fromType.GetModelDefinition().FieldDefinitions.FirstOrDefault(x=>x.Name==propertyInfo.Name);
                    if(fd!=default(FieldDefinition) ){
                        alias= fd.FieldName;
                        fieldAlias = propertyInfo.Name;
                        belongsToAlias= tableAlias;
                    }
                }
                
                var fieldDefinition = new FieldDefinition
                {
                    Name = propertyInfo.Name,
                    Alias = alias?? (aliasAttr != null ? aliasAttr.Name : null),
                    FieldAlias= fieldAlias,
                    BelongsToAlias= belongsToAlias,
                    FieldType = propertyType,
                    PropertyInfo = propertyInfo,
                    IsNullable = isNullable,
                    IsPrimaryKey = isPrimaryKey,
                    AutoIncrement = isPrimaryKey && propertyInfo.FirstAttribute<AutoIncrementAttribute>() != null,
                    IsIndexed = isIndex,
                    IsUnique = isUnique,
                    FieldLength = stringLengthAttr != null ? stringLengthAttr.MaximumLength : (int?)null,
                    DefaultValue = defaultValueAttr != null ? defaultValueAttr.DefaultValue : null,
                    ReferencesType = referencesAttr != null ? referencesAttr.Type : null,
                    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,
                };

                modelDef.FieldDefinitions.Add(fieldDefinition);
            }
            modelDef.SqlSelectAllFromTable = "SELECT {0} FROM {1}{2}".Fmt(OrmLiteConfig.DialectProvider.GetColumnNames(modelDef),
                OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef),
                tableAlias.IsNullOrEmpty()?
                    "":
                    OrmLiteConfig.DialectProvider.GetQuotedName(tableAlias));

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

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

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

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

            var           fromAttr = modelType.FirstAttribute <SelectFromAttribute>();
            string        tableAlias; //=null;
            string        modelName = null;
            Type          fromType  = null;
            StringBuilder join      = new StringBuilder();

            if (fromAttr != null)
            {
                tableAlias = fromAttr.Alias;
                fromType   = fromAttr.From;
                modelName  = fromType.GetModelDefinition().ModelName;
            }
            else if (modelType.BaseType != typeof(Object))
            {
                fromType   = modelType.BaseType;
                modelName  = fromType.GetModelDefinition().ModelName;
                tableAlias = modelName;
            }
            else
            {
                tableAlias = modelAliasAttr != null ? modelAliasAttr.Name : modelType.Name;
            }

            //if(fromType!=null){

            var joinAttrList = modelType.GetCustomAttributes(typeof(JoinToAttribute), true).ToList()
                               .ConvertAll(x => (JoinToAttribute)x).OrderBy(x => x.Order).ToList();

            foreach (var ja in joinAttrList)
            {
                string parentField;
                string childField;

                FieldDefinition fd;
                if (ja.Parent == null && fromType == null)
                {
                    fd = null;
                }
                else
                {
                    fd = ((ja.Parent != null)? ja.Parent:fromType).
                         GetModelDefinition().FieldDefinitions.FirstOrDefault(x => x.Name == ja.ParentProperty);
                }

                if (fd != default(FieldDefinition))
                {
                    parentField = fd.FieldName;
                }
                else
                {
                    parentField = modelType.GetFieldName(ja.ParentProperty);
                }

                fd = ja.Child.GetModelDefinition().FieldDefinitions.FirstOrDefault(x => x.Name == ja.ChildProperty);
                if (fd != default(FieldDefinition))
                {
                    childField = fd.FieldName;
                }
                else
                {
                    childField = ja.ChildProperty;
                }

                join.AppendFormat("\n{0} Join {1} {2} on {3}={4}", ja.JoinType,
                                  OrmLiteConfig.DialectProvider.GetQuotedName(ja.Child.GetModelDefinition().ModelName),
                                  OrmLiteConfig.DialectProvider.GetQuotedName(ja.ChildAlias),
                                  (ja.ParentAlias.IsNullOrEmpty() && tableAlias.IsNullOrEmpty())?
                                  string.Format("{0}", OrmLiteConfig.DialectProvider.GetQuotedName(parentField))
                                  :string.Format("{0}.{1}",
                                                 OrmLiteConfig.DialectProvider.GetQuotedName(ja.ParentAlias ?? tableAlias),
                                                 OrmLiteConfig.DialectProvider.GetQuotedName(parentField)),


                                  string.Format("{0}.{1}",
                                                OrmLiteConfig.DialectProvider.GetQuotedName(ja.ChildAlias),
                                                OrmLiteConfig.DialectProvider.GetQuotedName(childField))
                                  );
            }
            //}


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

            modelDef = new ModelDefinition
            {
                ModelType  = modelType,
                Name       = modelName ?? modelType.Name,
                Alias      = modelAliasAttr != null ? modelAliasAttr.Name : null,
                Schema     = schemaAttr != null ? schemaAttr.Name : null,
                TableAlias = tableAlias,
                Join       = join.Length == 0? null: join.ToString()
            };

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

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

            var hasIdField = CheckForIdField(objProperties);

            var i = 0;

            foreach (var propertyInfo in objProperties)
            {
                if (propertyInfo.FirstAttribute <IgnoreAttribute>() != null)
                {
                    continue;
                }
                var sequenceAttr     = propertyInfo.FirstAttribute <SequenceAttribute>();
                var computeAttr      = propertyInfo.FirstAttribute <ComputeAttribute>();
                var pkAttribute      = propertyInfo.FirstAttribute <PrimaryKeyAttribute>();
                var decimalAttribute = propertyInfo.FirstAttribute <DecimalLengthAttribute>();
                var isFirst          = i++ == 0;

                var isPrimaryKey = propertyInfo.Name == OrmLiteConfig.IdField || (!hasIdField && isFirst) ||
                                   pkAttribute != null;

                var isNullableType = IsNullableType(propertyInfo.PropertyType);

                var isNullable = (!propertyInfo.PropertyType.IsValueType &&
                                  propertyInfo.FirstAttribute <RequiredAttribute>() == null) ||
                                 isNullableType;

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

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

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

                var stringLengthAttr = propertyInfo.FirstAttribute <StringLengthAttribute>();

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

                var referencesAttr = propertyInfo.FirstAttribute <ReferencesAttribute>();

                if (decimalAttribute != null && stringLengthAttr == null)
                {
                    stringLengthAttr = new StringLengthAttribute(decimalAttribute.Precision);
                }

                var belongsToAttr = propertyInfo.FirstAttribute <BelongsToAttribute>();

                string fieldAlias     = null;
                string belongsToAlias = null;
                string alias          = null;

                if (belongsToAttr != null)
                {
                    belongsToAlias = belongsToAttr.ParentAlias;
                    fieldAlias     = propertyInfo.Name;
                    var fd = belongsToAttr.Parent.GetModelDefinition().FieldDefinitions.
                             FirstOrDefault(x => x.Name == (belongsToAttr.PropertyName ?? propertyInfo.Name));
                    if (fd != default(FieldDefinition))
                    {
                        alias = fd.FieldName;
                    }
                    else
                    {
                        alias = propertyInfo.Name;
                    }
                }
                else if (fromType != null && fromType != modelType)
                {
                    var fd = fromType.GetModelDefinition().FieldDefinitions.FirstOrDefault(x => x.Name == propertyInfo.Name);
                    if (fd != default(FieldDefinition))
                    {
                        alias          = fd.FieldName;
                        fieldAlias     = propertyInfo.Name;
                        belongsToAlias = tableAlias;
                    }
                }

                var fieldDefinition = new FieldDefinition
                {
                    Name              = propertyInfo.Name,
                    Alias             = alias ?? (aliasAttr != null ? aliasAttr.Name : null),
                    FieldAlias        = fieldAlias,
                    BelongsToAlias    = belongsToAlias,
                    FieldType         = propertyType,
                    PropertyInfo      = propertyInfo,
                    IsNullable        = isNullable,
                    IsPrimaryKey      = isPrimaryKey,
                    AutoIncrement     = isPrimaryKey && propertyInfo.FirstAttribute <AutoIncrementAttribute>() != null,
                    IsIndexed         = isIndex,
                    IsUnique          = isUnique,
                    FieldLength       = stringLengthAttr != null ? stringLengthAttr.MaximumLength : (int?)null,
                    DefaultValue      = defaultValueAttr != null ? defaultValueAttr.DefaultValue : null,
                    ReferencesType    = referencesAttr != null ? referencesAttr.Type : null,
                    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,
                };

                modelDef.FieldDefinitions.Add(fieldDefinition);
            }
            modelDef.SqlSelectAllFromTable = "SELECT {0} FROM {1}{2}".Fmt(OrmLiteConfig.DialectProvider.GetColumnNames(modelDef),
                                                                          OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef),
                                                                          tableAlias.IsNullOrEmpty()?
                                                                          "":
                                                                          OrmLiteConfig.DialectProvider.GetQuotedName(tableAlias));

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

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

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

            var modelAliasAttr = modelType.FirstAttribute<AliasAttribute>();
            var schemaAttr = modelType.FirstAttribute<SchemaAttribute>();
            modelDef = new ModelDefinition {
                ModelType = modelType,
                Name = modelType.Name,
                Alias = modelAliasAttr != null ? modelAliasAttr.Name : null,
                Schema = schemaAttr != null ? schemaAttr.Name : null
            };

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

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

            var hasIdField = CheckForIdField(objProperties);

            var i = 0;
            foreach (var propertyInfo in objProperties)
            {
                var sequenceAttr = propertyInfo.FirstAttribute<SequenceAttribute>();
                var computeAttr= propertyInfo.FirstAttribute<ComputeAttribute>();
                var pkAttribute = propertyInfo.FirstAttribute<PrimaryKeyAttribute>();
                var decimalAttribute = propertyInfo.FirstAttribute<DecimalLengthAttribute>();
                var belongToAttribute = propertyInfo.FirstAttribute<BelongToAttribute>();
                var isFirst = i++ == 0;

                var isPrimaryKey = propertyInfo.Name == OrmLiteConfig.IdField || (!hasIdField && isFirst)
                    || pkAttribute != null;

                var isNullableType = IsNullableType(propertyInfo.PropertyType);

                var isNullable = (!propertyInfo.PropertyType.IsValueType
                                   && propertyInfo.FirstAttribute<RequiredAttribute>() == null)
                                 || isNullableType;

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

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

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

                var stringLengthAttr = propertyInfo.FirstAttribute<StringLengthAttribute>();

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

                var referencesAttr = propertyInfo.FirstAttribute<ReferencesAttribute>();
                var foreignKeyAttr = propertyInfo.FirstAttribute<ForeignKeyAttribute>();

                if (decimalAttribute != null && stringLengthAttr == null)
                    stringLengthAttr = new StringLengthAttribute(decimalAttribute.Precision);

                var fieldDefinition = new FieldDefinition {
                    Name = propertyInfo.Name,
                    Alias = aliasAttr != null ? aliasAttr.Name : null,
                    FieldType = propertyType,
                    PropertyInfo = propertyInfo,
                    IsNullable = isNullable,
                    IsPrimaryKey = isPrimaryKey,
                    AutoIncrement =
                        isPrimaryKey &&
                        propertyInfo.FirstAttribute<AutoIncrementAttribute>() != null,
                    IsIndexed = isIndex,
                    IsUnique = isUnique,
                    FieldLength =
                        stringLengthAttr != null
                            ? stringLengthAttr.MaximumLength
                            : (int?)null,
                    DefaultValue =
                        defaultValueAttr != null ? defaultValueAttr.DefaultValue : null,
                    ForeignKey =
                        foreignKeyAttr == null
                            ? referencesAttr == null
                                  ? null
                                  : new ForeignKeyConstraint(referencesAttr.Type)
                            : new ForeignKeyConstraint(foreignKeyAttr.Type,
                                                       foreignKeyAttr.OnDelete,
                                                       foreignKeyAttr.OnUpdate,
                                                       foreignKeyAttr.ForeignKeyName),
                    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, 
                };

                if (propertyInfo.FirstAttribute<IgnoreAttribute>() != null)
                  modelDef.IgnoredFieldDefinitions.Add(fieldDefinition);
                else
                  modelDef.FieldDefinitions.Add(fieldDefinition);                
            }

            modelDef.SqlSelectAllFromTable = "SELECT {0} FROM {1} ".Fmt(OrmLiteConfig.DialectProvider.GetColumnNames(modelDef),
                                                                        OrmLiteConfig.DialectProvider.GetQuotedTableName(
                                                                            modelDef));
            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));

            return modelDef;
        }
        public static FieldDefinition GetRefFieldDefIfExists(this ModelDefinition modelDef, ModelDefinition refModelDef)
        {
            var refField =
                refModelDef.FieldDefinitions.FirstOrDefault(x => x.ForeignKey != null && x.ForeignKey.ReferenceType == modelDef.ModelType &&
                                                            modelDef.IsRefField(x))
                ?? refModelDef.FieldDefinitions.FirstOrDefault(x => x.ForeignKey != null && x.ForeignKey.ReferenceType == modelDef.ModelType)
                ?? refModelDef.FieldDefinitions.FirstOrDefault(modelDef.IsRefField);

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

            modelDef = new ModelDefinition {
                ModelType = modelType,
                Name      = modelType.Name,
                Alias     = modelAliasAttr != null ? modelAliasAttr.Name : null,
                Schema    = schemaAttr != null ? schemaAttr.Name : null
            };

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

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

            var hasIdField = CheckForIdField(objProperties);

            var i = 0;

            foreach (var propertyInfo in objProperties)
            {
                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 = propertyInfo.Name == OrmLiteConfig.IdField || (!hasIdField && isFirst) ||
                                   propertyInfo.HasAttributeNamed(typeof(PrimaryKeyAttribute).Name);

                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;

                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 fieldDefinition = new FieldDefinition {
                    Name          = propertyInfo.Name,
                    Alias         = aliasAttr != null ? aliasAttr.Name : null,
                    FieldType     = propertyType,
                    PropertyInfo  = propertyInfo,
                    IsNullable    = isNullable,
                    IsPrimaryKey  = isPrimaryKey,
                    AutoIncrement =
                        isPrimaryKey &&
                        propertyInfo.HasAttributeNamed(typeof(AutoIncrementAttribute).Name),
                    IsIndexed   = isIndex,
                    IsUnique    = isUnique,
                    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,
                };

                var isIgnored = propertyInfo.HasAttributeNamed(typeof(IgnoreAttribute).Name) ||
                                fieldDefinition.IsReference;
                if (isIgnored)
                {
                    modelDef.IgnoredFieldDefinitions.Add(fieldDefinition);
                }
                else
                {
                    modelDef.FieldDefinitions.Add(fieldDefinition);
                }
            }

            modelDef.SqlSelectAllFromTable = "SELECT {0} FROM {1} "
                                             .Fmt(OrmLiteConfig.DialectProvider.GetColumnNames(modelDef),
                                                  OrmLiteConfig.DialectProvider.GetQuotedTableName(modelDef));

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