Automapper convert list of system object - c#

I need to convert one class to another using automapper and my objects looks like this:
public class Foo
{
public List<object> Objects { get; set; }
}
public class Bar
{
public List<object> Objects { get; set; }
}
public class FooItem
{
public string Name { get; set; }
}
public class BarItem
{
public string Name { get; set; }
}
There could be more types for items, not just FooItem and BarItem but I just use these two for simplicity and I need to have this design.
I've tried several things like type converters and so on but still no luck.
Here the current basic conversion code
Mapper.Initialize(cfg =>
{
cfg.CreateMap<FooItem, BarItem>();
cfg.CreateMap<Foo, Bar>();
});
var foo = new Foo()
{
Objects = new List<object>() { new FooItem() { Name = "name" } }
};
var map = Mapper.Map<Foo, Bar>(foo);
The goal is that the Bar object contains a list of BarItems at runtime, but so far I only have only managed to get a list of FooItem at runtime.
Any ideas?

Are you satisfied with only a list of BarItems? In this case you can do it like this:
var map = Mapper.Map<IEnumerable<FooItem>, List<BarItem>>(foo.Objects.Cast<FooItem>());
Update: You can do it like this.
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<FooItem, BarItem>();
cfg.CreateMap<Foo, Bar>()
.ForMember(dest => dest.Objects, opt => opt.ResolveUsing<CustomResolver>());
});
Mapper.AssertConfigurationIsValid();
var foo = new Foo()
{
Objects = new List<object>() { new FooItem() { Name = "name" } }
};
//var map = Mapper.Map<IEnumerable<FooItem>, List<BarItem>>(foo.Objects.Cast<FooItem>());
var map = Mapper.Map<Foo, Bar>(foo);
}
}
public class CustomResolver : IValueResolver<Foo, Bar, List<object>>
{
public List<object> Resolve(Foo source, Bar destination, List<object> member, ResolutionContext context)
{
var map = Mapper.Map<IEnumerable<FooItem>, List<BarItem>>(source.Objects.Cast<FooItem>());
return map.Cast<object>().ToList();
}
}

You can do type checks inside a ConstructUsing clause:
cfg.CreateMap<object, object>()
.ConstructUsing(src => {
if (src is FooItem) {
return Mapper.Map<BarItem>(src);
}
// ...
throw new InvalidOperationException($"Can not map source item of type '{src.GetType().FullName}'.");
});
But you probably need to introduce an interface for the items in the Objects collection, because the map object -> object overrides all other maps, but we want to use the map FooItem -> BarItem.

Inspired by #Ogglas I manage to found out an interesting solution where it is not needed to check for all the types.
Basically I wrote my own ITypeConverter
public class CustomResolver : ITypeConverter<List<object>, List<object>>
{
public List<object> Convert(List<object> source, List<object> destination, ResolutionContext context)
{
var objects = new List<object>();
foreach (var obj in source)
{
var destinationType = context.ConfigurationProvider.GetAllTypeMaps().First(x => x.SourceType == obj.GetType()).DestinationType;
var target = context.Mapper.Map(obj, obj.GetType(), destinationType);
objects.Add(target);
}
return objects;
}
}
And get from the context the proper mapping for each type. Currently is not aware of nulls and so on...
Then, when configuring automapper
Mapper.Initialize(cfg =>
{
cfg.CreateMap<FooItem, BarItem>();
cfg.CreateMap<List<object>, List<object>>().ConvertUsing<CustomResolver>();
cfg.CreateMap<Foo, Bar>();
});

Related

Automapper - Access default map function in MapFrom to fallback

