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

Commit 66f0c60

Browse files
author
KevinHoward
committed
Updated JSON support for SQL Server 2016+
Added unit test cases
1 parent 8fbafda commit 66f0c60

File tree

4 files changed

+290
-135
lines changed

4 files changed

+290
-135
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Data;
3+
using System.Data.SqlClient;
4+
using ServiceStack.DataAnnotations;
5+
using ServiceStack.OrmLite.Converters;
6+
using ServiceStack.Text;
7+
8+
namespace ServiceStack.OrmLite.SqlServer.Converters
9+
{
10+
public class SqlServerJsonToObjectConverter : SqlServerStringConverter
11+
{
12+
public override object FromDbValue(Type fieldType, object value)
13+
{
14+
var deflt = fieldType.GetDefaultValue();
15+
if (value == null || value == deflt)
16+
return deflt;
17+
18+
var json = value.ToString();
19+
return JsonSerializer.DeserializeFromString(json, fieldType);
20+
}
21+
22+
public override object ToDbValue(Type fieldType, object value)
23+
{
24+
if (value != null && value.GetType().HasInterface(typeof(ISqlJson)))
25+
{
26+
return value.ToJson();
27+
}
28+
29+
return base.ToDbValue(fieldType, value);
30+
}
31+
}
32+
33+
public interface ISqlJson
34+
{ }
35+
}

src/ServiceStack.OrmLite.SqlServer/SqlServer2016OrmLiteDialectProvider.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
using System;
2-
using System.Data;
3-
using System.Text;
4-
using ServiceStack.Text;
1+
using ServiceStack.OrmLite.SqlServer.Converters;
52

