/// <summary> /// Constructs the decomposed objects as necessary. /// </summary> /// <param name="code">The code.</param> /// <param name="path">The path.</param> /// <param name="properties">The properties.</param> private static void ConstructDecomposedObjects(StringBuilder code, string path, PropertyMetadataCollection properties) { foreach (var property in properties) { if (property.Decompose) { if (property.CanWrite) { code.AppendLine($" if ({path}.{property.Name} == null)"); code.AppendLine($" {path}.{property.Name} = new {property.PropertyType.FullName}();"); } ConstructDecomposedObjects(code, path + "." + property.Name, MetadataCache.GetMetadata(property.PropertyType).Properties); } } }
internal ClassMetadata(TypeInfo typeInfo) { #if !DataAnnotations_Missing var table = (TableAttribute)typeInfo.GetCustomAttributes(typeof(TableAttribute), true).SingleOrDefault(); if (table != null) { MappedTableName = table.Name; MappedSchemaName = table.Schema; } #endif #if Weird_Reflection var shadowingProperties = (from p in typeInfo.DeclaredProperties where IsHidingMember(p) select p).ToList(); var propertyList = typeInfo.DeclaredProperties.ToList(); #elif TypeInfo_Is_Not_Type var type = typeInfo.AsType(); var shadowingProperties = (from p in type.GetProperties() where IsHidingMember(p) select p).ToList(); var propertyList = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); #else var shadowingProperties = (from p in typeInfo.GetProperties() where IsHidingMember(p) select p).ToList(); var propertyList = typeInfo.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); #endif Func<PropertyInfo, bool> IsHidden = propertyInfo => !shadowingProperties.Contains(propertyInfo) && shadowingProperties.Any(p => p.Name == propertyInfo.Name); Properties = new PropertyMetadataCollection(propertyList.Where(p => !IsHidden(p)).Select(p => new PropertyMetadata(p))); //List the properties that are affected when the indicated property is modified. foreach (var property in Properties) foreach (CalculatedFieldAttribute fieldList in property.PropertyInfo.GetCustomAttributes(typeof(CalculatedFieldAttribute), true)) foreach (var field in fieldList.SourceProperties) { if (!Properties.Contains(field)) throw new InvalidOperationException(string.Format("Cannot find property {0} on type {1}. This is needed for the calculated property {2}", field, typeInfo.FullName, property.Name)); Properties[field].AddCalculatedField(property); } foreach (var property in Properties) property.EndInit(); Constructors = new ConstructorMetadataCollection(typeInfo.DeclaredConstructors); }
/// <summary> /// Sets the properties. /// </summary> /// <param name="code">The code being generated.</param> /// <param name="columns">The columns in the data reader.</param> /// <param name="properties">The properties for the current object.</param> /// <param name="columnIndex">Index of the column being read.</param> /// <param name="path">The path to the object whose properties are being set.</param> /// <param name="decompositionPrefix">The decomposition prefix used when reading the column data.</param> private static void SetProperties(StringBuilder code, Dictionary <string, ColumnData> columns, PropertyMetadataCollection properties, int columnIndex, string path, string decompositionPrefix) { foreach (var property in properties) { if (property.Decompose) { SetProperties(code, columns, MetadataCache.GetMetadata(property.PropertyType).Properties, columnIndex, path + "." + property.Name, decompositionPrefix + property.DecompositionPrefix); } if (property.MappedColumnName == null) { continue; } ColumnData column; if (!columns.TryGetValue(decompositionPrefix + property.MappedColumnName, out column)) { continue; //not a valid column } if (column.Index != columnIndex) { continue; //we'll get it on another iteration } if (property.PropertyType == column.ColumnType || (property.PropertyType.Name == "Nullable`1" && property.PropertyType.IsGenericType && property.PropertyType.GenericTypeArguments[0] == column.ColumnType)) { if (property.PropertyType.IsClass || (property.PropertyType.Name == "Nullable`1" && property.PropertyType.IsGenericType)) { //null handler code.AppendLine($" if (reader.IsDBNull({column.Index}))"); code.AppendLine($" {path}.{property.Name} = null;"); code.AppendLine($" else"); code.AppendLine($" {path}.{property.Name} = {column.Getter}({column.Index});"); } else { //non-null handler code.AppendLine($" {path}.{property.Name} = {column.Getter}({column.Index});"); } } else //type casting is required { var propertyTypeName = MetadataCache.GetMetadata(property.PropertyType).CSharpFullName; if (property.PropertyType.IsClass || (property.PropertyType.Name == "Nullable`1" && property.PropertyType.IsGenericType)) { //null handler code.AppendLine($" if (reader.IsDBNull({column.Index}))"); code.AppendLine($" {path}.{property.Name} = null;"); code.AppendLine($" else"); code.AppendLine($" {path}.{property.Name} = ({propertyTypeName}){column.Getter}({column.Index});"); } else { //non-null handler code.AppendLine($" {path}.{property.Name} = ({propertyTypeName}){column.Getter}({column.Index});"); } } } }
/// <summary> /// Sets the properties. /// </summary> /// <param name="code">The code being generated.</param> /// <param name="columns">The columns in the data reader.</param> /// <param name="properties">The properties for the current object.</param> /// <param name="columnIndex">Index of the column being read.</param> /// <param name="path">The path to the object whose properties are being set.</param> /// <param name="decompositionPrefix">The decomposition prefix used when reading the column data.</param> private static void SetProperties(StringBuilder code, Dictionary <string, ColumnData> columns, PropertyMetadataCollection properties, int columnIndex, string path, string?decompositionPrefix) { foreach (var property in properties) { if (property.Decompose) { SetProperties(code, columns, MetadataCache.GetMetadata(property.PropertyType).Properties, columnIndex, path + "." + property.Name, decompositionPrefix + property.DecompositionPrefix); } if (property.MappedColumnName == null) { continue; } if (!columns.TryGetValue(decompositionPrefix + property.MappedColumnName, out var column)) { continue; //not a valid column } if (column.Index != columnIndex) { continue; //we'll get it on another iteration } if (property.PropertyType == column.ColumnType || (string.Equals(property.PropertyType.Name, "Nullable`1", StringComparison.Ordinal) && property.PropertyType.IsGenericType && property.PropertyType.GenericTypeArguments[0] == column.ColumnType)) { if (column.IsNullable && (property.PropertyType.IsClass || (string.Equals(property.PropertyType.Name, "Nullable`1", StringComparison.Ordinal) && property.PropertyType.IsGenericType))) { //null handler code.AppendLine($" if (reader.IsDBNull({column.Index}))"); code.AppendLine($" {path}.{property.Name} = null;"); code.AppendLine($" else"); code.AppendLine($" {path}.{property.Name} = {column.Getter}({column.Index});"); } else { //non-null handler code.AppendLine($" {path}.{property.Name} = {column.Getter}({column.Index});"); } } else //type casting is required { var propertyTypeName = MetadataCache.GetMetadata(property.PropertyType).CSharpFullName; string getterWithConversion; string?tempVariable = null; //special handling for OleDB if ((property.PropertyType == typeof(TimeSpan) || property.PropertyType == typeof(TimeSpan?)) && column.ColumnType == typeof(string)) { getterWithConversion = $"TimeSpan.Parse({column.Getter}({column.Index}), System.Globalization.CultureInfo.InvariantCulture);"; } else if ((property.PropertyType == typeof(TimeSpan) || property.PropertyType == typeof(TimeSpan?)) && column.ColumnType == typeof(object)) { getterWithConversion = $"TimeSpan.Parse((string){column.Getter}({column.Index}), System.Globalization.CultureInfo.InvariantCulture);"; } else if ((property.PropertyType == typeof(TimeSpan) || property.PropertyType == typeof(TimeSpan?)) && column.ColumnType == typeof(DateTime)) { getterWithConversion = $"({column.Getter}({column.Index})).TimeOfDay"; } else if ((property.PropertyType == typeof(TimeSpan) || property.PropertyType == typeof(TimeSpan?)) && column.ColumnType == typeof(DateTime)) { getterWithConversion = $"({column.Getter}({column.Index})).TimeOfDay"; } else if (property.PropertyType == typeof(char) && column.ColumnType == typeof(string)) { tempVariable = $"var temp{column.Index} = {column.Getter}({column.Index});"; getterWithConversion = $"temp{column.Index}.Length >= 1 ? temp{column.Index}[0] : default(char)"; } else if (property.PropertyType == typeof(char?) && column.ColumnType == typeof(string)) { tempVariable = $"var temp{column.Index} = {column.Getter}({column.Index});"; getterWithConversion = $"temp{column.Index}.Length >= 1 ? temp{column.Index}[0] : null"; } #if NET6_0_OR_GREATER else if ((property.PropertyType == typeof(DateOnly) || property.PropertyType == typeof(DateOnly?)) && column.ColumnType == typeof(DateTime)) { getterWithConversion = $"DateOnly.FromDateTime({column.Getter}({column.Index}))"; } else if ((property.PropertyType == typeof(TimeOnly) || property.PropertyType == typeof(TimeOnly?)) && column.ColumnType == typeof(DateTime)) { getterWithConversion = $"TimeOnly.FromDateTime({column.Getter}({column.Index}))"; } else if ((property.PropertyType == typeof(TimeOnly) || property.PropertyType == typeof(TimeOnly?)) && column.ColumnType == typeof(TimeSpan)) { getterWithConversion = $"TimeOnly.FromTimeSpan({column.Getter}({column.Index}))"; } //special handling for OleDB else if ((property.PropertyType == typeof(TimeOnly) || property.PropertyType == typeof(TimeOnly?)) && column.ColumnType == typeof(string)) { getterWithConversion = $"TimeOnly.Parse({column.Getter}({column.Index}), System.Globalization.CultureInfo.InvariantCulture);"; } else if ((property.PropertyType == typeof(TimeOnly) || property.PropertyType == typeof(TimeOnly?)) && column.ColumnType == typeof(object)) { getterWithConversion = $"TimeOnly.Parse((string){column.Getter}({column.Index}), System.Globalization.CultureInfo.InvariantCulture);"; } #endif else { //simple casting getterWithConversion = $"({propertyTypeName}){column.Getter}({column.Index})"; } if (column.IsNullable && (property.PropertyType.IsClass || (string.Equals(property.PropertyType.Name, "Nullable`1", StringComparison.Ordinal) && property.PropertyType.IsGenericType))) { //null handler code.AppendLine($" if (reader.IsDBNull({column.Index}))"); code.AppendLine($" {path}.{property.Name} = null;"); code.AppendLine($" else"); code.AppendLine(" {"); if (tempVariable != null) { code.AppendLine(tempVariable); } code.AppendLine($" {path}.{property.Name} = {getterWithConversion};"); code.AppendLine(" }"); } else { //non-null handler if (tempVariable != null) { code.AppendLine(tempVariable); } code.AppendLine($" {path}.{property.Name} = {getterWithConversion};"); } } } }