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()
typeMap[typeof(DateTimeOffset?).TypeHandle] = DbType.DateTimeOffset;
}
private static DbType LookupDbType(Type type)
private static DbType LookupDbType(Type type, string name)
{
DbType dbType;
var nullUnderlyingType = Nullable.GetUnderlyingType(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 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)
{
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)
: this(sql, connection.ConnectionString, type, parametersType, otherTypes, 0)
internal Identity(string sql, CommandType? commandType, IDbConnection connection, Type type, Type parametersType, Type[] otherTypes)
: 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.commandType = commandType;
this.connectionString = connectionString;
this.type = type;
this.parametersType = parametersType;
......@@ -233,6 +234,7 @@ private Identity(string sql, string connectionString, Type type, Type parameters
unchecked
{
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 + (sql == null ? 0 : sql.GetHashCode());
hashCode = hashCode * 23 + (type == null ? 0 : type.GetHashCode());
......@@ -251,7 +253,8 @@ public override bool Equals(object obj)
{
return Equals(obj as Identity);
}
private readonly string sql;
internal readonly string sql;
internal readonly CommandType? commandType;
private readonly int hashCode, gridIndex;
private readonly Type type;
private readonly string connectionString;
......@@ -267,6 +270,7 @@ public bool Equals(Identity other)
gridIndex == other.gridIndex &&
type == other.type &&
sql == other.sql &&
commandType == other.commandType &&
connectionString == other.connectionString &&
parametersType == other.parametersType;
}
......@@ -301,7 +305,7 @@ public bool Equals(Identity other)
{
masterSql = cmd.CommandText;
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);
}
else
......@@ -317,7 +321,7 @@ public bool Equals(Identity other)
}
// 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);
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
)
{
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);
IDbCommand cmd = null;
......@@ -380,7 +384,7 @@ public static IEnumerable<dynamic> Query(this IDbConnection cnn, string sql, dyn
/// </summary>
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);
bool clean = true;
try
......@@ -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)
{
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);
IDbCommand ownedCommand = null;
......@@ -646,7 +650,7 @@ private static CacheInfo GetCacheInfo(Identity identity)
}
else
{
info.ParamReader = CreateParamInfoGenerator(identity.parametersType);
info.ParamReader = CreateParamInfoGenerator(identity);
}
}
SetQueryCache(identity, info);
......@@ -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 il = dm.GetILGenerator();
......@@ -895,6 +902,14 @@ public static void PackListParameters(IDbCommand command, string namePrefix, obj
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))
{
il.Emit(OpCodes.Ldloc_0); // stack is now [parameters] [typed-param]
......@@ -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]
continue;
}
DbType dbType = LookupDbType(prop.PropertyType);
DbType dbType = LookupDbType(prop.PropertyType, prop.Name);
if (dbType == DbType.Xml)
{
// this actually represents special handling for list types;
......
......@@ -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