Skip to content

Commit 7b61bae

Browse files
committed
Fix casting for custom registered types
1 parent f8dd4ee commit 7b61bae

File tree

5 files changed

+153
-2
lines changed

5 files changed

+153
-2
lines changed

src/NHibernate.DomainModel/NHSpecific/NullableInt32.cs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace NHibernate.DomainModel.NHSpecific
77
/// A nullable type that wraps an <see cref="Int32"/> value.
88
/// </summary>
99
[TypeConverter(typeof(NullableInt32Converter)), Serializable()]
10-
public struct NullableInt32 : IFormattable, IComparable
10+
public struct NullableInt32 : IFormattable, IComparable, IConvertible
1111
{
1212
public static readonly NullableInt32 Default = new NullableInt32();
1313

@@ -234,5 +234,94 @@ public static NullableInt32 Parse(string s)
234234
// TODO: implement the rest of the Parse overloads found in Int32
235235

236236
#endregion
237+
238+
#region IConvertible
239+
240+
public TypeCode GetTypeCode()
241+
{
242+
return _value.GetTypeCode();
243+
}
244+
245+
public bool ToBoolean(IFormatProvider provider)
246+
{
247+
return ((IConvertible) _value).ToBoolean(provider);
248+
}
249+
250+
public char ToChar(IFormatProvider provider)
251+
{
252+
return ((IConvertible) _value).ToChar(provider);
253+
}
254+
255+
public sbyte ToSByte(IFormatProvider provider)
256+
{
257+
return ((IConvertible) _value).ToSByte(provider);
258+
}
259+
260+
public byte ToByte(IFormatProvider provider)
261+
{
262+
return ((IConvertible) _value).ToByte(provider);
263+
}
264+
265+
public short ToInt16(IFormatProvider provider)
266+
{
267+
return ((IConvertible) _value).ToInt16(provider);
268+
}
269+
270+
public ushort ToUInt16(IFormatProvider provider)
271+
{
272+
return ((IConvertible) _value).ToUInt16(provider);
273+
}
274+
275+
public int ToInt32(IFormatProvider provider)
276+
{
277+
return ((IConvertible) _value).ToInt32(provider);
278+
}
279+
280+
public uint ToUInt32(IFormatProvider provider)
281+
{
282+
return ((IConvertible) _value).ToUInt32(provider);
283+
}
284+
285+
public long ToInt64(IFormatProvider provider)
286+
{
287+
return ((IConvertible) _value).ToInt64(provider);
288+
}
289+
290+
public ulong ToUInt64(IFormatProvider provider)
291+
{
292+
return ((IConvertible) _value).ToUInt64(provider);
293+
}
294+
295+
public float ToSingle(IFormatProvider provider)
296+
{
297+
return ((IConvertible) _value).ToSingle(provider);
298+
}
299+
300+
public double ToDouble(IFormatProvider provider)
301+
{
302+
return ((IConvertible) _value).ToDouble(provider);
303+
}
304+
305+
public decimal ToDecimal(IFormatProvider provider)
306+
{
307+
return ((IConvertible) _value).ToDecimal(provider);
308+
}
309+
310+
public DateTime ToDateTime(IFormatProvider provider)
311+
{
312+
return ((IConvertible) _value).ToDateTime(provider);
313+
}
314+
315+
public string ToString(IFormatProvider provider)
316+
{
317+
return _value.ToString(provider);
318+
}
319+
320+
public object ToType(System.Type conversionType, IFormatProvider provider)
321+
{
322+
return ((IConvertible) _value).ToType(conversionType, provider);
323+
}
324+
325+
#endregion
237326
}
238327
}

src/NHibernate.Test/Async/Linq/SelectionTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
using System;
1212
using System.Collections.Generic;
1313
using System.Linq;
14+
using NHibernate.DomainModel.NHSpecific;
1415
using NHibernate.DomainModel.Northwind.Entities;
16+
using NHibernate.Type;
1517
using NUnit.Framework;
1618
using NHibernate.Linq;
1719

