/// <summary> /// Sets the <see cref="IPropertyMapperConverter{TSrceProperty, TDestProperty}"/> (used where a specific source and destination type conversion is required). /// </summary> /// <typeparam name="TDestProperty">The destination property <see cref="Type"/>.</typeparam> /// <param name="converter">The <see cref="IPropertyMapperConverter{TSrceProperty, TDestProperty}"/>.</param> /// <returns>The <see cref="PropertyMapperCustomBase{TSrce, TSrceProperty}"/>.</returns> /// <remarks>The <see cref="Mapper"/> and <see cref="Converter"/> are mutually exclusive.</remarks> public PropertyMapperCustomBase <TSrce, TSrceProperty> SetConverter <TDestProperty>(IPropertyMapperConverter <TSrceProperty, TDestProperty> converter) { if (Mapper != null && converter != null) { throw new MapperException("The Mapper and Converter cannot be both set; only one is permissible."); } Converter = converter; return(this); }
/// <summary> /// Sets the <see cref="Converter"/>. /// </summary> /// <param name="converter">The <see cref="IPropertyMapperConverter"/>.</param> void IPropertyMapperBase.SetConverter(IPropertyMapperConverter converter) { if (Mapper != null && converter != null) { throw new MapperException("The Mapper and Converter cannot be both set; only one is permissible."); } if (converter != null && converter.SrceType != typeof(TSrceProperty)) { throw new MapperException($"The PropertyMapper SrceType '{typeof(TSrceProperty).Name}' and Converter SrceType '{converter.SrceType.Name}' must match."); } Converter = converter; }
/// <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); } }
/// <summary> /// Sets the <see cref="IPropertyMapperConverter{TSrceProperty, TDestProperty}"/> (used where a specific source and destination type conversion is required). /// </summary> /// <typeparam name="TDestProperty">The destination property <see cref="Type"/>.</typeparam> /// <param name="converter">The <see cref="IPropertyMapperConverter{TSrceProperty, TDestProperty}"/>.</param> /// <returns>The <see cref="PropertyMapperCustomBase{TSrce, TSrceProperty}"/>.</returns> public new ODataPropertyMapper<TSrce, TSrceProperty> SetConverter<TDestProperty>(IPropertyMapperConverter<TSrceProperty, TDestProperty> converter) { base.SetConverter(converter); return this; }
/// <summary> /// Automatically map commonly named properties. /// </summary> private void AutomagicallyMap(string[] ignoreSrceProperties) { MapperPropertyAttribute mpa = null; foreach (var sp in SrceType.GetProperties(BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Instance)) { // Do not auto-map where ignore has been specified. if (ignoreSrceProperties.Contains(sp.Name)) { continue; } if (sp.GetCustomAttributes(typeof(MapperIgnoreAttribute), true).OfType <MapperIgnoreAttribute>().FirstOrDefault() != null) { continue; } // Find corresponding property. mpa = sp.GetCustomAttributes(typeof(MapperPropertyAttribute), true).OfType <MapperPropertyAttribute>().FirstOrDefault(); var dname = mpa == null || string.IsNullOrEmpty(mpa.Name) ? sp.Name : mpa.Name; var dp = DestType.GetProperty(dname, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty | BindingFlags.Instance); if (dp == null) { if (mpa != null) { throw new InvalidOperationException($"Type '{SrceType.Name}' Property '{sp.Name}' has 'MapperPropertyAttribute' with Name set to '{dname}' which does not exist for destination Type '{DestType.Name}'."); } continue; } // Create the lambda expressions for the property and add to the mapper. var spe = Expression.Parameter(SrceType, "x"); var sex = Expression.Lambda(Expression.Property(spe, sp.Name), spe); var dpe = Expression.Parameter(DestType, "x"); var dex = Expression.Lambda(Expression.Property(dpe, dname), dpe); var pmap = (IPropertyMapper <TSrce, TDest>) typeof(EntityMapper <TSrce, TDest>) .GetMethod("PropertySrceAndDest", BindingFlags.NonPublic | BindingFlags.Instance) .MakeGenericMethod(new Type[] { sp.PropertyType, dp.PropertyType }) .Invoke(this, new object[] { sex, dex }); 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 '{SrceType.Name}' Property '{sp.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 '{SrceType.Name}' Property '{sp.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) { if (!typeof(IEntityMapperBase).IsAssignableFrom(mpa.MapperType)) { throw new MapperException($"Type '{SrceType.Name}' Property '{sp.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 '{SrceType.Name}' Property '{sp.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 '{SrceType.Name}' Property '{sp.Name}' has 'MapperPropertyAttribute' with MapperType set to '{mpa.ConverterType.Name}' although the property is not a complex type."); } } }