[Mono-bugs] [Bug 522748] InvalidCastException in DynamicInvoke call
bugzilla_noreply at novell.com
bugzilla_noreply at novell.com
Tue Mar 2 13:36:18 EST 2010
http://bugzilla.novell.com/show_bug.cgi?id=522748
http://bugzilla.novell.com/show_bug.cgi?id=522748#c2
andrus moor <kobruleht2 at hot.ee> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |kobruleht2 at hot.ee
--- Comment #2 from andrus moor <kobruleht2 at hot.ee> 2010-03-02 18:36:16 UTC ---
Below is testcase which does not have dblinq dependency. Exception occurs in
MONO 2.6.1/Windows and in 2.4/Linux
using VCS 2008 created exe file. In .NET it runs OK.
Andrus.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Npgsql;
class Program
{
public const string connstr = "SERVER=localhost;USER=postgres;PASSWORD=a";
static void Main()
{
Kontekst kontekst;
kontekst = MyDataContext.ExecQuery<Kontekst>(@"
CREATE temp TABLE kontekst( akuupaev date ) on commit drop;
insert into kontekst ( akuupaev ) values ( null );
SELECT * FROM kontekst"
).SingleOrDefault();
Console.WriteLine("Kontekst " + (kontekst == null).ToString());
Console.ReadLine();
}
}
class ArrayComparer<T> : IEqualityComparer<T[]>
{
private readonly IEqualityComparer<T> comparer;
public ArrayComparer() : this(null) { }
public ArrayComparer(IEqualityComparer<T> comparer)
{
this.comparer = comparer ?? EqualityComparer<T>.Default;
}
public int GetHashCode(T[] values)
{
if (values == null) return 0;
int hashCode = 1;
for (int i = 0; i < values.Length; i++)
{
hashCode = (hashCode * 13) + comparer.GetHashCode(values[i]);
}
return hashCode;
}
public bool Equals(T[] lhs, T[] rhs)
{
if (ReferenceEquals(lhs, rhs)) return true;
if (lhs == null || rhs == null || lhs.Length != rhs.Length) return
false;
for (int i = 0; i < lhs.Length; i++)
{
if (!comparer.Equals(lhs[i], rhs[i])) return false;
}
return true;
}
}
internal class BindingInfo
{
public bool CanBeNull { get; private set; }
public MemberInfo StorageMember { get; private set; }
public MemberInfo BindingMember { get; private set; }
public BindingInfo(bool canBeNull, MemberInfo bindingMember, MemberInfo
storageMember)
{
CanBeNull = canBeNull;
BindingMember = bindingMember;
StorageMember = storageMember;
}
public Type StorageType
{
get
{
switch (StorageMember.MemberType)
{
case MemberTypes.Field:
return ((FieldInfo)StorageMember).FieldType;
case MemberTypes.Property:
return ((PropertyInfo)StorageMember).PropertyType;
default:
throw new NotSupportedException(string.Format("Unexpected
member-type: {0}", StorageMember.Name));
}
}
}
}
static class InitializerCache<T>
{
static readonly Dictionary<string[], Func<IDataRecord, MyDataContext, T>>
convertReaders
= new Dictionary<string[], Func<IDataRecord, MyDataContext, T>>(
new
ArrayComparer<string>(StringComparer.InvariantCultureIgnoreCase)),
vanillaReaders = new Dictionary<string[], Func<IDataRecord,
MyDataContext, T>>(
new
ArrayComparer<string>(StringComparer.InvariantCultureIgnoreCase));
static readonly SortedList<string, BindingInfo> dataMembers
= new SortedList<string,
BindingInfo>(StringComparer.InvariantCultureIgnoreCase);
static bool TryGetBinding(string columnName, out BindingInfo binding)
{
return dataMembers.TryGetValue(columnName, out binding);
}
const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic;
const MemberTypes PROP_FIELD = MemberTypes.Property | MemberTypes.Field;
static MemberInfo GetBindingMember(string name)
{
Type type = typeof(T);
return FirstMember(type.GetMember(name, PROP_FIELD, FLAGS)) ??
FirstMember(type.GetMember(name, PROP_FIELD, FLAGS |
BindingFlags.IgnoreCase));
}
static InitializerCache()
{
Type type = typeof(T);
foreach (MemberInfo member in type.GetMembers(FLAGS))
{
if ((member.MemberType & PROP_FIELD) == 0) continue; // only
applies to prop/fields
ColumnAttribute col = Attribute.GetCustomAttribute(member,
typeof(ColumnAttribute)) as ColumnAttribute;
if (col == null) continue; // not a column
string name = col.Name;
if (string.IsNullOrEmpty(name))
{ // default to self
name = member.Name;
}
string storage = col.Storage;
MemberInfo storageMember;
if (string.IsNullOrEmpty(storage) || storage == name)
{ // default to self
storageMember = member;
}
else
{
// locate prop/field: case-sensitive first, then insensitive
storageMember = GetBindingMember(storage);
if (storageMember == null)
{
throw new InvalidOperationException("Storage member not
found: " + storage);
}
}
if (storageMember.MemberType == MemberTypes.Property &&
!((PropertyInfo)storageMember).CanWrite)
{ // write to a r/o prop?
throw new InvalidOperationException("Cannot write to readonly
storage property: " + storage);
}
// log it...
dataMembers.Add(name, new BindingInfo(col.CanBeNull, member,
storageMember));
}
}
static MemberInfo FirstMember(MemberInfo[] members)
{
return members != null && members.Length > 0 ? members[0] : null;
}
public static Func<IDataRecord, MyDataContext, T> GetInitializer(string[]
names, bool useConversion)
{
if (names == null) throw new ArgumentNullException();
Func<IDataRecord, MyDataContext, T> initializer;
Dictionary<string[], Func<IDataRecord, MyDataContext, T>> cache =
useConversion ? convertReaders : vanillaReaders;
lock (cache)
{
if (!cache.TryGetValue(names, out initializer))
{
initializer = CreateInitializer(names, useConversion);
cache.Add((string[])names.Clone(), initializer);
}
}
return initializer;
}
static Func<IDataRecord, MyDataContext, T> CreateInitializer(string[]
names, bool useConversion)
{
//Trace.WriteLine("Creating initializer for: " + typeof(T).Name);
if (names == null) throw new ArgumentNullException("names");
ParameterExpression readerParam =
Expression.Parameter(typeof(IDataRecord), "record"),
ctxParam = Expression.Parameter(typeof(MyDataContext), "ctx");
Type entityType = typeof(T),
underlyingEntityType = Nullable.GetUnderlyingType(entityType) ??
entityType,
readerType = typeof(IDataRecord);
List<MemberBinding> bindings = new List<MemberBinding>();
Type[] byOrdinal = { typeof(int) };
MethodInfo defaultMethod = readerType.GetMethod("GetValue", byOrdinal),
isNullMethod = readerType.GetMethod("IsDBNull", byOrdinal),
convertMethod = typeof(MyDataContext).GetMethod("OnConvertValue",
BindingFlags.Instance | BindingFlags.NonPublic);
NewExpression ctor = Expression.New(underlyingEntityType); // try this
first...
for (int ordinal = 0; ordinal < names.Length; ordinal++)
{
string name = names[ordinal];
BindingInfo bindingInfo;
if (!TryGetBinding(name, out bindingInfo))
{ // try implicit binding
MemberInfo member = GetBindingMember(name);
if (member == null) continue; // not bound
bindingInfo = new BindingInfo(true, member, member);
}
Type valueType = bindingInfo.StorageType;
Type underlyingType = Nullable.GetUnderlyingType(valueType) ??
valueType;
// get the rhs of a binding
MethodInfo method = readerType.GetMethod("Get" +
underlyingType.Name, byOrdinal);
Expression rhs;
ConstantExpression ordinalExp = Expression.Constant(ordinal,
typeof(int));
if (method != null && method.ReturnType == underlyingType)
{
rhs = Expression.Call(readerParam, method, ordinalExp);
}
else
{
rhs = Expression.Convert(Expression.Call(readerParam,
defaultMethod, ordinalExp), underlyingType);
}
if (underlyingType != valueType)
{ // Nullable<T>; convert underlying T to T?
rhs = Expression.Convert(rhs, valueType);
}
if (bindingInfo.CanBeNull && (underlyingType.IsClass ||
underlyingType != valueType))
{
// reference-type of Nullable<T>; check for null
// (conditional ternary operator)
rhs = Expression.Condition(
Expression.Call(readerParam, isNullMethod, ordinalExp),
Expression.Constant(null, valueType), rhs);
}
if (useConversion)
{
rhs = Expression.Convert(Expression.Call(ctxParam,
convertMethod, ordinalExp, readerParam,
Expression.Convert(rhs, typeof(object))), valueType);
}
bindings.Add(Expression.Bind(bindingInfo.StorageMember, rhs));
}
Expression body = Expression.MemberInit(ctor, bindings);
if (entityType != underlyingEntityType)
{ // entity itself was T? - so convert
body = Expression.Convert(body, entityType);
}
return Expression.Lambda<Func<IDataRecord, MyDataContext, T>>(body,
readerParam, ctxParam).Compile();
}
}
public class ValueConversionEventArgs : EventArgs
{
internal void Init(int ordinal, IDataRecord record, object value)
{
Ordinal = ordinal;
Record = record;
Value = value;
}
internal ValueConversionEventArgs() { }
public ValueConversionEventArgs(int ordinal, IDataRecord record, object
value)
{
Init(ordinal, record, value);
}
public int Ordinal { get; private set; }
public object Value { get; set; }
public IDataRecord Record { get; private set; }
}
public class MyDataContext
{
public static List<TEntity> ExecQuery<TEntity>(string command, params
object[] prm)
where TEntity : class, new()
{
IEnumerable<TEntity> res;
var db = new MyDataContext();
res = db.ExecuteQuery<TEntity>(command, prm);
if (res == null)
throw new ArgumentException(command);
List<TEntity> res2;
//try
//{
res2 = res.ToList();
//}
//catch (InvalidCastException ex)
//{
// throw new ArgumentException("Cast error on query " + command,
"command", ex);
//}
return res2;
}
readonly ValueConversionEventArgs conversionArgs = new
ValueConversionEventArgs();
public event EventHandler<ValueConversionEventArgs> ConvertValue;
internal object OnConvertValue(int ordinal, IDataRecord record, object
value)
{
if (ConvertValue == null)
{
return value;
}
else
{
conversionArgs.Init(ordinal, record, value);
ConvertValue(this, conversionArgs);
return conversionArgs.Value;
}
}
public IEnumerable<T> ExecuteQuery<T>(string command, params object[]
parameters)
{
if (parameters == null) throw new ArgumentNullException("parameters");
using (IDbConnection conn = new NpgsqlConnection(Program.connstr))
using (IDbCommand cmd = conn.CreateCommand())
{
string[] paramNames = new string[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
paramNames[i] = "@p" + i.ToString();
IDbDataParameter param = cmd.CreateParameter();
param.ParameterName = paramNames[i];
param.Value = parameters[i] ?? DBNull.Value;
cmd.Parameters.Add(param);
}
cmd.CommandType = CommandType.Text;
cmd.CommandText = string.Format(command, paramNames);
conn.Open();
using (IDataReader reader =
cmd.ExecuteReader(CommandBehavior.CloseConnection |
CommandBehavior.SingleResult))
{
if (reader.Read())
{
string[] names = new string[reader.FieldCount];
for (int i = 0; i < names.Length; i++)
{
names[i] = reader.GetName(i);
}
Func<IDataRecord, MyDataContext, T> objInit =
InitializerCache<T>.GetInitializer(names, ConvertValue != null);
do
{ // walk the data
yield return objInit(reader, this);
} while (reader.Read());
}
while (reader.NextResult()) { } // ensure any trailing errors
caught
}
}
}
}
[Table(Name = "kontekst")]
public class Kontekst
{
System.DateTime? akuupaev;
[Column(Storage = "akuupaev", Name = "akuupaev", DbType = "date",
Expression = null)]
public System.DateTime? Akuupaev
{
get
{
return akuupaev;
}
set
{
if (value != akuupaev)
{
akuupaev = value;
}
}
}
}
--
Configure bugmail: http://bugzilla.novell.com/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
More information about the mono-bugs
mailing list