I'm writing an extension method in order to do translation with Automapper.
I have some classes :
public class TranslatableClass : ITranslatable<TranslationClass>
{
public string Id { get; set; }
public string Label { get; set; }
public string Description { get; set; }
public List<TranslationClass> Translations { get; set; }
public string OtherEntityId { get; set; }
public string OtherEntityLabel { get; set; }
public List<OtherEntityTranslation> OtherEntityTranslations { get; set; }
}
public class TranslationClass : ITranslation
{
public Guid LanguageId { get; set; }
public string Label { get; set; }
public string Description { get; set; }
}
public class TranslatedClass
{
public string Id { get; set; }
public string Label { get; set; }
public string Description { get; set; }
public string OtherEntityLabel { get; set; }
}
public class OtherEntityTranslation : ITranslation
{
public string Label { get; set; }
public Guid LanguageId { get; set; }
}
I'd like to get an extension method like this one :
cfg.CreateMap<TranslatableClass, TranslatedClass>()
.ForMember(t => t.OtherEntityLabel, opt => opt.MapFromTranslation(t => t.OtherEntityTranslations, oet => oet.Label));
And my extension method looks like this one
public static void MapFromTranslation<TSource, TDestination, TMember, TTranslation>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, Func<TSource, IEnumerable<TTranslation>> getTranslations, Func<TTranslation, string> getValue)
where TTranslation : ITranslation
{
opt.MapFrom((src, _, _, context) =>
{
string result = null; // here is the pain point ; I'd like to get the value as if I was automapper
if (context.Options.Items.TryGetValue(LANGUAGE, out object contextLanguage) && contextLanguage is Guid languageId)
{
var translations = getTranslations(src);
var translation = translations.FirstOrDefault(t => t.LanguageId == languageId);
if (translation != null)
{
result = getValue(translation);
}
}
return result;
});
}
The issue I'm facing is I can't find a nice way to get the default behavior of AutoMapper when I don't have a translation. In this implementation, if I don't find a translation for my language, the value will be null while it should be the value of the source object (which is the default value).
I try to put PreCondition before the MapFrom but that doesn't map the property so I get null too.
I can try to get the value from the source object with reflexion but I will lose all the capabilities of Automapper like naming convention and other stuffs.
public static void MapFromTranslation<TSource, TDestination, TMember, TTranslation>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, Func<TSource, IEnumerable<TTranslation>> getTranslations, Func<TTranslation, string> getValue)
where TTranslation : ITranslation
{
var destinationMember = opt.DestinationMember as PropertyInfo;
var source = typeof(TSource);
var sourceProperty = source.GetProperty(destinationMember.Name);
if (sourceProperty != null)
{
opt.MapFrom((src, _, _, context) =>
{
string result = sourceProperty.GetValue(src) as string; // Get value from source as if it was the mapper
if (context.Options.Items.TryGetValue(LANGUAGE, out object contextLanguage) && contextLanguage is Guid languageId)
{
var translations = getTranslations(src);
if (translations != null)
{
var translation = translations.FirstOrDefault(t => t.LanguageId == languageId);
if (translation != null)
{
var value = getValue(translation);
if (!String.IsNullOrWhiteSpace(value))
{
result = value;
}
}
}
}
return result;
});
}
else
{
throw new Exception($"Can't map property {opt.DestinationMember.Name} from {source.Name}");
}
}
Let's re-define configuration without using extension method, trying to simplify things. Following mapping example, we can implement custom IValueResolver
cfg.CreateMap<TranslatableClass, TranslatedClass>()
.ForMember(dest => dest.OtherEntityLabel, opt => opt.MapFrom<CustomResolver>();
Implementing IValueResolver<TranslatableClass, TranslatedClass, string> interface:
public class CustomResolver: IValueResolver<TranslatableClass, TranslatedClass, string>
{
public string Resolve(TranslatableClass source, TranslatedClass destination, string member, ResolutionContext context)
{
string result = source.Label; /* needed effect! */
/* can we simplify this condition? */
if (context.Options.Items.TryGetValue(source.OtherEntityLabel, out object contextLanguage)
&& contextLanguage is Guid languageId)
{
var translations = source.OtherEntityTranslations;
var translation = translations.FirstOrDefault(t => t.LanguageId == languageId);
if (translation != null)
{
result = translation.Label;
};
}
return result;
}
}
Here comes same logic from
MapFromTranslation<TSource, TDestination, TMember, ... extension method provided below, let's put that logic right - we map TSource as TranslatableClass to TDestination as TranslatedClass.
Also, I believe that if (context.Options.Items.TryGetValue(...)) should be removed for simplicity too (are we trying to get languageId here?)
So, by using Custom Value Resolvers feature we can simplify mapper configuration and refactor for test coverage or debugging needs.
Update
I do want to use this extensions method on 50 others entity and I
won't write custom resolver for each one
Using Expressions instead of reflection should help to implement 'generic solution'. Solution is to define a cache of mapping expressions to access TSource and TDestination properties.
public static class MappingCache<TFirst, TSecond>
{
static MappingCache()
{
var first = Expression.Parameter(typeof(TFirst), "first");
var second = Expression.Parameter(typeof(TSecond), "second");
var secondSetExpression = MappingCache.GetSetExpression(second, first);
var blockExpression = Expression.Block(first, second, secondSetExpression, first);
Map = Expression.Lambda<Func<TFirst, TSecond, TFirst>>(blockExpression, first, second).Compile();
}
public static Func<TFirst, TSecond, TFirst> Map { get; private set; }
}
Next, let's try to define generic lambda-expressions for both
Func<TTranslation, string> getValue and getTranslations(...
e.g.:
public static Expression GetSetExpression(ParameterExpression sourceExpression, params ParameterExpression[] destinationExpressions)
{
/** AutoMapper also can be used here */
/* compile here all (source)=>(destination) expressions */
var destination = destinationExpressions
.Select(parameter => new
{
Parameter = parameter,
Property = parameter.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.FirstOrDefault(property => IsWritable(property) && IsOfType(property, sourceExpression.Type))
})
.FirstOrDefault(parameter => parameter.Property != null);
if (destination == null)
{
throw new InvalidOperationException(string.Format("No writable property of type {0} found in types {1}.", sourceExpression.Type.FullName, string.Join(", ", destinationExpressions.Select(parameter => parameter.Type.FullName))));
}
/* Here is the generic version of mapping code! */
return Expression.IfThen(
Expression.Not(Expression.Equal(destination.Parameter, Expression.Constant(null))),
Expression.Call(destination.Parameter, destination.Property.GetSetMethod(), sourceExpression));
}
Next goes IsWritable(PropertyInfo property) that is used to check validate properties, try to implement convention-based property filtering (names, attributes, etc.) here
public static bool IsWritable(PropertyInfo property)
{
/* eliminating reflection code from extension method */
return property.CanWrite && !property.GetIndexParameters().Any();
}
Next IsOfType(PropertyInfo... and IsSubclassOf methods, - define simple rules of proper TSource->TDestination ways of mapping...
public static bool IsOfType(PropertyInfo property, Type type)
{
/* here AutoMapper could be used too, making filtering needed destination entities by following some convention */
return property.PropertyType == type || IsSubclassOf(type, property.PropertyType) || property.PropertyType.IsAssignableFrom(type);
}
public static bool IsSubclassOf(Type type, Type otherType)
{
return type.IsSubclassOf(otherType);
}
}
Trying to implement convention based mapping approach:
public static void MapFromTranslation<TSource, TDestination, TMember, TTranslation>(this IMemberConfigurationExpression<TSource, TDestination, TMember> opt, Expression<Func<TSource, TDestination, TMember, TTranslation>> mapping )
where TTranslation : ITranslation
Wiring around the Expression<Func<TSource,TDestination,TMember, TTranslation> mapping and the MappingCache<TSource,TDestination,TMember, TTranslation>.Map is the next step. Our lambda expression represents the property transformation intent generically (mapping,conversion,validating,navigating, etc...), and when compiled lambda called with parameters passed, we get the result of such transformation.
Expression:
MappingCache<TSource,TDestination,TMember, TTranslation>.GetSetExpression(first, second, third, proxy...
Function:
var result = MappingCache<TSource,TDestination,TMember, TTranslation>.Map(first,second,third,...
Keeping statically compiled lambda-delegates abstractions open, we can cover every needed mapping aspect with proper tests, - seems like the generic approach that could be used to address the question
Access default map function in MapFrom to fallback
(c) Dapper.Mapper + Tests

Automapper - Get all entries in resolver

I'm working on a project heavily relying on Automapper, and most of the times we are mapping complete sets of data into a set view models, for example
IEnumerable<ObjectA> ListOfObjectA = MockupOfObjectA;
IEnumerable<ViewModelA> = Mapper.Map<IEnumerable<ObjectA>>(ListOfOjectA)
In the mapping setup we are using Custom resolvers thanks to IMemberValueResolver. The parameters and accessible data in a Resolve and ResolveStatic-method, is only the current entity being mapped. Is it possible to access the complete source (ListOfOjectA) in this case, inside the resolver?
So far I am adding the ListOfOjectA into MappingOperationsOptions.Items and use them from context.Items, but this is a work around that is not easy to work with and does not scale well.
I hope I made my question relatively clear.
It's worth pointing out that you're not really mapping ObjectA to ViewModelA. Rather (ObjectA, List<ObjectA>) to ViewModelA, as you can't seem to define ViewModelA without List<ObjectA>.
To simulate, say ObjectA has an Index property as well as the number of Pages it contains.
public class ObjectA
{
public int Index { get; set; }
public int Pages { get; set; }
public string MyProperty { get; set; }
}
And for ViewModelA we want to resolve StartPage, based on the properties of the previous ObjectA's.
public class ViewModelA
{
public int StartPage { get; set; }
public string MyProperty { get; set; }
}
We can clean up your current approach using extension methods.
public static class AutoMapperExt
{
public static TDestination MapWithSource<TSource, TDestination>(this IMapper mapper, TSource source)
=> mapper.Map<TSource, TDestination>(source, opts => opts.Items[typeof(TSource).ToString()] = source);
public static TSource GetSource<TSource>(this ResolutionContext context)
=> (TSource)context.Items[typeof(TSource).ToString()];
}
Using these methods we no longer need to handle the context's Items collection directly.
class Program
{
static void Main(string[] args)
{
var config =
new MapperConfiguration(cfg =>
cfg.CreateMap<ObjectA, ViewModelA>()
.ForMember(dest => dest.StartPage, opt => opt.MapFrom<CustomResolver, int>(src => src.Index))
);
var mapper = config.CreateMapper();
var source = new List<ObjectA>
{
new ObjectA { Index = 0, Pages = 3, MyProperty = "Foo" },
new ObjectA { Index = 1, Pages = 2, MyProperty = "Bar" },
new ObjectA { Index = 2, Pages = 1, MyProperty = "Foz" },
};
var result = mapper.MapWithSource<List<ObjectA>, List<ViewModelA>>(source);
result.ForEach(o => Console.WriteLine(o.StartPage)); // prints 1,4,6
Console.ReadKey();
}
}
public class CustomResolver : IMemberValueResolver<object, object, int, int>
{
public int Resolve(object source, object destination, int sourceMember, int destMember, ResolutionContext context)
{
var index = sourceMember;
var list = context.GetSource<List<ObjectA>>();
var pages = 1;
for (int i = 0; i < index; i++)
{
pages += list[i].Pages;
}
return pages;
}
}
If you want to reuse CustomResolver on different classes, you can abstract the properties it operates on into an interface.
public interface IHavePages
{
int Index { get; }
int Pages { get; }
}
public class ObjectA : IHavePages
{
public int Index { get; set; }
public int Pages { get; set; }
public string MyProperty { get; set; }
}
This way the resolver is no longer bound to a concrete implementation. We can now even use the interface as a type parameter.
public class CustomResolver : IMemberValueResolver<IHavePages, object, int, int>
{
public int Resolve(IHavePages source, object destination, int sourceMember, int destMember, ResolutionContext context)
{
var hasPages = source;
var index = sourceMember;
var list = context.GetSource<List<IHavePages>>();
var pages = 1;
for (int i = 0; i < index; i++)
{
pages += list[i].Pages;
}
return pages;
}
}
All we need to do is transform our List<ObjectA> before mapping.
var listOfObjectA = new List<ObjectA>
{
new ObjectA { Index = 0, Pages = 3, MyProperty = "Foo" },
new ObjectA { Index = 1, Pages = 2, MyProperty = "Bar" },
new ObjectA { Index = 2, Pages = 1, MyProperty = "Foz" },
};
var source = listOfObjectA.OfType<IHavePages>().ToList();
var result = mapper.MapWithSource<List<IHavePages>, List<ViewModelA>>(source);
// AutoMapper still maps properties that aren't part of the interface
result.ForEach(o => Console.WriteLine($"{o.StartPage} - {o.MyProperty}"));
Once you code to an interface, the sourceMember in the CustomResolver becomes redundant. We can now get it through the passed source. Allowing for one final refactor, as we derive from IValueResolver instead of IMemberValueResolver.
public class CustomResolver : IValueResolver<IHavePages, object, int>
{
public int Resolve(IHavePages source, object destination, int destMember, ResolutionContext context)
{
var list = context.GetSource<List<IHavePages>>();
var pages = 1;
for (int i = 0; i < source.Index; i++)
{
pages += list[i].Pages;
}
return pages;
}
}
Updating the signature.
cfg.CreateMap<ObjectA, ViewModelA>()
.ForMember(dest => dest.StartPage, opt => opt.MapFrom<CustomResolver>())
How far you take it is entirely up to you, but you can improve code reuse by introducing abstractions.
You can map a collection of items from a dto collection or other class in a way under.
public Order Convert(OrderDto orderDto)
{
var order = new Order { OrderLines = new OrderLines() };
order.OrderLines = Mapper.Map<List<OrderLine>>(orderDto.Positions);
return order;
}
And your profile class constructor can be written in a way. This is a only a example. You do not need to accept a list in your resolver, you can do it for one object and map to list from outside.
public Profile()
{
CreateMap<PositionDto, OrderLine>()
.ForMember(dest => dest, opt => opt.ResolveUsing<OrderResolver>());
}
}
}
If you prefer not to use the ResolutionContext, you can set up a mapping via an intermediate object holding both the current source item as also the full source list.
Use a lightweight value type, eg. a Tuple or ValueTuple.
The mapping below uses a ValueTuple (but can also be expressed using a Tuple).
Note that the intent and prerequisites of this mapping are quite explicit; it indicates that 2 input/source elements are required: ObjectA and IEnumerable<ObjectA> (passed via a ValueTuple).
Mapper.Initialize(cfg =>
cfg.CreateMap<(ObjectA, IEnumerable<ObjectA>), ViewModelA>()
.ForMember(
dest => dest.Name,
opt => opt.MapFrom<CustomResolver>()
));
At the time of mapping, you project the source list into one of corresponding ValueTuples.
Prefer to keep the flow streaming using only 1 current ValueTuple.
var viewModels =
Mapper.Map<IEnumerable<ViewModelA>>(
ListOfObjectA.Select(o => (o, ListOfObjectA))
);
A custom IValueResolver receives both the current input item and the full list via the ValueTuple source argument.
public class CustomResolver :
IValueResolver<
(ObjectA Item, IEnumerable<ObjectA> List),
ViewModelA,
String
>
{
public string Resolve(
(ObjectA Item, IEnumerable<ObjectA> List) source,
ViewModelA destination,
string destMember,
ResolutionContext context
)
{
/* Retrieve something via the list. */
var suffix = source.List.Count().ToString();
return $"{source.Item.Name} {suffix}";
}
}
Full example.
IEnumerable<ObjectA> ListOfObjectA = new List<ObjectA> {
new ObjectA { Name = "One" },
new ObjectA { Name = "Two" },
new ObjectA { Name = "Three" }
};
Mapper.Initialize(cfg =>
cfg.CreateMap<(ObjectA, IEnumerable<ObjectA>), ViewModelA>()
.ForMember(
dest => dest.Name,
opt => opt.MapFrom<CustomResolver>()
));
var viewModels =
Mapper.Map<IEnumerable<ViewModelA>>(
ListOfObjectA.Select(o => (o, ListOfObjectA))
);

How to reflect an interfaced type<t> at runtime

I have multiple data-points and an associated data-processor for each.
public interface IDataPointProcessor<T> where T : DataPointInputBase
{
DataPointOutputBase GetOutput(T input);
}
I load a list of data points from a file and wish to process them using its single associated processor.
foreach (DataPointInputBase item in input.DataPoints)
{
//assuming item coming in is of type 'X' how do I get correct processor
var type = typeof(IDataPointProcessor<X>);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p) && !p.IsAbstract);
IDataPointProcessor<X> matchedType = ??
}
How do I solve for 'X' so I can instantiate it and process the input?
Update #1
Combining answers from below from Slava and Lucky I get the following, but it throws an exception - 'Object does not match target type.' even though it all seems to match up ok in debugger. Is it possible to cast as IDataPointProcessor<> and call interface method cleanly, ie: instance.GetOutput(item);
foreach (DataPointInputBase item in input.DataPoints)
{
Type typeGenArg = item.GetType();
Type typeInterfaceGen = typeof(IDataPointProcessor<>).MakeGenericType(typeGenArg);
Type type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => typeInterfaceGen.IsAssignableFrom(x) && !x.IsAbstract)
.FirstOrDefault();
Type genericType = typeof(IDataPointProcessor<>);
Type dependedGenericType = genericType.MakeGenericType(typeof(DataPointInputBase));
var method = dependedGenericType.GetMethod("GetOutput");
var instance = Activator.CreateInstance(type);
//currently throws:System.Reflection.TargetException: 'Object does not match target type.'
var result = method.Invoke(instance, new object[] { item });
//Ideally I want to do this and avoid the magic strings etc
//var temp_output = instance.GetOutput(item);
}
Update #2
To keep things moving I've hard coded the type 'Age_Input' to validate the thing works. What am I missing to call the hard coded bit dynamically?
I should be able to cast instance to IDataPointProcessor<IDataPointInput> and call GetOutput() on the interface
foreach (IDataPointInput item in input.DataPoints)
{
Type typeGenArg = item.GetType();
Type typeInterfaceGen = typeof(IDataPointProcessor<>).MakeGenericType(typeGenArg);
Type type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => typeInterfaceGen.IsAssignableFrom(x) && !x.IsAbstract)
.FirstOrDefault();
Type genericType = typeof(IDataPointProcessor<>);
Type dependedGenericType = genericType.MakeGenericType(typeof(IDataPointInput));
var instance = Activator.CreateInstance(type);
if (instance is IDataPointProcessor<Age_Input>)//hard-coded
{
var processor = instance as IDataPointProcessor<Age_Input>;
Age_Input temp = item as Age_Input;
var result = processor.GetOutput(temp);
}
if (instance is DataPointProcessorBase<DataPointInputBase>)
{
//false
}
if (instance is IDataPointProcessor<DataPointInputBase>)
{
//false
}
if (instance is IDataPointProcessor<IDataPointInput>)
{
//false - shouldn't this work?
}
}
Age_Input is a trivial class, inheriting from a dumb base class and an empty interface
public class Age_Input : DataPointInputBase, IDataPointInput
{
public int AgeExact { get; set; }
}
public class DataPointInputBase : IDataPointInput
{
}
public interface IDataPointInput
{
}
Processor class is similarly simple
public abstract class DataPointProcessorBase<T> : IDataPointProcessor<T> where T : IDataPointInput, new()
{
//public abstract DataPointOutputBase GetOutput(DataPointInputBase input);
public abstract DataPointOutputBase GetOutput(T input);
}
public interface IDataPointInput
{
}
public interface IDataPointProcessor<IDataPointInput>
{
DataPointOutputBase GetOutput(IDataPointInput input);
}
Firstly, you should make covariant your interface like this.
public interface IDataPointProcessor<in T> where T : DataPointInputBase
{
DataPointOutputBase GetOutput(T input);
}
You should retrieve types which is implemented by IDataPointProcessor<>, then you should create the instance of retrieved type and invoke the method of generic type.
Type genericType = typeof(IDataPointProcessor<>);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => genericType.IsAssignableFrom(p) && !p.IsAbstract).ToList();
var dependedGenericType = genericType.MakeGenericType(typeof(DataPointInputBase));
var method = dependedGenericType.GetMethod("GetOutput");
var instance = Activator.CreateInstance(types[0]);
method.Invoke(instance, new object[] { new DataPointInputBase() });
So as is usually the case, if you can avoid Reflection you're generally better off. I traded a tiny bit of code smell for a much simpler solution.
Essentially I went back to basics and used dumb interfaces, and a helper method on the input that returned a primed instance of the corresponding processor.
Now my big reflection loop is replaced with this:
foreach (IDataPointInput item in input)
{
IDataPointProcessor processor = item.GetProcessor();
IDataPointOutput output = processor.GetOutput();
}
The code smell is this - not an issue
public override IDataPointProcessor GetProcessor()
{
return new Age_Processor(this);
}
Full code below
#region Interfaces
public interface IDataPointProcessor
{
IDataPointOutput GetOutput();
}
public interface IDataPointInput
{
IDataPointProcessor GetProcessor();
}
public interface IDataPointOutput
{
List<string> DebugStrings { get; set; }
}
#endregion
#region Base Classes
public abstract class DataPointProcessorBase : IDataPointProcessor
{
public abstract IDataPointOutput GetOutput();
}
public abstract class DataPointInputBase : IDataPointInput
{
public abstract IDataPointProcessor GetProcessor();
}
public abstract class DataPointOutputBase : IDataPointOutput
{
public List<string> DebugStrings { get; set; }
public DataPointOutputBase()
{
DebugStrings = new List<string>();
}
}
#endregion
public class Age_Output : DataPointOutputBase
{
}
public class Age_Input : DataPointInputBase
{
public int AgeExact { get; set; }
public override IDataPointProcessor GetProcessor()
{
return new Age_Processor(this);
}
}
public class Age_Processor : DataPointProcessorBase
{
public Age_Input Input { get; set; }
public Age_Processor(Age_Input input)
{
Input = input;
}
public override IDataPointOutput GetOutput()
{
Age_Output output = new Age_Output();
if (Input.AgeExact > 30)
{
output.DebugStrings.Add("Getting old");
}
else
{
output.DebugStrings.Add("Still young");
}
return output;
}
}
public class DecisionEngine
{
public void GetDecisions()
{
List<IDataPointInput> input = new List<IDataPointInput>();
input.Add(new Age_Input { AgeExact = 44 });
foreach (IDataPointInput item in input)
{
IDataPointProcessor processor = item.GetProcessor();
IDataPointOutput output = processor.GetOutput();
}
}
}

