[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