@@ -453,6 +455,23 @@ public async Task CanSelectConditionalObjectAsync()
453455
Assert.That(fatherIsKnown, Has.Exactly(1).With.Property("FatherIsKnown").True);
454456
}
455457

458+
[Test]
459+
public async Task CanCastToDerivedTypeAsync()
460+
{
461+
var dogs = await (db.Animals
462+
.Where(a => ((Dog) a).Pregnant)
463+
.Select(a => new {a.SerialNumber})
464+
.ToListAsync());
465+
Assert.That(dogs, Has.Exactly(1).With.Property("SerialNumber").Not.Null);
466+
}
467+
468+
[Test]
469+
public async Task CanCastToCustomRegisteredTypeAsync()
470+
{
471+
TypeFactory.RegisterType(typeof(NullableInt32), new NullableInt32Type(), Enumerable.Empty<string>());
472+
Assert.That(await (db.Users.Where(o => (NullableInt32) o.Id == 1).ToListAsync()), Has.Count.EqualTo(1));
473+
}
474+
456475
public class Wrapper<T>
457476
{
458477
public T item;

src/NHibernate.Test/Linq/SelectionTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using NHibernate.DomainModel.NHSpecific;
45
using NHibernate.DomainModel.Northwind.Entities;
6+
using NHibernate.Type;
57
using NUnit.Framework;
68

79
namespace NHibernate.Test.Linq
@@ -492,6 +494,23 @@ public void CanSelectConditionalObject()
492494
Assert.That(fatherIsKnown, Has.Exactly(1).With.Property("FatherIsKnown").True);
493495
}
494496

497+
[Test]
498+
public void CanCastToDerivedType()
499+
{
500+
var dogs = db.Animals
501+
.Where(a => ((Dog) a).Pregnant)
502+
.Select(a => new {a.SerialNumber})
503+
.ToList();
504+
Assert.That(dogs, Has.Exactly(1).With.Property("SerialNumber").Not.Null);
505+
}
506+
507+
[Test]
508+
public void CanCastToCustomRegisteredType()
509+
{
510+
TypeFactory.RegisterType(typeof(NullableInt32), new NullableInt32Type(), Enumerable.Empty<string>());
511+
Assert.That(db.Users.Where(o => (NullableInt32) o.Id == 1).ToList(), Has.Count.EqualTo(1));
512+
}
513+
495514
public class Wrapper<T>
496515
{
497516
public T item;

src/NHibernate/Hql/Ast/HqlTreeNode.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,30 @@ internal HqlIdent(IASTFactory factory, System.Type type)
257257
throw new NotSupportedException(string.Format("Don't currently support idents of type {0}", type.Name));
258258
}
259259
}
260+
261+
internal static bool SupportsType(System.Type type)
262+
{
263+
type = type.UnwrapIfNullable();
264+
switch (System.Type.GetTypeCode(type))
265+
{
266+
case TypeCode.Boolean:
267+
case TypeCode.Int16:
268+
case TypeCode.Int32:
269+
case TypeCode.Int64:
270+
case TypeCode.Decimal:
271+
case TypeCode.Single:
272+
case TypeCode.DateTime:
273+
case TypeCode.String:
274+
case TypeCode.Double:
275+
return true;
276+
default:
277+
return new[]
278+
{
279+
typeof(Guid),
280+
typeof(DateTimeOffset)
281+
}.Contains(type);
282+
}
283+
}
260284
}
261285

262286
public class HqlRange : HqlStatement

src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ protected HqlTreeNode VisitUnaryExpression(UnaryExpression expression)
488488
return IsCastRequired(expression.Operand, expression.Type, out var existType)
489489
? _hqlTreeBuilder.Cast(VisitExpression(expression.Operand).AsExpression(), expression.Type)
490490
// Make a transparent cast when an IType exists, so that it can be used to retrieve the value from the data reader
491-
: existType
491+
: existType && HqlIdent.SupportsType(expression.Type)
492492
? _hqlTreeBuilder.TransparentCast(VisitExpression(expression.Operand).AsExpression(), expression.Type)
493493
: VisitExpression(expression.Operand);
494494
}

0 commit comments

Comments
 (0)