Is it possible to create a List using anonymous types on both sides (input and output) of the declaration?

Is it possible to create a list using anonymous types on both sides (input and output) of the declaration?
I would like to do something like this:
List<Class1> list = getList<Class1>(List<Class2> list2);
...using a method declared something like this:
public static List<T1> getList<T1>(List<T2> list2)
{
List<T1> list = new List<T1>();
foreach (var item in list2) {
list.Add(new T1(item));
}
return list;
}
Basically, I just want to convert a list containing class2 items to a new list containing class1 items. My idea was to set up a constructor in Class1, doing the convert job (from class1 to class2). But there might be better ways of doing such a thing.
These are my classes:
public class Class2
{
string prop1 { get; set; }
string prop2 { get; set; }
}
public class Class1
{
string member1 { get; set; }
string member2 { get; set; }
//constructor: 'converts' class1 in class2
public Class1(Class2 class2)
{
this.member1 = Util.getThisAndThat(class2.prop1);
this.member2 = Util.doBunchOfStuff(class2.prop2);
}
}
Any help appreciated :)
Another answer mentioned AutoMapper, but I would use it directly.
Mapper.CreateMap<Class1, Class2>()
.ForMember(dest => dest.member1,
opt => opt.MapFrom(src => Util.getThisAndThat(src.prop1)))
.ForMember(dest => dest.member2,
opt => opt.MapFrom(src => Util.doBunchOfStuff(src.prop2)));
public static List<T1> getList<T1>(List<T2> list2)
{
return Mapper.Map<List<T2>, List<T1>>(list2);
}
You can do something like below, if it works. For each different type like Class1 and Class2 you need to implement the interface. But what you are actually looking for seems like AutoMapper
public interface ILoadable<T>
{
void LoadFrom(T other);
}
public class Class1 : ILoadable<Class2>
{
public void LoadFrom(Class2 other)
{
// Do property mapping here
}
}
public static List<T1> GetList<T1, T2>(List<T2> list2) where T1 : ILoadable<T2>, new()
{
List<T1> list = new List<T1>();
foreach (var item in list2) {
T1 t1 = new T1();
t1.LoadFrom(item);
list.Add(t1);
}
return list;
}
You can just write:
List<Class1> list1 = list2.Select(class2 => new Class1(class2)).ToList();
But for it to work, Class1 must have a constructor that takes Class2 as a parameter, which is not great in my opinion.
I would write a separate static method which converts a Class2 instance to a Class1 instance:
public static Class1 ToClass1(Class2 class2)
{
//return a Class1 instance
}
And do:
List<Class1> list1 = list2.Select(class2 => ToClass1(class2)).ToList();
If you're happy to have some coupling between both classes, you can add a conversion operator to one of them and do:
List<Class1> list1 = list2.Cast<Class1>().ToList();

