diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs
index b1939d54..ea1803be 100644
--- a/Dapper/SqlMapper.cs
+++ b/Dapper/SqlMapper.cs
@@ -5,6 +5,7 @@
Note: to build on C# 3.0 + .NET 3.5, include the CSHARP30 compiler symbol (and yes,
I know the difference between language and runtime versions; this is a compromise).
*/
+
using System;
using System.Collections;
using System.Collections.Generic;
@@ -18,6 +19,7 @@ using System.Threading;
using System.Text.RegularExpressions;
using System.Diagnostics;
+
namespace Dapper
{
///
@@ -713,7 +715,7 @@ this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transac
///
public static IEnumerable Query(this IDbConnection cnn, string sql, dynamic param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null)
{
- return Query(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);
+ return Query(cnn, sql, param as object, transaction, buffered, commandTimeout, commandType);
}
#else
///
@@ -1174,9 +1176,9 @@ this IDbConnection cnn, string sql, Func)))
@@ -1193,131 +1195,304 @@ this IDbConnection cnn, string sql, Func
- {
- IDictionary data;
- public static FastExpando Attach(IDictionary data)
+#if !CSHARP30
+ sealed partial class DapperTable
+ {
+ string[] fieldNames;
+ readonly Dictionary fieldNameLookup;
+
+ internal string[] FieldNames { get { return fieldNames; } }
+
+ public DapperTable(string[] fieldNames)
{
- return new FastExpando { data = data };
+ if (fieldNames == null) throw new ArgumentNullException("fieldNames");
+ this.fieldNames = fieldNames;
+
+ fieldNameLookup = new Dictionary(fieldNames.Length, StringComparer.Ordinal);
+ // if there are dups, we want the **first** key to be the "winner" - so iterate backwards
+ for (int i = fieldNames.Length - 1; i >= 0; i--)
+ {
+ string key = fieldNames[i];
+ if (key != null)
+ {
+ // in case of dups, keep the *last* key, arbitrarily; avoids a check
+ fieldNameLookup[key] = i;
+ }
+ }
}
- public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
+ internal int IndexOfName(string name)
{
- data[binder.Name] = value;
+ int result;
+ return (name != null && fieldNameLookup.TryGetValue(name, out result)) ? result : -1;
+ }
+ internal int AddField(string name)
+ {
+ if (name == null) throw new ArgumentNullException("name");
+ if (fieldNameLookup.ContainsKey(name)) throw new InvalidOperationException("Field already exists: " + name);
+ int oldLen = fieldNames.Length;
+ Array.Resize(ref fieldNames, oldLen + 1); // yes, this is sub-optimal, but this is not the expected common case
+ fieldNameLookup[name] = oldLen;
+ return oldLen;
+ }
+
+
+ internal bool FieldExists(string key)
+ {
+ return key != null && fieldNameLookup.ContainsKey(key);
+ }
+
+ public int FieldCount { get { return fieldNames.Length; } }
+ }
+
+ sealed partial class DapperRowMetaObject : System.Dynamic.DynamicMetaObject
+ {
+ static readonly MethodInfo getValueMethod = typeof(IDictionary).GetProperty("Item").GetGetMethod();
+ static readonly MethodInfo setValueMethod = typeof(DapperRow).GetMethod("SetValue");
+
+ public DapperRowMetaObject(
+ System.Linq.Expressions.Expression expression,
+ System.Dynamic.BindingRestrictions restrictions
+ )
+ : base(expression, restrictions)
+ {
+ }
+
+ public DapperRowMetaObject(
+ System.Linq.Expressions.Expression expression,
+ System.Dynamic.BindingRestrictions restrictions,
+ object value
+ )
+ : base(expression, restrictions, value)
+ {
+ }
+
+ System.Dynamic.DynamicMetaObject CallMethod(
+ MethodInfo method,
+ System.Linq.Expressions.Expression[] parameters
+ )
+ {
+ var callMethod = new System.Dynamic.DynamicMetaObject(
+ System.Linq.Expressions.Expression.Call(
+ System.Linq.Expressions.Expression.Convert(Expression, LimitType),
+ method,
+ parameters),
+ System.Dynamic.BindingRestrictions.GetTypeRestriction(Expression, LimitType)
+ );
+ return callMethod;
+ }
+
+ public override System.Dynamic.DynamicMetaObject BindGetMember(System.Dynamic.GetMemberBinder binder)
+ {
+ var parameters = new System.Linq.Expressions.Expression[]
+ {
+ System.Linq.Expressions.Expression.Constant(binder.Name)
+ };
+
+ var callMethod = CallMethod(getValueMethod, parameters);
+
+ return callMethod;
+ }
+
+ public override System.Dynamic.DynamicMetaObject BindSetMember(System.Dynamic.SetMemberBinder binder, System.Dynamic.DynamicMetaObject value)
+ {
+ var parameters = new System.Linq.Expressions.Expression[]
+ {
+ System.Linq.Expressions.Expression.Constant(binder.Name),
+ value.Expression,
+ };
+
+ var callMethod = CallMethod(setValueMethod, parameters);
+
+ return callMethod;
+ }
+ }
+
+ sealed partial class DapperRow
+ : System.Dynamic.IDynamicMetaObjectProvider
+ , IDictionary
+ {
+ readonly DapperTable table;
+ object[] values;
+
+ public DapperRow(DapperTable table, object[] values)
+ {
+ if (table == null) throw new ArgumentNullException("table");
+ if (values == null) throw new ArgumentNullException("values");
+ this.table = table;
+ this.values = values;
+ }
+
+ int ICollection>.Count
+ {
+ get { return table.FieldCount; }
+ }
+
+ public bool TryGetValue(string name, out object value)
+ {
+ var index = table.IndexOfName(name);
+ if (index < 0)
+ { // doesn't exist
+ value = null;
+ return false;
+ }
+ // exists, **even if** we don't have a value; consider table rows heterogeneous
+ value = index < values.Length ? values[index] : null;
return true;
}
- public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
+ /* public object SetValue(string name, object value)
{
- return data.TryGetValue(binder.Name, out result);
- }
-
- public override IEnumerable GetDynamicMemberNames()
- {
- return data.Keys;
- }
-
- #region IDictionary Members
-
- void IDictionary.Add(string key, object value)
- {
- data.Add(key, value);
- }
-
- bool IDictionary.ContainsKey(string key)
- {
- return data.ContainsKey(key);
- }
-
- ICollection IDictionary.Keys
- {
- get { return data.Keys; }
- }
-
- bool IDictionary.Remove(string key)
- {
- return data.Remove(key);
- }
-
- bool IDictionary.TryGetValue(string key, out object value)
- {
- return data.TryGetValue(key, out value);
- }
-
- ICollection
@@ -2268,7 +2491,7 @@ Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnN
///
public IEnumerable Read()
{
- return Read();
+ return Read();
}
#endif
diff --git a/Tests/Tests.cs b/Tests/Tests.cs
index 281ff04e..3eebbe30 100644
--- a/Tests/Tests.cs
+++ b/Tests/Tests.cs
@@ -728,7 +728,7 @@ Order by p.Id
connection.Execute("drop table #Users drop table #Posts");
}
-
+
public void TestMultiMapDynamic()
{
var createSql = @"