Skip to content

Commit 1a36180

Browse files
committed
Optimised ObjectToDictionary using dynamic methods
1 parent e3248bd commit 1a36180

3 files changed

Lines changed: 57 additions & 55 deletions

File tree

Simple.Data.Mocking/XmlMockAdapter.cs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55

66
namespace Simple.Data.Mocking
77
{
8-
using System.Collections;
9-
using Extensions;
10-
118
public class XmlMockAdapter : Adapter, IAdapterWithRelation
129
{
1310
private readonly Lazy<XElement> _data;
@@ -194,31 +191,5 @@ private IEnumerable<string> GetKeyFieldNames(string tableName)
194191
if (keyAttribute == null) return Enumerable.Empty<string>();
195192
return keyAttribute.Value.Split(',');
196193
}
197-
198-
private static object ObjectToDictionary(object obj)
199-
{
200-
var dynamicRecord = obj as SimpleRecord;
201-
if (dynamicRecord != null)
202-
{
203-
return new Dictionary<string, object>(dynamicRecord, HomogenizedEqualityComparer.DefaultInstance);
204-
}
205-
206-
var dictionary = obj as IDictionary<string, object>;
207-
if (dictionary != null)
208-
{
209-
return dictionary;
210-
}
211-
212-
var list = obj as IEnumerable;
213-
if (list != null)
214-
{
215-
var originals = list.Cast<object>().ToList();
216-
var dictionaries = originals.Select(o => ObjectToDictionary(o) as IDictionary<string, object>).Where(o => o != null && o.Count > 0).ToList();
217-
if (originals.Count == dictionaries.Count)
218-
return dictionaries;
219-
}
220-
221-
return obj.ObjectToDictionary();
222-
}
223194
}
224195
}

Simple.Data/ConcreteCollectionTypeCreator.cs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
5-
using System.Collections;
6-
using System.Dynamic;
7-
using System.Linq.Expressions;
8-
91
namespace Simple.Data
102
{
3+
using System;
4+
using System.Collections;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
118
internal static class ConcreteCollectionTypeCreator
129
{
1310
private static readonly List<Creator> _creators = new List<Creator>
14-
{
15-
new GenericSetCreator(),
16-
new GenericListCreator(),
17-
new NonGenericListCreator()
18-
};
11+
{
12+
new GenericSetCreator(),
13+
new GenericListCreator(),
14+
new NonGenericListCreator()
15+
};
1916

2017
public static bool IsCollectionType(Type type)
2118
{
@@ -85,10 +82,10 @@ private static bool ConvertEnum(Type type, object value, out object result)
8582
{
8683
if (value is string)
8784
{
88-
result = Enum.Parse(type, (string) value);
85+
result = Enum.Parse(type, (string)value);
8986
return true;
9087
}
91-
88+
9289
result = Enum.ToObject(type, value);
9390
return true;
9491
}
@@ -103,10 +100,10 @@ protected bool TryConvertElements(Type type, IEnumerable items, out Array result
103100
list = items.OfType<object>().ToList();
104101

105102
var array = Array.CreateInstance(type, list.Count);
106-
for(var i = 0; i < array.Length; i++)
103+
for (var i = 0; i < array.Length; i++)
107104
{
108105
object element;
109-
if(!TryConvertElement(type, list[i], out element))
106+
if (!TryConvertElement(type, list[i], out element))
110107
return false;
111108
array.SetValue(element, i);
112109
}
@@ -124,9 +121,9 @@ public override bool IsCollectionType(Type type)
124121
return false;
125122

126123
return type == typeof(IEnumerable) ||
127-
type == typeof(ICollection) ||
128-
type == typeof(IList) ||
129-
type == typeof(ArrayList);
124+
type == typeof(ICollection) ||
125+
type == typeof(IList) ||
126+
type == typeof(ArrayList);
130127
}
131128

132129
public override bool TryCreate(Type type, IEnumerable items, out object result)
@@ -147,7 +144,7 @@ public override bool IsCollectionType(Type type)
147144
return false;
148145

149146
var genericTypeDef = type.GetGenericTypeDefinition();
150-
if(genericTypeDef.GetGenericArguments().Length != 1)
147+
if (genericTypeDef.GetGenericArguments().Length != 1)
151148
return false;
152149

153150
return genericTypeDef == typeof(IEnumerable<>) ||

Simple.Data/Extensions/ObjectEx.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,50 @@
55

66
namespace Simple.Data.Extensions
77
{
8+
using System.Collections.Concurrent;
9+
using System.Linq.Expressions;
10+
using System.Reflection;
11+
812
public static class ObjectEx
913
{
14+
private static readonly ConcurrentDictionary<Type, Func<object, IDictionary<string, object>>> Converters =
15+
new ConcurrentDictionary<Type, Func<object, IDictionary<string, object>>>();
16+
1017
public static IDictionary<string, object> ObjectToDictionary(this object obj)
1118
{
1219
if (obj == null) return new Dictionary<string, object>();
13-
return (from property in obj.GetType().GetProperties()
14-
select
15-
new KeyValuePair<string, object>(property.Name,
16-
property.GetValue(obj, null)))
17-
.ToDictionary();
20+
21+
return Converters.GetOrAdd(obj.GetType(), MakeToDictionaryFunc)(obj);
22+
}
23+
24+
private static Func<object, IDictionary<string, object>> MakeToDictionaryFunc(Type type)
25+
{
26+
var param = Expression.Parameter(typeof(object));
27+
var typed = Expression.Variable(type);
28+
var newDict = Expression.New(typeof(Dictionary<string, object>));
29+
var listInit = Expression.ListInit(newDict, GetElementInitsForType(type, typed));
30+
31+
var block = Expression.Block(new[] { typed },
32+
Expression.Assign(typed, Expression.Convert(param, type)),
33+
listInit);
34+
35+
return Expression.Lambda<Func<object, IDictionary<String, object>>>(block, param).Compile();
36+
}
37+
38+
static readonly MethodInfo DictionaryAddMethod = typeof(Dictionary<string, object>).GetMethod("Add");
39+
40+
static IEnumerable<ElementInit> GetElementInitsForType(Type type, Expression param)
41+
{
42+
return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
43+
.Where(p => p.CanRead)
44+
.Select(p => PropertyToElementInit(p, param));
45+
}
46+
47+
static ElementInit PropertyToElementInit(PropertyInfo propertyInfo, Expression instance)
48+
{
49+
return Expression.ElementInit(DictionaryAddMethod,
50+
Expression.Constant(propertyInfo.Name),
51+
Expression.Convert(Expression.Property(instance, propertyInfo), typeof(object)));
1852
}
1953
}
2054
}

0 commit comments

Comments
 (0)