Commit 5a7c570a authored by mgravell's avatar mgravell

better error message for impossible props; filter props/params when possible

parent d35b72e3
...@@ -185,7 +185,7 @@ static SqlMapper() ...@@ -185,7 +185,7 @@ static SqlMapper()
typeMap[typeof(DateTimeOffset?).TypeHandle] = DbType.DateTimeOffset; typeMap[typeof(DateTimeOffset?).TypeHandle] = DbType.DateTimeOffset;
} }
private static DbType LookupDbType(Type type) private static DbType LookupDbType(Type type, string name)
{ {
DbType dbType; DbType dbType;
var nullUnderlyingType = Nullable.GetUnderlyingType(type); var nullUnderlyingType = Nullable.GetUnderlyingType(type);
...@@ -205,27 +205,28 @@ private static DbType LookupDbType(Type type) ...@@ -205,27 +205,28 @@ private static DbType LookupDbType(Type type)
} }
throw new NotSupportedException(string.Format("The type : {0} is not supported by dapper", type)); throw new NotSupportedException(string.Format("The member {0} of type {1} cannot be used as a parameter value", name, type));
} }
internal class Identity : IEquatable<Identity> internal class Identity : IEquatable<Identity>
{ {
internal Identity ForGrid(Type primaryType, int gridIndex) internal Identity ForGrid(Type primaryType, int gridIndex)
{ {
return new Identity(sql, connectionString, primaryType, parametersType, null, gridIndex); return new Identity(sql, commandType, connectionString, primaryType, parametersType, null, gridIndex);
} }
internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex) internal Identity ForGrid(Type primaryType, Type[] otherTypes, int gridIndex)
{ {
return new Identity(sql, connectionString, primaryType, parametersType, otherTypes, gridIndex); return new Identity(sql, commandType, connectionString, primaryType, parametersType, otherTypes, gridIndex);
} }
internal Identity(string sql, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes) internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)
: this(sql, connection.ConnectionString, type, parametersType, otherTypes, 0) : this(sql, commandType, connection.ConnectionString, type, parametersType, otherTypes, 0)
{ } { }
private Identity(string sql, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex) private Identity(string sql, CommandType? commandType, string connectionString, Type type, Type parametersType, Type[] otherTypes, int gridIndex)
{ {
this.sql = sql; this.sql = sql;
this.commandType = commandType;
this.connectionString = connectionString; this.connectionString = connectionString;
this.type = type; this.type = type;
this.parametersType = parametersType; this.parametersType = parametersType;
...@@ -233,6 +234,7 @@ private Identity(string sql, string connectionString, Type type, Type parameters ...@@ -233,6 +234,7 @@ private Identity(string sql, string connectionString, Type type, Type parameters
unchecked unchecked
{ {
hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this hashCode = 17; // we *know* we are using this in a dictionary, so pre-compute this
hashCode = hashCode * 23 + commandType.GetHashCode();
hashCode = hashCode * 23 + gridIndex.GetHashCode(); hashCode = hashCode * 23 + gridIndex.GetHashCode();
hashCode = hashCode * 23 + (sql == null ? 0 : sql.GetHashCode()); hashCode = hashCode * 23 + (sql == null ? 0 : sql.GetHashCode());
hashCode = hashCode * 23 + (type == null ? 0 : type.GetHashCode()); hashCode = hashCode * 23 + (type == null ? 0 : type.GetHashCode());
...@@ -251,7 +253,8 @@ public override bool Equals(object obj) ...@@ -251,7 +253,8 @@ public override bool Equals(object obj)
{ {
return Equals(obj as Identity); return Equals(obj as Identity);
} }
private readonly string sql; internal readonly string sql;
internal readonly CommandType? commandType;
private readonly int hashCode, gridIndex; private readonly int hashCode, gridIndex;
private readonly Type type; private readonly Type type;
private readonly string connectionString; private readonly string connectionString;
...@@ -267,6 +270,7 @@ public bool Equals(Identity other) ...@@ -267,6 +270,7 @@ public bool Equals(Identity other)
gridIndex == other.gridIndex && gridIndex == other.gridIndex &&
type == other.type && type == other.type &&
sql == other.sql && sql == other.sql &&
commandType == other.commandType &&
connectionString == other.connectionString && connectionString == other.connectionString &&
parametersType == other.parametersType; parametersType == other.parametersType;
} }
...@@ -301,7 +305,7 @@ public bool Equals(Identity other) ...@@ -301,7 +305,7 @@ public bool Equals(Identity other)
{ {
masterSql = cmd.CommandText; masterSql = cmd.CommandText;
isFirst = false; isFirst = false;
identity = new Identity(sql, cnn, null, obj.GetType(), null); identity = new Identity(sql, cmd.CommandType, cnn, null, obj.GetType(), null);
info = GetCacheInfo(identity); info = GetCacheInfo(identity);
} }
else else
...@@ -317,7 +321,7 @@ public bool Equals(Identity other) ...@@ -317,7 +321,7 @@ public bool Equals(Identity other)
} }
// nice and simple // nice and simple
identity = new Identity(sql, cnn, null, (object)param == null ? null : ((object)param).GetType(), null); identity = new Identity(sql, commandType, cnn, null, (object)param == null ? null : ((object)param).GetType(), null);
info = GetCacheInfo(identity); info = GetCacheInfo(identity);
return ExecuteCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType); return ExecuteCommand(cnn, transaction, sql, info.ParamReader, (object)param, commandTimeout, commandType);
} }
...@@ -356,7 +360,7 @@ public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dyn ...@@ -356,7 +360,7 @@ public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dyn
) )
{ {
Identity identity = new Identity(sql, cnn, typeof(GridReader), (object)param == null ? null : ((object)param).GetType(), null); Identity identity = new Identity(sql, commandType, cnn, typeof(GridReader), (object)param == null ? null : ((object)param).GetType(), null);
CacheInfo info = GetCacheInfo(identity); CacheInfo info = GetCacheInfo(identity);
IDbCommand cmd = null; IDbCommand cmd = null;
...@@ -380,7 +384,7 @@ public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dyn ...@@ -380,7 +384,7 @@ public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dyn
/// </summary> /// </summary>
private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType) private static IEnumerable<T> QueryInternal<T>(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)
{ {
var identity = new Identity(sql, cnn, typeof(T), param == null ? null : param.GetType(), null); var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
var info = GetCacheInfo(identity); var info = GetCacheInfo(identity);
bool clean = true; bool clean = true;
try try
...@@ -478,7 +482,7 @@ class DontMap { } ...@@ -478,7 +482,7 @@ class DontMap { }
static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, IDataReader reader, Identity identity) static IEnumerable<TReturn> MultiMapImpl<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(this IDbConnection cnn, string sql, object map, object param, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType, IDataReader reader, Identity identity)
{ {
identity = identity ?? new Identity(sql, cnn, typeof(TFirst), (object)param == null ? null : ((object)param).GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) }); identity = identity ?? new Identity(sql, commandType, cnn, typeof(TFirst), (object)param == null ? null : ((object)param).GetType(), new[] { typeof(TFirst), typeof(TSecond), typeof(TThird), typeof(TFourth), typeof(TFifth) });
CacheInfo cinfo = GetCacheInfo(identity); CacheInfo cinfo = GetCacheInfo(identity);
IDbCommand ownedCommand = null; IDbCommand ownedCommand = null;
...@@ -646,7 +650,7 @@ private static CacheInfo GetCacheInfo(Identity identity) ...@@ -646,7 +650,7 @@ private static CacheInfo GetCacheInfo(Identity identity)
} }
else else
{ {
info.ParamReader = CreateParamInfoGenerator(identity.parametersType); info.ParamReader = CreateParamInfoGenerator(identity);
} }
} }
SetQueryCache(identity, info); SetQueryCache(identity, info);
...@@ -877,8 +881,11 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj ...@@ -877,8 +881,11 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
} }
private static Action<IDbCommand, object> CreateParamInfoGenerator(Type type) private static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity)
{ {
Type type = identity.parametersType;
bool filterParams = identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text;
var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true); var dm = new DynamicMethod(string.Format("ParamInfo{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, type, true);
var il = dm.GetILGenerator(); var il = dm.GetILGenerator();
...@@ -895,6 +902,14 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj ...@@ -895,6 +902,14 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
foreach (var prop in type.GetProperties().OrderBy(p => p.Name)) foreach (var prop in type.GetProperties().OrderBy(p => p.Name))
{ {
if (filterParams)
{
if (identity.sql.IndexOf("@" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0
&& identity.sql.IndexOf(":" + prop.Name, StringComparison.InvariantCultureIgnoreCase) < 0)
{ // can't see the parameter in the text (even in a comment, etc) - burn it with fire
continue;
}
}
if (prop.PropertyType == typeof(DbString)) if (prop.PropertyType == typeof(DbString))
{ {
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param] il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
...@@ -904,7 +919,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj ...@@ -904,7 +919,7 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); // stack is now [parameters] il.EmitCall(OpCodes.Callvirt, typeof(DbString).GetMethod("AddParameter"), null); // stack is now [parameters]
continue; continue;
} }
DbType dbType = LookupDbType(prop.PropertyType); DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
if (dbType == DbType.Xml) if (dbType == DbType.Xml)
{ {
// this actually represents special handling for list types; // this actually represents special handling for list types;
......
...@@ -896,5 +896,28 @@ public void TestMagicParam() ...@@ -896,5 +896,28 @@ public void TestMagicParam()
} }
* */ * */
class WithBizarreData
{
public GenericUriParser Foo { get; set; }
public int Bar { get; set; }
}
public void TestUnexpectedDataMessage()
{
string msg = null;
try {
connection.Query<int>("select count(1) where 1 = @Foo", new WithBizarreData { Foo = new GenericUriParser(GenericUriParserOptions.Default), Bar = 23 }).First();
} catch(Exception ex)
{
msg = ex.Message;
}
msg.IsEqualTo("The member Foo of type System.GenericUriParser cannot be used as a parameter value");
}
public void TestUnexpectedButFilteredDataMessage()
{
int i = connection.Query<int>("select @Bar", new WithBizarreData { Foo = new GenericUriParser(GenericUriParserOptions.Default), Bar = 23 }).Single();
i.IsEqualTo(23);
}
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment