Lets say I have this:
public class Languages
{
public string Language;
public string SpokenAbility;
public string WrittenAbility;
}
Is there a way I can load this into a dropdown so that the dropdown displays the items: Language, SpokenAbility, and WrittenAbility?
// using System.Reflection;
// using System.Linq;
IEnumerable<String> properties = typeof(Languages)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.Name);
You can use reflection to get the properties, and LINQ to make it easier.
As Spontifixus pointed out you're using fields. all that needs to be switch is .GetProperties to .GetFields:
IEnumerable<String> fields = typeof(Languages)
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.Name);
Extension method to make it easier:
public static class FieldAndPropertyExtensions
{
/*
* Field Methods
*/
public static IEnumerable<String> GetFields<T>(this T obj, Boolean includeInheritedFields = true) where T : class
{
return getFieldsFor<T>(includeInheritedFields).Select(x => x.Name);
}
public static IEnumerable<String> GetFieldsFor<T>(Boolean includeInheritedFields = true) where T : class
{
return getFieldsFor<T>(includeInheritedFields).Select(x => x.Name);
}
public static IDictionary<String, Object> GetFieldValueDictionary<T>(this T obj, Boolean includeInheritedFields = true) where T : class
{
IEnumerable<FieldInfo> fields = getFieldsFor<T>(includeInheritedFields);
IDictionary<String, Object> result = new Dictionary<String, Object>();
foreach (var field in fields)
{
result.Add(field.Name, field.GetValue(obj));
}
return result;
}
/*
* Property Methods
*/
public static IEnumerable<String> GetProperties<T>(this T obj, Boolean includeInheritedProperties = true) where T : class
{
return getPropertiesFor<T>(includeInheritedProperties).Select(x => x.Name);
}
public static IEnumerable<String> GetPropertiesFor<T>(Boolean includeInheritedProperties = true) where T : class
{
return getPropertiesFor<T>(includeInheritedProperties).Select(x => x.Name);
}
public static IDictionary<String, Object> GetPropertyValueDictionary<T>(this T obj, Boolean includeInheritedProperties = true) where T : class
{
IEnumerable<PropertyInfo> properties = getPropertiesFor<T>(includeInheritedProperties);
IDictionary<String, Object> result = new Dictionary<String, Object>();
foreach (var property in properties)
{
result.Add(property.Name, property.GetValue(obj));
}
return result;
}
/*
* Helper methods
*/
private static IEnumerable<FieldInfo> getFieldsFor<T>(Boolean includeInheritedFields = true) where T : class
{
return typeof(T)
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(x => includeInheritedFields || x.DeclaringType == typeof(T));
}
private static IEnumerable<PropertyInfo> getPropertiesFor<T>(Boolean includeInheritedFields = true) where T : class
{
return typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => includeInheritedFields || x.DeclaringType == typeof(T));
}
}
Example usage:
// instance methods:
var languages = new Languages();
var properties = languages.GetProperties(); // prop1,prop2,prop3
var fields = languages.GetFields(); // field1,field2,field3
var propAndValue = languages.GetPropertyValueDictionary(); // Dict<propertyName,value>
var fieldAndValue = languages.GetFieldValueDictionary(); // Dict<fieldName,value>
// non-instance methods:
var properties = ObjectExtensions.GetPropertiesFor<Languages>(); // prop1,prop2,prop3
var fields = ObjectExtensions.GetFieldsFor<Languages>(); // field1,field2,field3
First of all you will need to make sure that your class has properties. The one defined in your question are fields. To transform them into properties simply add the get; and set;-methods:
public class Languages
{
public string Language { get; set; }
public string SpokenAbility { get; set; }
public string WrittenAbility {get; set; }
}
Then you can list the properties using the following code:
var properties = typeof(Languages).GetProperties().Select(p => p.Name)
To retrieve the value of a property use the following code:
var language = new Languages(){ Language="German" };
var result = typeof(Languages).GetProperty("Language").GetValue(language);
Related
I have a DTO I want to map to an entity. The entity has some properties decorated with the MaxLength attribute.
I would like AutoMapper to truncate all the strings coming from the DTO when mapping to my entity according to the MaxLength for each property, so that I don't get validation errors when saving the entity.
So, if entity is defined like this:
public class Entity
{
[MaxLength(10)]
string Name { get; set; }
}
I would like that doing this:
var myDto = new MyDto() { Name = "1231321312312312312312" };
var entity = Mapper.Map<Entity>(myDto);
The resulting entity should have its Name limited to a maximum of 10 characters.
I'm not sure that it's a good place to put that logic, but here is an example that should work in your case (AutoMapper 4.x): Custom Mapping with AutoMapper
In this example, I'm reading a custom MapTo property on my entity, you could do the same with MaxLength.
Here a full example with the current version of AutoMapper (6.x)
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(configuration =>
configuration.CreateMap<Dto, Entity>()
.ForMember(x => x.Name, e => e.ResolveUsing((dto, entity, value, context) =>
{
var result = entity.GetType().GetProperty(nameof(Entity.Name)).GetCustomAttribute<MaxLengthAttribute>();
return dto.MyName.Substring(0, result.Length);
})));
var myDto = new Dto { MyName = "asadasdfasfdaasfasdfaasfasfd12" };
var myEntity = Mapper.Map<Dto, Entity>(myDto);
}
}
public class Entity
{
[MaxLength(10)]
public string Name { get; set; }
}
public class Dto
{
public string MyName { get; set; }
}
For AutoMapper 8.0, and building on #SuperJMN's answer:
Create a file AutoMapperExtensions.cs in your project:
using AutoMapper;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
namespace YourNamespaceHere
{
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDestination> LimitStrings<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.Configuration.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType);
var propertyMaps = existingMaps.PropertyMaps.Where(map => !map.Ignored && ((PropertyInfo)map.SourceMember).PropertyType == typeof(string));
foreach (var propertyMap in propertyMaps)
{
var attr = propertyMap.DestinationMember.GetCustomAttribute<MaxLengthAttribute>();
if (attr != null)
{
expression.ForMember(propertyMap.DestinationMember.Name,
opt => opt.ConvertUsing(new StringLimiter(attr.Length), propertyMap.SourceMember.Name));
}
}
return expression;
}
}
public class StringLimiter : IValueConverter<string, string>
{
private readonly int length;
private readonly PropertyInfo propertyMapSourceMember;
public StringLimiter(int length)
{
this.length = length;
propertyMapSourceMember = propertyMapSourceMember ?? throw new ArgumentNullException(nameof(propertyMapSourceMember));
}
public string Convert(string sourceMember, ResolutionContext context)
{
var sourceValue = (string)propertyMapSourceMember.GetValue(sourceMember);
return new string(sourceValue.Take(length).ToArray());
}
}
}
... and add the following to the end of your CreateMap (e.g.):
.ForMember(
dest => dest.ShortField,
opts => opts.MapFrom(src => src.LongField))
.LimitStrings();
For Automapper 4.x
I got something with this code:
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(configuration =>
configuration.CreateMap<Dto, Entity>()
.ForMember(x => x.Name, e => e.MapFrom(d => d.MyName))
.ForMember(x => x.Free, e => e.MapFrom(d => d.Free))
.ForMember(x => x.AnotherName, e => e.MapFrom(d => d.Another))
.LimitStrings());
var dto = new Dto() { MyName = "asadasdfasfdaasfasdfaasfasfd12", Free = "ASFÑLASJDFÑALSKDJFÑALSKDJFAMLSDFASDFASFDASFD", Another = "blalbalblalblablalblablalblablalblablabb"};
var entity = Mapper.Map<Entity>(dto);
}
}
public static class Extensions
{
public static IMappingExpression<TSource, TDestination> LimitStrings<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType);
var propertyMaps = existingMaps.GetPropertyMaps().Where(map => !map.IsIgnored() && ((PropertyInfo)map.SourceMember).PropertyType == typeof(string));
foreach (var propertyMap in propertyMaps)
{
var attr = propertyMap.DestinationProperty.MemberInfo.GetCustomAttribute<MaxLengthAttribute>();
if (attr != null)
{
expression.ForMember(propertyMap.DestinationProperty.Name,
opt => opt.ResolveUsing(new StringLimiter(attr.Length, (PropertyInfo) propertyMap.SourceMember)));
}
}
return expression;
}
}
public class StringLimiter : IValueResolver
{
private readonly int length;
private readonly PropertyInfo propertyMapSourceMember;
public StringLimiter(int length, PropertyInfo propertyMapSourceMember)
{
this.length = length;
this.propertyMapSourceMember = propertyMapSourceMember ?? throw new ArgumentNullException(nameof(propertyMapSourceMember));
}
public ResolutionResult Resolve(ResolutionResult source)
{
var sourceValue = (string)propertyMapSourceMember.GetValue(source.Context.SourceValue);
var result = new string(sourceValue.Take(length).ToArray());
return source.New(result);
}
}
Please, tell me if it has sense or has some bugs!
Thanks to #bidou for the tip. Here is the post where I took the inspiration here
I am trying to write this method:
public static T Nullify<T>(T item, params Func<T, object> [] properties)
{
// Sets any specified properties to null, returns the object.
}
I will call it like this:
var kitten = new Kitten() { Name = "Mr Fluffykins", FurColour = "Brown" };
var anonymousKitten = Nullify(kitten, c => c.Name);
However I am unsure of how to do this. Any ideas?
Another approach is to do this (it doesn't need to be an extension method)
public static T Nullify<T>(this T item, params Expression<Func<T, object>> [] properties)
{
foreach(var property in properties)
{
var memberSelectorExpression = property.Body as MemberExpression;
if (memberSelectorExpression != null)
{
var propertyInfo = memberSelectorExpression.Member as PropertyInfo;
if (propertyInfo != null)
{
propertyInfo.SetValue(item, null, null);
}
}
}
return item;
}
Usage
item.Nullify(i => i.PropertyName, i => i.PropertyName2)
You'd need to pass a "setter method" not a "reader method" in properties.
static void Nullify<T, D>(T item, params Action<T, D>[] properties)
where D : class
{
foreach (var property in properties)
{
property(item, null);
}
}
usage:
Nullify<Kitten, string>(kitten, (c, d) => { c.Name = d; });
But this will just set the data for you. If you want a copy and then apply the properties, the items would probably have to be clonable (alternatively you can go though some hell with reflection):
static T Nullify<T, D>(T item, params Action<T, D>[] properties)
where D : class
where T : ICloneable
{
T copy = (T)item.Clone();
foreach (var property in properties)
{
property(copy, null);
}
return copy;
}
class Kitten : ICloneable
{
public string Name { get; set; }
public string FurColour { get; set; }
public object Clone()
{
return new Kitten() { Name = this.Name, FurColour = this.FurColour };
}
}
usage
var anonymousKitten = Nullify(kitten, (c, d) => { c.Name = d; });
Without modifying your method definition much:
namespace ConsoleApplication
{
public class Kitten : ISimpleClone<Kitten>
{
public string Name { get; set; }
public string FurColour { get; set; }
public int? Number { get; set; }
public Kitten SimpleClone()
{
return new Kitten { Name = this.Name, FurColour = this.FurColour, Number = this.Number };
}
}
public interface ISimpleClone<T>
{
T SimpleClone();
}
public class Program
{
public static PropertyInfo GetProperty<TObject, TProperty>(Expression<Func<TObject, TProperty>> propertyExpression)
{
MemberExpression body = propertyExpression.Body as MemberExpression;
if (body == null)
{
var unaryExp = propertyExpression.Body as UnaryExpression;
if (unaryExp != null)
{
body = ((UnaryExpression)unaryExp).Operand as MemberExpression;
}
}
return body.Member as PropertyInfo;
}
public static T Nullify<T>(T item, params Expression<Func<T, object>>[] properties)
where T : ISimpleClone<T>
{
// Creates a new instance
var newInstance = item.SimpleClone();
// Gets the properties that will be null
var propToNull = properties.Select(z => GetProperty<T, object>(z));
var filteredProp = propToNull
.Where(z => !z.PropertyType.IsValueType || Nullable.GetUnderlyingType(z.PropertyType) != null) // Can be null
.Where(z => z.GetSetMethod(false) != null && z.CanWrite); // Can be set
foreach (var prop in filteredProp)
{
prop.SetValue(newInstance, null);
}
return newInstance;
}
public static void Main(string[] args)
{
var kitten = new Kitten() { Name = "Mr Fluffykins", FurColour = "Brown", Number = 12 };
var anonymousKitten = Nullify(kitten, c => c.Name, c => c.Number);
Console.Read();
}
}
}
Looks a bit hacky though....
I'm trying to use AutoMapper to map classes like this:
class FooDTO
{
public int X { get; set; }
public EmbeddedDTO Embedded { get; set; }
public class EmbeddedDTO
{
public BarDTO Y { get; set; }
public BazDTO Z { get; set; }
}
}
To classes like this:
class Foo
{
public int X { get; set; }
public Bar Y { get; set; }
public Baz Z { get; set; }
}
(FooDTO is a HAL resource)
I know I can do it by creating the map explicitly like this:
Mapper.CreateMap<FooDTO, Foo>()
.ForMember(f => f.Y, c => c.MapFrom(f => f.Embedded.Y))
.ForMember(f => f.Z, c => c.MapFrom(f => f.Embedded.Z));
Or even with a trick like this:
Mapper.CreateMap<FooDTO, Foo>()
.AfterMap((source, dest) => Mapper.Map(source.Embedded, dest));
But the problem is that I will have many similar HAL resources to map, and I'd rather not have to configure each one separately. I actually have a generic object model that looks like this:
class HalResource
{
[JsonProperty("_links")]
public IDictionary<string, HalLink> Links { get; set; }
}
class HalResource<TEmbedded> : HalResource
{
[JsonProperty("_embedded")]
public TEmbedded Embedded { get; set; }
}
class HalLink
{
[JsonProperty("href")]
public string Href { get; set; }
}
With this model, the FooDTO class is actually declared like this
class FooDTO : HalResource<FooDTO.EmbeddedDTO>
{
public int X { get; set; }
public class EmbeddedDTO
{
public int Y { get; set; }
public int Z { get; set; }
}
}
Is there a way to configure the mapping globally for all classes that inherit HalResource<TEmbedded>, so that the properties of the DTO's Embedded property are mapped directly to the target object? I tried to do it with a custom IObjectMapper, but it proved more challenging than I expected...
If your use case is as limited as presented in the question, that is:
A one-way mapping from HalResource derived instances to straight POCOS (vs bidirectional mapping)
Mapping of properties of the same name and type
The exact embedded structure you presented here
than it may make sense to setup a specific mapping yourself that takes into account this structure. This is something I tend to do if I have a very narrowly defined need for mapping with some clear mapping conventions (instead of relying on a generic mapper such as AutoMapper). For this purpose I have some building blocks that I tend to reuse in different contexts. I whipped together a mapper that applies to the problem you described from these building blocks, as shown below:
public class Mapper
{
private const BindingFlags DestConstructorFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags DestFlags = BindingFlags.Instance | BindingFlags.Public;
private const BindingFlags SrcFlags = BindingFlags.Instance | BindingFlags.Public;
private static readonly object[] NoArgs = new object[0];
private static readonly Type GenericEmbeddedSourceType = typeof(HalResource<>);
private readonly Dictionary<Type, Func<object, object>> _oneWayMap = new Dictionary<Type, Func<object, object>>();
public void CreateMap<TDestination, TSource>()
where TDestination : class
where TSource : HalResource
{
CreateMap(typeof(TDestination), typeof(TSource));
}
public void CreateMap(Type destType, Type srcType)
{
_oneWayMap[srcType] = InternalCreateMapper(destType, srcType);
}
public object Map<TSource>(TSource toMap) where TSource : HalResource
{
var mapper = default(Func<object, object>);
if (!_oneWayMap.TryGetValue(typeof(TSource), out mapper))
throw new KeyNotFoundException(string.Format("No mapping for {0} is defined.", typeof(TSource)));
return mapper(toMap);
}
public TDestination Map<TDestination, TSource>(TSource toMap)
where TDestination : class
where TSource : HalResource
{
var converted = Map(toMap);
if (converted != null && !typeof(TDestination).IsAssignableFrom(converted.GetType()))
throw new InvalidOperationException(string.Format("No mapping from type {0} to type {1} has been configured.", typeof(TSource), typeof(TDestination)));
return (TDestination)converted;
}
public void Clear()
{
_oneWayMap.Clear();
}
private static Func<object, object> InternalCreateMapper(Type destType, Type srcType)
{
// Destination specific constructor + setter map.
var destConstructor = BuildConstructor(destType.GetConstructor(DestConstructorFlags, null, Type.EmptyTypes, null));
var destSetters = destType
.GetProperties(DestFlags)
.Where(p => p.CanWrite)
.ToDictionary(k => k.Name, v => Tuple.Create(v.PropertyType, BuildSetter(v)));
// Source specific getter maps
var srcPrimPropGetters = CreateGetters(srcType);
var srcEmbeddedGetter = default(Func<object, object>);
var srcEmbeddedPropGetters = default(IDictionary<string, Tuple<Type, Func<object, object>>>);
var baseType = srcType.BaseType;
while (baseType != null && baseType != typeof(object))
{
if (baseType.IsGenericType && GenericEmbeddedSourceType.IsAssignableFrom(baseType.GetGenericTypeDefinition()))
{
var genericParamType = baseType.GetGenericArguments()[0];
if (srcPrimPropGetters.Any(g => g.Value.Item1.Equals(genericParamType)))
{
var entry = srcPrimPropGetters.First(g => g.Value.Item1.Equals(genericParamType));
srcPrimPropGetters.Remove(entry.Key);
srcEmbeddedGetter = entry.Value.Item2;
srcEmbeddedPropGetters = CreateGetters(entry.Value.Item1);
break;
}
}
baseType = baseType.BaseType;
}
// Build mapper delegate function.
return (src) =>
{
var result = destConstructor(NoArgs);
var srcEmbedded = srcEmbeddedGetter != null ? srcEmbeddedGetter(src) : null;
foreach (var setter in destSetters)
{
var getter = default(Tuple<Type, Func<object, object>>);
if (srcPrimPropGetters.TryGetValue(setter.Key, out getter) && setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result, getter.Item2(src));
else if (srcEmbeddedPropGetters.TryGetValue(setter.Key, out getter) && setter.Value.Item1.IsAssignableFrom(getter.Item1))
setter.Value.Item2(result, getter.Item2(srcEmbedded));
}
return result;
};
}
private static IDictionary<string, Tuple<Type, Func<object, object>>> CreateGetters(Type srcType)
{
return srcType
.GetProperties(SrcFlags)
.Where(p => p.CanRead)
.ToDictionary(k => k.Name, v => Tuple.Create(v.PropertyType, BuildGetter(v)));
}
private static Func<object[], object> BuildConstructor(ConstructorInfo constructorInfo)
{
var param = Expression.Parameter(typeof(object[]), "args");
var argsExp = constructorInfo.GetParameters()
.Select((p, i) => Expression.Convert(Expression.ArrayIndex(param, Expression.Constant(i)), p.ParameterType))
.ToArray();
return Expression.Lambda<Func<object[], object>>(Expression.New(constructorInfo, argsExp), param).Compile();
}
private static Func<object, object> BuildGetter(PropertyInfo propertyInfo)
{
var instance = Expression.Parameter(typeof(object), "instance");
var instanceCast = propertyInfo.DeclaringType.IsValueType
? Expression.Convert(instance, propertyInfo.DeclaringType)
: Expression.TypeAs(instance, propertyInfo.DeclaringType);
var propertyCast = Expression.TypeAs(Expression.Property(instanceCast, propertyInfo), typeof(object));
return Expression.Lambda<Func<object, object>>(propertyCast, instance).Compile();
}
private static Action<object, object> BuildSetter(PropertyInfo propertyInfo)
{
var setMethodInfo = propertyInfo.GetSetMethod(true);
var instance = Expression.Parameter(typeof(object), "instance");
var value = Expression.Parameter(typeof(object), "value");
var instanceCast = propertyInfo.DeclaringType.IsValueType
? Expression.Convert(instance, propertyInfo.DeclaringType)
: Expression.TypeAs(instance, propertyInfo.DeclaringType);
var call = Expression.Call(instanceCast, setMethodInfo, Expression.Convert(value, propertyInfo.PropertyType));
return Expression.Lambda<Action<object, object>>(call, instance, value).Compile();
}
}
Some optimizations can be performed, but performance is likely sufficient for most problems. This can then be used like:
public abstract class HalResource
{
public IDictionary<string, HalLink> Links { get; set; }
}
public abstract class HalResource<TEmbedded> : HalResource
{
public TEmbedded Embedded { get; set; }
}
public class HalLink
{
public string Href { get; set; }
}
public class FooDTO : HalResource<FooDTO.EmbeddedDTO>
{
public int X { get; set; }
public class EmbeddedDTO
{
public int Y { get; set; }
public int Z { get; set; }
}
}
public class MyMappedFoo
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
class Program
{
public static void Main(params string[] args)
{
// Configure mapper manually
var mapper = new Mapper();
mapper.CreateMap<MyMappedFoo, FooDTO>();
var myDTO = new FooDTO
{
X = 10,
Embedded = new FooDTO.EmbeddedDTO { Y = 5, Z = 9 }
};
var mappedFoo = mapper.Map<MyMappedFoo, FooDTO>(myDTO);
Console.WriteLine("X = {0}, Y = {1}, Z = {2}", mappedFoo.X, mappedFoo.Y, mappedFoo.Z);
Console.WriteLine("Done");
Console.ReadLine();
}
}
If your source and destination types can be discovered by convention, you can go a step further and have a builder that encodes these conventions populate the map as in the example below (again not the most optimal implementation, but there to illustrate the point):
public static class ByConventionMapBuilder
{
public static Func<IEnumerable<Type>> DestinationTypesProvider = DefaultDestTypesProvider;
public static Func<IEnumerable<Type>> SourceTypesProvider = DefaultSourceTypesProvider;
public static Func<Type, Type, bool> TypeMatcher = DefaultTypeMatcher;
public static Mapper Build()
{
var mapper = new Mapper();
var sourceTypes = SourceTypesProvider().ToList();
var destTypes = DestinationTypesProvider();
foreach (var destCandidateType in destTypes)
{
var match = sourceTypes.FirstOrDefault(t => TypeMatcher(t, destCandidateType));
if (match != null)
{
mapper.CreateMap(destCandidateType, match);
sourceTypes.Remove(match);
}
}
return mapper;
}
public static IEnumerable<Type> TypesFromAssembliesWhere(Func<IEnumerable<Assembly>> assembliesProvider, Predicate<Type> matches)
{
foreach (var a in assembliesProvider())
{
foreach (var t in a.GetTypes())
{
if (matches(t))
yield return t;
}
}
}
private static IEnumerable<Type> DefaultDestTypesProvider()
{
return TypesFromAssembliesWhere(
() => new[] { Assembly.GetExecutingAssembly() },
t => t.IsClass && !t.IsAbstract && !t.Name.EndsWith("DTO"));
}
private static IEnumerable<Type> DefaultSourceTypesProvider()
{
return TypesFromAssembliesWhere(
() => new[] { Assembly.GetExecutingAssembly() },
t => typeof(HalResource).IsAssignableFrom(t) && !t.IsAbstract && t.Name.EndsWith("DTO"));
}
private static bool DefaultTypeMatcher(Type srcType, Type destType)
{
var stn = srcType.Name;
return (stn.Length > 3 && stn.EndsWith("DTO") && destType.Name.EndsWith(stn.Substring(0, stn.Length - 3)));
}
}
class Program
{
public static void Main(params string[] args)
{
// Configure mapper by type scanning & convention matching
var mapper = ByConventionMapBuilder.Build();
var myDTO = new FooDTO
{
X = 10,
Embedded = new FooDTO.EmbeddedDTO { Y = 5, Z = 9 }
};
var mappedFoo = mapper.Map<MyMappedFoo, FooDTO>(myDTO);
Console.WriteLine("X = {0}, Y = {1}, Z = {2}", mappedFoo.X, mappedFoo.Y, mappedFoo.Z);
Console.WriteLine("Done");
Console.ReadLine();
}
}
If you have other reasons to want to hang on to AutoMapper, I suggest creating a similar map builder that encodes both the type matching and the embedded property mapping.
I've made an extension method which I use to make serializable dictionaries from EF Entities:
public static class Extensions
{
public static IDictionary<string, object> ToSerializable(this object obj)
{
var result = new Dictionary<string, object>();
foreach (var property in obj.GetType().GetProperties().ToList())
{
var value = property.GetValue(obj, null);
if (value != null && (value.GetType().IsPrimitive
|| value is decimal || value is string || value is DateTime
|| value is List<object>))
{
result.Add(property.Name, value);
}
}
return result;
}
}
I'm using it like this:
using(MyDbContext context = new MyDbContext())
{
var someEntity = context.SomeEntity.FirstOrDefault();
var serializableEntity = someEntity.ToSerializable();
}
I would like to know if there is any way to constrain it to be usable on my entities only, instead of all object:s.
Code for Patryk's answer:
public interface ISerializableEntity { };
public class CustomerEntity : ISerializableEntity
{
....
}
public static class Extensions
{
public static IDictionary<string, object> ToSerializable(
this ISerializableEntity obj)
{
var result = new Dictionary<string, object>();
foreach (var property in obj.GetType().GetProperties().ToList())
{
var value = property.GetValue(obj, null);
if (value != null && (value.GetType().IsPrimitive
|| value is decimal || value is string || value is DateTime
|| value is List<object>))
{
result.Add(property.Name, value);
}
}
return result;
}
}
Seeing how this code works with the marker interface, you may choose to put the serialization method in the interface to avoid the reflection and to have finer control on what gets serialized and how it might be encoded or encrypted:
public interface ISerializableEntity
{
Dictionary<string, object> ToDictionary();
};
public class CustomerEntity : ISerializableEntity
{
public string CustomerName { get; set; }
public string CustomerPrivateData { get; set; }
public object DoNotSerializeCustomerData { get; set; }
Dictionary<string, object> ISerializableEntity.ToDictionary()
{
var result = new Dictionary<string, object>();
result.Add("CustomerName", CustomerName);
var encryptedPrivateData = // Encrypt the string data here
result.Add("EncryptedCustomerPrivateData", encryptedPrivateData);
}
return result;
}
public static IDictionary<string, T> ToSerializable(this T obj) where T:Class
Will narrow it down a bit. If you need more than that you will need to assign a marker interface to all entities and use:
public static IDictionary<string, T> ToSerializable(this T obj) where T:IEntity
I have an interface that defines a method for returning an IList<PropertyInfo> :
public interface IWriteable
{
IList<PropertyInfo> WriteableProperties();
}
.
.
It is implemented in various (dissimilar) classes in the following manner:
public abstract class Foo
{
private IList<PropertyInfo> _props;
protected Foo()
{
this._props = new List<PropertyInfo>();
foreach (PropertyInfo p in this.GetType().GetProperties())
{
if (Attribute.IsDefined(p, typeof(WriteableAttribute)))
this._props.Add(p);
}
}
#region IWriteable Members
public IList<PropertyInfo> WriteableProperties()
{
return this._props;
}
#endregion
}
public class Bar : Foo
{
public string A
{
get { return "A"; }
}
[Writeable()]
public string B
{
get { return "B"; }
}
[Writeable()]
public string C
{
get { return "C"; }
}
// Snip
}
Please note the attributes marking a couple of the properties, as these are the properties that will get added to the list. This IList will then be used elsewhere during some file write operations.
It is important to me that they are ordered in the list in the order they appear in the code file.
However, MSDN states:
The GetProperties method does not return properties in a particular
order, such as alphabetical or declaration order. Your code must not
depend on the order in which properties are returned, because that
order varies.
So, what is the best way to ensure each PropertyInfo gets added in the order I would like to to be?
(I am also using .NET2.0, so I can't use any Linq goodness, should there be any that would help, although it would be interesting to see.)
Add information to the attribute about the ordering, you can then use this to ensure the ordering, e.g.:
[Writeable(Order = 1)]
So for the following attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class WriteableAttribute : Attribute
{
public int Order { get; set; }
}
You can get an ordered selection of the properties as follows:
private readonly List<PropertyInfo> _props;
protected Foo()
{
_props = new List<PropertyInfo>();
var props = new Dictionary<int, PropertyInfo>();
foreach (PropertyInfo p in GetType().GetProperties())
{
if (Attribute.IsDefined(p, typeof(WriteableAttribute)))
{
var attr = (WriteableAttribute)p
.GetCustomAttributes(typeof(WriteableAttribute))[0];
props.Add(attr.Order, p);
}
}
_props.AddRange(props.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value));
}
NB For production code I would recommend caching the property information (per type for example) as this will be relatively slow if carried out for each instance.
Update - Caching
With some example caching of property lookup and ordering:
public static class PropertyReflector
{
private static readonly object SyncObj = new object();
private static readonly Dictionary<Type, List<PropertyInfo>> PropLookup =
new Dictionary<Type, List<PropertyInfo>>();
public static IList<PropertyInfo> GetWritableProperties(Type type)
{
lock (SyncObj)
{
List<PropertyInfo> props;
if (!PropLookup.TryGetValue(type, out props))
{
var propsOrder = new Dictionary<int, PropertyInfo>();
foreach (PropertyInfo p in type.GetProperties())
{
if (Attribute.IsDefined(p, typeof(WriteableAttribute)))
{
var attr = (WriteableAttribute)p.GetCustomAttributes(
typeof(WriteableAttribute), inherit: true)[0];
propsOrder.Add(attr.Order, p);
}
}
props = new List<PropertyInfo>(propsOrder
.OrderBy(kvp => kvp.Key)
.Select(kvp => kvp.Value));
PropLookup.Add(type, props);
}
return props;
}
}
}
Update - No Linq
You can replace the Linq section with the following code to order the properties and add them to the cache:
List<int> order = new List<int>(propsOrder.Keys);
order.Sort();
props = new List<PropertyInfo>();
order.ForEach(i => props.Add(propsOrder[i]));
PropLookup.Add(type, props);
Update - Full Linq
And using a fully Linq solution:
static IList<PropertyInfo> GetWritableProperties(Type type)
{
lock (SyncObj)
{
List<PropertyInfo> props;
if (!PropLookup.TryGetValue(type, out props))
{
props = type.GetProperties()
.Select(p => new { p, Atts = p.GetCustomAttributes(typeof(WriteableAttribute), inherit: true) })
.Where(p => p.Atts.Length != 0)
.OrderBy(p => ((WriteableAttribute)p.Atts[0]).Order)
.Select(p => p.p)
.ToList();
PropLookup.Add(type, props);
}
return props;
}
}
A while ago when I had the same problem I wrote a helper class to sort the properties based on the Order property of the attribute. I used the built-in DisplayAttribute but you can just add an Order property to any attribute you write.
class FieldSorter : IComparer, IComparer<DisplayAttribute>, IEqualityComparer<DisplayAttribute>
{
public int Compare(object x, object y)
{
return Compare((DisplayAttribute)x, (DisplayAttribute)y);
}
public int Compare(DisplayAttribute x, DisplayAttribute y)
{
return x.Order.CompareTo(y.Order);
}
public bool Equals(DisplayAttribute x, DisplayAttribute y)
{
return Compare(x, y) == 0;
}
public int GetHashCode(DisplayAttribute obj)
{
return obj.GetHashCode();
}
public static SortedList<DisplayAttribute, PropertyInfo> GetSortedFields(Type type)
{
PropertyInfo[] props = type.GetProperties();
var sortedProps = new SortedList<DisplayAttribute, PropertyInfo>(props.Length, new FieldSorter());
object[] atts;
int assignedOrder = 1000; // anything without pre-assigned order gets a ridiculously high order value. same for duplicates.
foreach (var prop in props)
{
atts = prop.GetCustomAttributes(typeof(DisplayAttribute), true);
if (atts.Length > 0)
{
var att = (DisplayAttribute)atts[0];
if (!att.GetOrder().HasValue || sortedProps.Keys.Contains(att, new FieldSorter()))
att.Order = assignedOrder++;
sortedProps.Add(att, prop);
}
}
return sortedProps;
}
}
This gives you a SortedList where the key is the attribute and the value is the PropertyInfo. This was because I still needed to access other properties of the attribute.
Example usage:
public class Stats
{
[Display(Name = "Changes", Description = "Changed records.", Order = 8)]
public int RecordsWithChanges { get; set; }
[Display(Name = "Invalid", Description = "Number of invalid records analyzed.", Order = 4)]
public int InvalidRecordCount { get; set; }
[Display(Name = "Valid", Description = "Number of valid records.", Order = 6)]
public int ValidRecordCount { get; set; }
[Display(Name = "Cost", Description = "Number of records with a Cost value.", Order = 10)]
public int RecordsWithCost { get; set; }
public Stats(int changed, int valid, int invalid, int cost)
{
RecordsWithChanges = changed;
ValidRecordCount = valid;
InvalidRecordCount = invalid;
RecordsWithCost = cost;
}
}
class Program
{
static void Main(string[] args)
{
var foo = new Stats(123, 456, 7, 89);
var fields = FieldSorter.GetSortedFields(foo.GetType());
foreach (DisplayAttribute att in fields.Keys)
Console.WriteLine("{0}: {1} ({2}) == {3}",
att.Order, att.Name, att.Description, fields[att].GetValue(foo, null));
null));
}
}
Output:
4: Invalid (Number of invalid records analyzed.) -- 7
6: Valid (Number of valid records.) -- 456
8: Changes (Changed records.) -- 123
10: Cost (Number of records with a Cost value.) -- 89