Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 04fe97b

Browse files
committed
Added support for "Contains(x.Nullable.Value)" expressions to SqlExpression - useful when the database field is nullable, but the input array/list is not
1 parent 94db4b1 commit 04fe97b

File tree

5 files changed

+43
-18
lines changed

5 files changed

+43
-18
lines changed

src/ServiceStack.OrmLite.SqlServer.Converters/SqlServerHierarchyIdTypeConverter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ public override DbType DbType
2727
public override void InitDbParam(IDbDataParameter p, Type fieldType)
2828
{
2929
var sqlParam = (SqlParameter)p;
30-
sqlParam.IsNullable = (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>));
30+
sqlParam.IsNullable = fieldType.IsNullableType();
3131
sqlParam.SqlDbType = SqlDbType.Udt;
3232
sqlParam.UdtTypeName = ColumnDefinition;
3333
}
3434

3535
public override object FromDbValue(Type fieldType, object value)
3636
{
37-
if (((SqlHierarchyId)value).IsNull && fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
37+
if (((SqlHierarchyId)value).IsNull && fieldType.IsNullableType())
3838
{
3939
return null;
4040
}

src/ServiceStack.OrmLite.SqlServer.Converters/SqlServerTypeConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public override void InitDbParam(IDbDataParameter p, Type fieldType)
1515
{
1616
var sqlParam = (SqlParameter)p;
1717
sqlParam.SqlDbType = SqlDbType.Udt;
18-
sqlParam.IsNullable = (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>));
18+
sqlParam.IsNullable = fieldType.IsNullableType();
1919
sqlParam.UdtTypeName = ColumnDefinition;
2020
}
2121
}

src/ServiceStack.OrmLite/Expressions/SqlExpression.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,11 +1335,13 @@ protected virtual void ConvertToPlaceholderAndParameter(ref object right)
13351335