63
namespace ServiceStack.OrmLite.SqlServer
74
{
85
public class SqlServer2016OrmLiteDialectProvider : SqlServer2014OrmLiteDialectProvider
96
{
7+
public SqlServer2016OrmLiteDialectProvider() : base()
8+
{
9+
base.RegisterConverter<string>(new SqlServerJsonToObjectConverter());
10+
}
11+
1012
public new static SqlServer2016OrmLiteDialectProvider Instance = new SqlServer2016OrmLiteDialectProvider();
1113

1214
public override SqlExpression<T> SqlExpression<T>() => new SqlServer2016Expression<T>(this);
Lines changed: 210 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,215 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Configuration;
4+
5+
using ServiceStack.Logging;
6+
using ServiceStack.OrmLite;
7+
using ServiceStack.OrmLite.SqlServer.Converters;
8+
29
using NUnit.Framework;
310

411
namespace ServiceStack.OrmLite.SqlServerTests.Expressions
512
{
6-
public class JsonExpressionsTest : OrmLiteTestBase
7-
{
8-
[Test]
9-
public void Can_select_json_scalar_value()
10-
{
11-
using (var db = OpenDbConnection(dialectProvider: SqlServer2016Dialect.Provider))
12-
{
13-
db.DropAndCreateTable<TestType>();
14-
15-
var obj = new
16-
{
17-
Address = new Address
18-
{
19-
Line1 = "1234 Main Street",
20-
Line2 = "Apt. 404",
21-
City = "Las Vegas",
22-
State = "NV"
23-
}
24-
};
25-
26-
//{ "Address": { "Line1": "1234 Main Street", "Line2": "Apt. 404", "City": "Las Vegas", "State": "NV" } }
27-
var stringValue = obj.ToJson();
28-
29-
db.Insert(new TestType { StringColumn = stringValue });
30-
31-
List<TestType> actual = db.Select<TestType>(q =>
32-
Sql.JsonValue(q.StringColumn, "$.address.state") == "NV");
33-
34-
Assert.That(actual, Is.EqualTo(obj.Address.State));
35-
}
36-
}
37-
38-
[Test]
39-
public void Can_select_json_object_value()
40-
{
41-
using (var db = OpenDbConnection(dialectProvider: SqlServer2016Dialect.Provider))
42-
{
43-
db.DropAndCreateTable<TestType>();
44-
45-
var expected = new Address
46-
{
47-
Line1 = "1234 Main Street",
48-
Line2 = "Apt. 404",
49-
City = "Las Vegas",
50-
State = "NV"
51-
};
52-
var obj = new { Address = expected };
53-
54-
//{ "Address": { "Line1": "1234 Main Street", "Line2": "Apt. 404", "City": "Las Vegas", "State": "NV" } }
55-
var stringValue = obj.ToJson();
56-
57-
db.Insert(new TestType { StringColumn = stringValue });
58-
59-
SqlExpression<TestType> q = db.From<TestType>().Select(x =>
60-
Sql.JsonQuery<Address>(x.StringColumn, "$.address"));
61-
62-
var address = q.ConvertTo<Address>();
63-
64-
Assert.That(address, Is.EqualTo(obj.Address));
65-
}
66-
}
67-
68-
internal class Address
69-
{
70-
public string Line1 { get; set; }
71-
public string Line2 { get; set; }
72-
public string City { get; set; }
73-
public string State { get; set; }
74-
}
75-
}
13+
public class JsonExpressionsTest : OrmLiteTestBase
14+
{
15+
[OneTimeSetUp]
16+
public override void TestFixtureSetUp()
17+
{
18+
LogManager.LogFactory = new ConsoleLogFactory();
19+
20+
OrmLiteConfig.DialectProvider = SqlServer2016Dialect.Provider;
21+
OrmLiteConfig.DialectProvider.RegisterConverter<Address>(new SqlServerJsonToObjectConverter());
22+
23+
ConnectionString = ConfigurationManager.ConnectionStrings["testDb"].ConnectionString;
24+
}
25+
26+
27+
[Test]
28+
public void Can_test_if_string_field_contains_json()
29+
{
30+
using (var db = OpenDbConnection())
31+
{
32+
db.DropAndCreateTable<TestType>();
33+
34+
// test if string field is not JSON with Sql.IsJson
35+
db.Insert(new TestType { Id = 1, StringColumn = "not json" });
36+
37+
var j = db.From<TestType>()
38+
.Select(x => Sql.IsJson(x.StringColumn))
39+
.Where(x => x.Id == 1);
40+
var isJson = db.Scalar<bool>(j);
41+
42+
Assert.IsFalse(isJson);
43+
44+
// test if string field is JSON with Sql.IsJson
45+
var expected = new Address
46+
{
47+
Line1 = "1234 Main Street",
48+
Line2 = "Apt. 404",
49+
City = "Las Vegas",
50+
State = "NV"
51+
};
52+
var obj = new { Address = expected };
53+
54+
//{ "Address": { "Line1": "1234 Main Street", "Line2": "Apt. 404", "City": "Las Vegas", "State": "NV" } }
55+
var stringValue = obj.ToJson();
56+
57+
db.Insert(new TestType { Id = 2, StringColumn = stringValue });
58+
59+
j = db.From<TestType>()
60+
.Select(x => Sql.IsJson(x.StringColumn))
61+
.Where(x => x.Id == 2);
62+
isJson = db.Scalar<bool>(j);
63+
64+
Assert.IsTrue(isJson);
65+
}
66+
}
67+
68+
[Test]
69+
public void Can_select_using_a_json_scalar_filter()
70+
{
71+
using (var db = OpenDbConnection())
72+
{
73+
db.DropAndCreateTable<TestType>();
74+
75+
var obj = new
76+
{
77+
Address = new Address
78+
{
79+
Line1 = "1234 Main Street",
80+
Line2 = "Apt. 404",
81+
City = "Las Vegas",
82+
State = "NV"
83+
}
84+
};
85+
86+
//{ "Address": { "Line1": "1234 Main Street", "Line2": "Apt. 404", "City": "Las Vegas", "State": "NV" } }
87+
var stringValue = obj.ToJson();
88+
89+
db.Insert(new TestType { StringColumn = stringValue });
90+
91+
// retrieve records where City in Address is NV (1 record)
92+
var actual = db.Select<TestType>(q =>
93+
Sql.JsonValue(q.StringColumn, "$.Address.State") == "NV");
94+
95+
Assert.IsNotEmpty(actual);
96+
97+
// retrieve records where City in Address is FL (0 records)
98+
actual = db.Select<TestType>(q =>
99+
Sql.JsonValue(q.StringColumn, "$.Address.State") == "FL");
100+
101+
Assert.IsEmpty(actual);
102+
}
103+
}
104+
105+
[Test]
106+
public void Can_select_a_json_scalar_value()
107+
{
108+
using (var db = OpenDbConnection())
109+
{
110+
db.DropAndCreateTable<TestType>();
111+
112+
var obj = new
113+
{
114+
Address = new Address
115+
{
116+
Line1 = "1234 Main Street",
117+
Line2 = "Apt. 404",
118+
City = "Las Vegas",
119+
State = "NV"
120+
}
121+
};
122+
123+
//{ "Address": { "Line1": "1234 Main Street", "Line2": "Apt. 404", "City": "Las Vegas", "State": "NV" } }
124+
var stringValue = obj.ToJson();
125+
126+
db.Insert(new TestType { StringColumn = stringValue });
127+
128+
// retrieve only the State in a field that contains a JSON Address
129+
var state = db.Scalar<string>(
130+
db.From<TestType>().Select(q =>
131+
Sql.As(Sql.JsonValue(q.StringColumn, "$.Address.State"), "State")
132+
)
133+
);
134+
135+
Assert.AreEqual(state, obj.Address.State);
136+
}
137+
}
138+
139+
[Test]
140+
public void Can_select_a_json_object_value()
141+
{
142+
using (var db = OpenDbConnection())
143+
{
144+
db.DropAndCreateTable<TestType>();
145+
146+
var expected = new Address
147+
{
148+
Line1 = "1234 Main Street",
149+
Line2 = "Apt. 404",
150+
City = "Las Vegas",
151+
State = "NV"
152+
};
153+
154+
//{ "Line1": "1234 Main Street", "Line2": "Apt. 404", "City": "Las Vegas", "State": "NV" }
155+
var stringValue = expected.ToJson();
156+
157+
db.Insert(new TestType { StringColumn = stringValue });
158+
159+
// demo how to retrieve inserted JSON string directly to an object
160+
var address = db.Scalar<Address>(
161+
db.From<TestType>().Select(q => q.StringColumn)
162+
);
163+
164+
var sql = db.GetLastSql();
165+
166+
Assert.That(expected.Line1, Is.EqualTo(address.Line1));
167+
Assert.That(expected.Line2, Is.EqualTo(address.Line2));
168+
Assert.That(expected.City, Is.EqualTo(address.City));
169+
Assert.That(expected.State, Is.EqualTo(address.State));
170+
}
171+
}
172+
173+
[Ignore("Not functioning properly, issue with converter")]
174+
[Test]
175+
public void Can_insert_an_object_directly_to_json()
176+
{
177+
using (var db = OpenDbConnection())
178+
{
179+
db.DropAndCreateTable<TestType>();
180+
181+
var expected = new Address
182+
{
183+
Line1 = "1234 Main Street",
184+
Line2 = "Apt. 404",
185+
City = "Las Vegas",
186+
State = "NV"
187+
};
188+
189+
var tableName = db.GetDialectProvider().GetQuotedTableName(ModelDefinition<TestType>.Definition);
190+
191+
var sql = $"INSERT {tableName} (StringColumn) VALUES (@StringColumn);";
192+
db.ExecuteSql(sql, new { StringColumn = expected });
193+
194+
// demo how to retrieve inserted JSON string directly to an object
195+
var address = db.Scalar<Address>(
196+
db.From<TestType>().Select(q => q.StringColumn)
197+
);
198+
199+
Assert.That(expected.Line1, Is.EqualTo(address.Line1));
200+
Assert.That(expected.Line2, Is.EqualTo(address.Line2));
201+
Assert.That(expected.City, Is.EqualTo(address.City));
202+
Assert.That(expected.State, Is.EqualTo(address.State));
203+
}
204+
205+
}
206+
207+
public class Address : ISqlJson
208+
{
209+
public string Line1 { get; set; }
210+
public string Line2 { get; set; }
211+
public string City { get; set; }
212+
public string State { get; set; }
213+
}
214+
}
76215
}

0 commit comments

Comments
 (0)