/// <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);
            }
        }
Example #2
0
        /// <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);
            }
        }