http://bugzilla.novell.com/show_bug.cgi?id=522748
http://bugzilla.novell.com/show_bug.cgi?id=522748#c2 andrus moor <[email protected]> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |[email protected] --- Comment #2 from andrus moor <[email protected]> 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. _______________________________________________ mono-bugs maillist - [email protected] http://lists.ximian.com/mailman/listinfo/mono-bugs
