/// <summary> /// Initializes a new instance of the <see cref="FileColumnReflector"/> class. /// </summary> internal FileRecordReflector(Type type, FileFormatBase ff) { Type = type; TypeInfo = type.GetTypeInfo(); if (!TypeInfo.IsClass) { throw new ArgumentException($"Type '{type.Name}' must be a class.", nameof(type)); } // Get all the property/column metadata. FileFormat = ff; int i = 0; var columns = new List <FileColumnReflector>(); var children = new List <FileHierarchyReflector>(); foreach (var pi in TypeReflector.GetProperties(Type)) { i++; var fca = pi.GetCustomAttribute <FileColumnAttribute>(); var fha = pi.GetCustomAttribute <FileHierarchyAttribute>(); if (fca != null && fha != null) { throw new InvalidOperationException($"Type '{type.Name}' property '{pi.Name}' cannot specify both a FileColumnAttribute and FileHierarchyAttribute."); } if (fca != null) { columns.Add(new FileColumnReflector(i, fca, pi, FileFormat)); } if (fha != null) { var fhr = new FileHierarchyReflector(i, fha, pi, ff); if (children.SingleOrDefault(x => x.RecordIdentifier == fhr.RecordIdentifier) != null) { throw new InvalidOperationException($"Type '{type.Name}' property '{pi.Name}' FileHierarchyAttribute has a duplicate Record Identifier '{fhr.RecordIdentifier}' (must be unique within Type)."); } children.Add(new FileHierarchyReflector(i, fha, pi, ff)); } } // Order the Columns and Children by Order and Index for usage. Columns = columns.OrderBy(x => x.Order).ThenBy(x => x.Index).ToList(); Children = children.OrderBy(x => x.Order).ThenBy(x => x.Index).ToList(); for (int j = 0; j < Children.Count; j++) { _childrenIndexes.Add(Children[j].RecordIdentifier, j); } }
/// <summary> /// Gets from cache (or creates and adds to cache) the <see cref="IODataMapper"/> (with option to merge alternate property configuration). /// </summary> private static IODataMapper GetOrCreateMapper(Type type, Dictionary <string, MemberInfo> altProps, string overrideEntityName) { if (_cache.TryGetValue(type, out IODataMapper map)) { return(map); } lock (_lock) { if (_cache.TryGetValue(type, out map)) { return(map); } if (!type.IsClass) { throw new MapperException($"Type '{type.Name}' must be a class for auto-mapping."); } var mea = type.GetCustomAttributes(typeof(MapperEntityAttribute), true).OfType <MapperEntityAttribute>().FirstOrDefault(); map = (IODataMapper)Activator.CreateInstance(typeof(ODataMapper <>).MakeGenericType(type), (overrideEntityName ?? mea?.Name) ?? type.Name); var pe = Expression.Parameter(type, "x"); string dname = null; var hasProperties = false; MapperPropertyAttribute mpa = null; foreach (var p in TypeReflector.GetProperties(type)) { // Do not auto-map where the Ignore attribute has been specified. if (p.GetCustomAttributes(typeof(MapperIgnoreAttribute), true).OfType <MapperIgnoreAttribute>().FirstOrDefault() != null) { continue; } // Get property merge config. if (altProps == null) { mpa = p.GetCustomAttributes(typeof(MapperPropertyAttribute), true).OfType <MapperPropertyAttribute>().FirstOrDefault(); dname = mpa?.Name; } else { if (!altProps.TryGetValue(p.Name, out MemberInfo alt)) { throw new InvalidOperationException($"Type '{type.Name}' Property '{p.Name}' does not have an alternative property configuration specified."); } // Do not auto-map where the Ignore attribute has been specified. if (alt.GetCustomAttributes(typeof(MapperIgnoreAttribute), true).OfType <MapperIgnoreAttribute>().FirstOrDefault() != null) { continue; } mpa = alt.GetCustomAttributes(typeof(MapperPropertyAttribute), true).OfType <MapperPropertyAttribute>().FirstOrDefault(); dname = mpa?.Name ?? alt.Name; } // Create the lambda expression for the property and add to the mapper. hasProperties = true; var lex = Expression.Lambda(Expression.Property(pe, p.Name), pe); var pmap = (IODataPropertyMapper)map.GetType() .GetMethod("Property", BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) .MakeGenericMethod(p.PropertyType) .Invoke(map, new object[] { lex, dname, mpa == null ? OperationTypes.Any : mpa.OperationTypes }); if (mpa == null) { continue; } // Apply auto-map Property attribute IsUnique configuration. if (mpa.IsUniqueKey) { pmap.SetUniqueKey(mpa.IsUniqueKeyAutoGeneratedOnCreate); } // Apply auto-map Property attribute ConverterType configuration. if (mpa.ConverterType != null) { if (!typeof(IPropertyMapperConverter).IsAssignableFrom(mpa.ConverterType)) { throw new MapperException($"Type '{type.Name}' Property '{p.Name}' has 'MapperPropertyAttribute' with ConverterType set to '{mpa.ConverterType.Name}' which does not implement 'IPropertyMapperConverter'."); } IPropertyMapperConverter pmc = null; var pdef = mpa.ConverterType.GetProperty("Default", BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static); if (pdef == null) { if (mpa.ConverterType.GetConstructor(Type.EmptyTypes) == null) { throw new MapperException($"Type '{type.Name}' Property '{p.Name}' has 'MapperPropertyAttribute' with ConverterType set to '{mpa.ConverterType.Name}' does not have a static 'Default' property or default constructor."); } pmc = (IPropertyMapperConverter)Activator.CreateInstance(mpa.ConverterType); } else { pmc = (IPropertyMapperConverter)pdef.GetValue(null); } pmap.SetConverter(pmc); continue; } // Apply auto-map Property attribute MapperType configuration for complex types. if (pmap.IsSrceComplexType) { IEntityMapperBase em = null; if (mpa.MapperType == null) { em = GetMapper(pmap.SrceComplexTypeReflector.ItemType); } else { if (!typeof(IEntityMapperBase).IsAssignableFrom(mpa.MapperType)) { throw new MapperException($"Type '{type.Name}' Property '{p.Name}' has 'MapperPropertyAttribute' with MapperType set to '{mpa.MapperType.Name}' which does not implement 'IEntityMapper'."); } var mdef = mpa.MapperType.GetProperty("Default", BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static); if (mdef == null) { if (mpa.ConverterType.GetConstructor(Type.EmptyTypes) == null) { throw new MapperException($"Type '{type.Name}' Property '{p.Name}' has 'MapperPropertyAttribute' with MapperType set to '{mpa.MapperType.Name}' does not have a static 'Default' property or default constructor."); } em = (IEntityMapperBase)Activator.CreateInstance(mpa.MapperType); } else { em = (IEntityMapperBase)mdef.GetValue(null); } } if (em != null) { pmap.SetMapper(em); } } else if (mpa.MapperType != null) { throw new MapperException($"Type '{type.Name}' Property '{p.Name}' has 'MapperPropertyAttribute' with MapperType set to '{mpa.ConverterType.Name}' although the property is not a complex type."); } } if (!hasProperties) { throw new MapperException($"AutoMapper has found no properties for Type '{type.Name}' that it is able to auto-map."); } _cache.Add(type, map); return(map); } }