Maximum performance with the SmartConventionInjection
this is a new injection that I will include in the next release
It's similar to the ConventionInjection except this one remembers the properties that are used to inject value from each combination of types T1,T2 ( T1.InjectFrom<Smart>(T2) ), so it doesn't has to look for them each time.
It has one limitation comparing to the ConventionInjection, you don't have the values of the Source and Target Properties in the Match method but you have them in the SetValue Method and you can cancel the setting of the value to that property if you set false to the ref parameter setValue
Speed Test
for 10000 iterations
Convention: 00:00:00.7606872
AutoMapper: 00:00:00.6242320
Smart convention: 00:00:00.3352158
The Test
comparing the SmartConventionInjection with ConventionInjection and AutoMapper
public class PerformanceTests
{
public class Foo
{
public string Name { get; set; }
public string Prop1 { get; set; }
public object Prop2 { get; set; }
public string Prop3 { get; set; }
public int Prop4 { get; set; }
public int Prop5 { get; set; }
}
public class Smart : SmartConventionInjection
{
protected override bool Match(SmartConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
}
public class Conv : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name;
}
}
[Test]
public void SpeedTest()
{
var foo = new Foo { Prop3 = "Aaa", Name = "foo" };
AutoMapper.Mapper.CreateMap<Foo, Foo>();
var w = new Stopwatch();
w.Reset();
w.Start();
for (var i = 0; i < 10000; i++)
{
new Foo().InjectFrom<Conv>(foo);
}
w.Stop();
Console.WriteLine("Convention: " + w.Elapsed);
w.Reset();
w.Start();
for (var i = 0; i < 10000; i++)
{
AutoMapper.Mapper.Map<Foo,Foo>(foo);
}
w.Stop();
Console.WriteLine("Automapper: " + w.Elapsed);
w.Reset();
w.Start();
for (var i = 0; i < 10000; i++)
{
new Foo().InjectFrom<Smart>(foo);
}
w.Stop();
Console.WriteLine("Smart convention: " + w.Elapsed);
w.Reset();
}
}
The Injection
public class SmartConventionInjection : ValueInjection
{
private class Path
{
public IDictionary<string, string> MatchingProps { get; set; }
}
private static readonly ConcurrentDictionary<Type, ConcurrentDictionary<KeyValuePair<Type, Type>, Path>> WasLearned = new ConcurrentDictionary<Type, ConcurrentDictionary<KeyValuePair<Type, Type>, Path>>();
protected virtual void SetValue(PropertyDescriptor prop, object component, object value)
{
prop.SetValue(component, value);
}
protected virtual object GetValue(PropertyDescriptor prop, object component)
{
return prop.GetValue(component);
}
protected virtual bool Match(SmartConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.PropertyType == c.TargetProp.PropertyType;
}
protected virtual void ExecuteMatch(SmartMatchInfo mi)
{
SetValue(mi.TargetProp, mi.Target, GetValue(mi.SourceProp, mi.Source));
}
private Path Learn(object source, object target)
{
Path path = null;
var sourceProps = source.GetProps();
var targetProps = target.GetProps();
var smartConventionInfo = new SmartConventionInfo
{
SourceType = source.GetType(),
TargetType = target.GetType()
};
for (var i = 0; i < sourceProps.Count; i++)
{
var sourceProp = sourceProps[i];
smartConventionInfo.SourceProp = sourceProp;
for (var j = 0; j < targetProps.Count; j++)
{
var targetProp = targetProps[j];
smartConventionInfo.TargetProp = targetProp;
if (!Match(smartConventionInfo)) continue;
if (path == null)
path = new Path
{
MatchingProps = new Dictionary<string, string> { { smartConventionInfo.SourceProp.Name, smartConventionInfo.TargetProp.Name } }
};
else path.MatchingProps.Add(smartConventionInfo.SourceProp.Name, smartConventionInfo.TargetProp.Name);
}
}
return path;
}
protected override void Inject(object source, object target)
{
var sourceProps = source.GetProps();
var targetProps = target.GetProps();
var cacheEntry = WasLearned.GetOrAdd(GetType(), new ConcurrentDictionary<KeyValuePair<Type, Type>, Path>());
var path = cacheEntry.GetOrAdd(new KeyValuePair<Type, Type>(source.GetType(), target.GetType()), pair => Learn(source, target));
if (path == null) return;
foreach (var pair in path.MatchingProps)
{
var sourceProp = sourceProps.GetByName(pair.Key);
var targetProp = targetProps.GetByName(pair.Value);
ExecuteMatch(new SmartMatchInfo
{
Source = source,
Target = target,
SourceProp = sourceProp,
TargetProp = targetProp
});
}
}
}
public class SmartConventionInfo
{
public Type SourceType { get; set; }
public Type TargetType { get; set; }
public PropertyDescriptor SourceProp { get; set; }
public PropertyDescriptor TargetProp { get; set; }
}
public class SmartMatchInfo
{
public PropertyDescriptor SourceProp { get; set; }
public PropertyDescriptor TargetProp { get; set; }
public object Source { get; set; }
public object Target { get; set; }
}