How to write a Custom DynamicObject class that supports object initializers

In the documentation for DynamicObject, there is an example of a DynamicDictionary that allows you to work with a dictionary as if it's a class with properties.
Here is the class (modified slightly for brevity):
public class DynamicDictionary : DynamicObject
{
Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public int Count
{
get { return _dictionary.Count; }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name.ToLower();
return _dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_dictionary[binder.Name.ToLower()] = value;
return true;
}
}
What I'd like to do is modify the class, so that I can do the following:
public class Test
{
public Test()
{
var result = Enumerable.Range(1, 5).Select(i => new DynamicDictionary
{
Id = i,
Foo = "Foo",
Bar = 2
});
}
}
Questions
Is this possible?
If yes, how?
DynamicObject provides TryCreateInstance(), which is meant for situations like this, but it's not usable from C#.
I see some ways around this:
Create a dynamic factory class. When you call its Create() method with named argumets, it passes it to the dictionary:
class DynamicDictionaryFactory : DynamicObject
{
public override bool TryInvokeMember(
InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name == "Create")
{
// use binder.CallInfo.ArgumentNames and args
// to create the dynamic dictionary
result = …;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
…
dynamic factory = new DynamicDictionaryFactory();
dynamic dict = factory.Create(Id: 42);
Use non-dynamic collection initializer. This means having the property names as strings in the code:
// has to implement IEnumerable, so that collection initializer works
class DynamicDictionary
: DynamicObject, IEnumerable<KeyValuePair<string, object>>
{
public void Add(string name, object value)
{
m_dictionary.Add(name, value);
}
// IEnumerable implmentation and actual DynamicDictionary code here
}
…
dynamic dict = new DynamicDictionary { { "Id", 42 } };
Probably the closest to what you asked for would be to use nested object initializer. That is, the class will have a dynamic property (say, Values), whose properties can be set using object initializer:
class DynamicDictionary : DynamicObject
{
private readonly IDictionary<string, object> m_expandoObject =
new ExpandoObject();
public dynamic Values
{
get { return m_expandoObject; }
}
// DynamicDictionary implementation that uses m_expandoObject here
}
…
dynamic dict = new DynamicDictionary { Values = { Id = 42 } };
Using the open source ImpromptuInterface (via nuget) it has a Builder Syntax. that lets you do something close to the initialization syntax. Specifically after including ImpromptuInterface.Dynamic you could do
var result = Enumerable.Range(1, 5).Select(i => Build<DynamicDictionary>.NewObject
(
Id: i,
Foo: "Foo",
Bar: 2
));
There are other options too listed on that syntax page if you drop the <DynamicDictionary> it will use an ImpromptuDictionary which is essentially the same thing. And you can look at the source for the build syntax too.
It turns out, I was able to solve my problem by using Linq's built-in ToDictionary() method.
Example:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
});
var result = data
.Select(d => d.GetType().GetProperties()
.Select(p => new { Name = p.Name, Value = p.GetValue(pd, null) })
.ToDictionary(
pair => pair.Name,
pair => pair.Value == null ? string.Empty : pair.Value.ToString()));
}
When I see a pattern of embedded language like this, I tend to use extension methods, so I could write the following code to achieve my goal:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
}.AsDynamicDictionary());
}
Then I would define the extension method with the code to convert any object (including anonymously typed ones) to DynamicDictionary:
public static class DynamicDictionaryExtensions
{
public static DynamicDictionary AsDynamicDictionary(this object data)
{
if (data == null) throw new ArgumentNullException("data");
return new DynamicDictionary(
data.GetType().GetProperties()
.Where(p => p. && p.CanRead)
.Select(p => new {Name: p.Name, Value: p.GetValue(data, null)})
.ToDictionary(p => p.Name, p => p.Value)
);
}
}
You would have to implement a constructor in DynamicDictionary to receive a IDictionary<string, object>, but that's a piece of cake.

Categories

Resources