13361336
protected virtual object VisitMemberAccess(MemberExpression m)
13371337
{
1338-
if (m.Expression != null &&
1339-
(m.Expression.NodeType == ExpressionType.Parameter ||
1340-
m.Expression.NodeType == ExpressionType.Convert))
1338+
if (m.Expression != null)
13411339
{
1342-
return GetMemberExpression(m);
1340+
if (m.Member.DeclaringType.IsNullableType() && m.Member.Name == nameof(Nullable<bool>.Value))
1341+
return Visit(m.Expression);
1342+
1343+
if (m.Expression.NodeType == ExpressionType.Parameter || m.Expression.NodeType == ExpressionType.Convert)
1344+
return GetMemberExpression(m);
13431345
}
13441346

13451347
return CachedExpressionCompiler.Evaluate(m);

src/ServiceStack.OrmLite/OrmLiteConfigExtensions.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ internal static class OrmLiteConfigExtensions
2222
{
2323
private static Dictionary<Type, ModelDefinition> typeModelDefinitionMap = new Dictionary<Type, ModelDefinition>();
2424

25-
private static bool IsNullableType(Type theType)
26-
{
27-
return (theType.IsGenericType
28-
&& theType.GetGenericTypeDefinition() == typeof(Nullable<>));
29-
}
30-
3125
internal static bool CheckForIdField(IEnumerable<PropertyInfo> objProperties)
3226
{
3327
// Not using Linq.Where() and manually iterating through objProperties just to avoid dependencies on System.Xml??
@@ -104,7 +98,7 @@ internal static ModelDefinition GetModelDefinition(this Type modelType)
10498
var isRowVersion = propertyInfo.Name == ModelDefinition.RowVersionName
10599
&& propertyInfo.PropertyType == typeof(ulong);
106100

107-
var isNullableType = IsNullableType(propertyInfo.PropertyType);
101+
var isNullableType = propertyInfo.PropertyType.IsNullableType();
108102

109103
var isNullable = (!propertyInfo.PropertyType.IsValueType
110104
&& !propertyInfo.HasAttributeNamed(typeof(RequiredAttribute).Name))

tests/ServiceStack.OrmLite.Tests/ExpressionVisitorTests.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Data;
34
using System.Linq;
45
using NUnit.Framework;
@@ -16,10 +17,10 @@ public void Setup()
1617
using (var db = OpenDbConnection())
1718
{
1819
db.DropAndCreateTable<TestType>();
19-
db.Insert(new TestType { Id = 1, BoolCol = true, DateCol = new DateTime(2012, 1, 1), TextCol = "asdf", EnumCol = TestEnum.Val0 });
20-
db.Insert(new TestType { Id = 2, BoolCol = true, DateCol = new DateTime(2012, 2, 1), TextCol = "asdf123", EnumCol = TestEnum.Val1 });
21-
db.Insert(new TestType { Id = 3, BoolCol = false, DateCol = new DateTime(2012, 3, 1), TextCol = "qwer", EnumCol = TestEnum.Val2 });
22-
db.Insert(new TestType { Id = 4, BoolCol = false, DateCol = new DateTime(2012, 4, 1), TextCol = "qwer123", EnumCol = TestEnum.Val3 });
20+
db.Insert(new TestType { Id = 1, BoolCol = true, DateCol = new DateTime(2012, 1, 1), TextCol = "asdf", EnumCol = TestEnum.Val0, NullableIntCol = 10 });
21+
db.Insert(new TestType { Id = 2, BoolCol = true, DateCol = new DateTime(2012, 2, 1), TextCol = "asdf123", EnumCol = TestEnum.Val1, NullableIntCol = null });
22+
db.Insert(new TestType { Id = 3, BoolCol = false, DateCol = new DateTime(2012, 3, 1), TextCol = "qwer", EnumCol = TestEnum.Val2, NullableIntCol = 30 });
23+
db.Insert(new TestType { Id = 4, BoolCol = false, DateCol = new DateTime(2012, 4, 1), TextCol = "qwer123", EnumCol = TestEnum.Val3, NullableIntCol = 40 });
2324
}
2425
Db = OpenDbConnection();
2526
}
@@ -145,6 +146,33 @@ public void Can_Select_using_IN_using_object_array()
145146
Assert.AreEqual(3, target.Count);
146147
}
147148

149+
[Test]
150+
public void Can_Select_using_int_array_Contains()
151+
{
152+
var ids = new[] { 1, 2 };
153+
var q = Db.From<TestType>().Where(x => ids.Contains(x.Id));
154+
var target = Db.Select(q);
155+
CollectionAssert.AreEquivalent(ids, target.Select(t => t.Id).ToArray());
156+
}
157+
158+
[Test]
159+
public void Can_Select_using_int_list_Contains()
160+
{
161+
var ids = new List<int> { 1, 2 };
162+
var q = Db.From<TestType>().Where(x => ids.Contains(x.Id));
163+
var target = Db.Select(q);
164+
CollectionAssert.AreEquivalent(ids, target.Select(t => t.Id).ToArray());
165+
}
166+
167+
[Test]
168+
public void Can_Select_using_int_array_Contains_Value()
169+
{
170+
var ints = new[] { 10, 40 };
171+
var q = Db.From<TestType>().Where(x => ints.Contains(x.NullableIntCol.Value)); // Doesn't compile without ".Value" here - "ints" is not nullable
172+
var target = Db.Select(q);
173+
CollectionAssert.AreEquivalent(new[] { 1, 4 }, target.Select(t => t.Id).ToArray());
174+
}
175+
148176
[Test]
149177
public void Can_Select_using_Startswith()
150178
{
@@ -240,5 +268,6 @@ public class TestType
240268
public DateTime DateCol { get; set; }
241269
public TestEnum EnumCol { get; set; }
242270
public TestType ComplexObjCol { get; set; }
271+
public int? NullableIntCol { get; set; }
243272
}
244273
}

0 commit comments

Comments
 (0)