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

Commit fbb384e

Browse files
authored
Merge pull request #544 from KevinHoward/master
Added MemoryOptimized attribute to SqlServer provider
2 parents e9bd802 + ac8cd58 commit fbb384e

File tree

7 files changed

+222
-36
lines changed

7 files changed

+222
-36
lines changed

src/ServiceStack.OrmLite.SqlServer/ServiceStack.OrmLite.SqlServer.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@
102102
<Compile Include="Converters\SqlServerSpecialConverters.cs" />
103103
<Compile Include="Converters\SqlServerStringConverters.cs" />
104104
<Compile Include="Converters\SqlServerTimeConverter.cs" />
105+
<Compile Include="SqlServer2016OrmLiteDialectProvider.cs" />
106+
<Compile Include="SqlServer2014OrmLiteDialectProvider.cs" />
105107
<Compile Include="SqlServer2012OrmLiteDialectProvider.cs" />
106108
<Compile Include="SqlServerDialect.cs" />
107109
<Compile Include="SqlServerExpression.cs" />
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using System;
2+
using System.Data;
3+
using System.Text;
4+
using ServiceStack.Text;
5+
using ServiceStack.DataAnnotations;
6+
7+
namespace ServiceStack.OrmLite.SqlServer
8+
{
9+
public class SqlServer2014OrmLiteDialectProvider : SqlServer2012OrmLiteDialectProvider
10+
{
11+
public static new SqlServer2014OrmLiteDialectProvider Instance = new SqlServer2014OrmLiteDialectProvider();
12+
13+
public override string ToCreateTableStatement(Type tableType)
14+
{
15+
var sbColumns = StringBuilderCache.Allocate();
16+
var sbConstraints = StringBuilderCacheAlt.Allocate();
17+
var sbMemOptimized = StringBuilderCacheAlt.Allocate();
18+
19+
var modelDef = OrmLiteUtils.GetModelDefinition(tableType);
20+
foreach (var fieldDef in modelDef.FieldDefinitions)
21+
{
22+
if (fieldDef.CustomSelect != null)
23+
continue;
24+
25+
var columnDefinition = GetColumnDefinition(
26+
fieldDef.FieldName,
27+
fieldDef.ColumnType,
28+
fieldDef.IsPrimaryKey,
29+
fieldDef.AutoIncrement,
30+
fieldDef.IsNullable,
31+
fieldDef.IsRowVersion,
32+
fieldDef.FieldLength,
33+
fieldDef.Scale,
34+
GetDefaultValue(fieldDef),
35+
fieldDef.CustomFieldDefinition);
36+
37+
if (columnDefinition == null)
38+
continue;
39+
40+
if (sbColumns.Length != 0)
41+
sbColumns.Append(", \n ");
42+
43+
sbColumns.Append(columnDefinition);
44+
45+
if (fieldDef.ForeignKey == null) continue;
46+
47+
var refModelDef = OrmLiteUtils.GetModelDefinition(fieldDef.ForeignKey.ReferenceType);
48+
sbConstraints.Append(
49+
$", \n\n CONSTRAINT {GetQuotedName(fieldDef.ForeignKey.GetForeignKeyName(modelDef, refModelDef, NamingStrategy, fieldDef))} " +
50+
$"FOREIGN KEY ({GetQuotedColumnName(fieldDef.FieldName)}) " +
51+
$"REFERENCES {GetQuotedTableName(refModelDef)} ({GetQuotedColumnName(refModelDef.PrimaryKey.FieldName)})");
52+
53+
sbConstraints.Append(GetForeignKeyOnDeleteClause(fieldDef.ForeignKey));
54+
sbConstraints.Append(GetForeignKeyOnUpdateClause(fieldDef.ForeignKey));
55+
}
56+
57+
if (tableType.HasAttribute<MemoryOptimizedAttribute>())
58+
{
59+
sbMemOptimized.Append(" WITH (MEMORY_OPTIMIZED=ON");
60+
var attrib = tableType.FirstAttribute<MemoryOptimizedAttribute>();
61+
if (attrib.Durability == TableDurability.SCHEMA_ONLY)
62+
sbMemOptimized.Append(", DURABILITY=SCHEMA_ONLY");
63+
else if (attrib.Durability == TableDurability.SCHEMA_AND_DATA)
64+
sbMemOptimized.Append(", DURABILITY=SCHEMA_AND_DATA");
65+
sbMemOptimized.Append(")");
66+
}
67+
68+
var sql = $"CREATE TABLE {GetQuotedTableName(modelDef)} " +
69+
$"\n(\n {StringBuilderCache.ReturnAndFree(sbColumns)}{StringBuilderCacheAlt.ReturnAndFree(sbConstraints)} \n){StringBuilderCache.ReturnAndFree(sbMemOptimized)}; \n";
70+
71+
return sql;
72+
}
73+
}
74+
}
75+
76+
namespace ServiceStack.DataAnnotations
77+
{
78+
// SQL 2014: https://msdn.microsoft.com/en-us/library/dn553122(v=sql.120).aspx
79+
// SQL 2016: https://msdn.microsoft.com/en-us/library/dn553122(v=sql.130).aspx
80+
public class MemoryOptimizedAttribute : Attribute
81+
{
82+
public MemoryOptimizedAttribute() { }
83+
84+
public MemoryOptimizedAttribute(TableDurability durability) { Durability = durability; }
85+
86+
public TableDurability? Durability { get; set; }
87+
}
88+
89+
public enum TableDurability
90+
{
91+
SCHEMA_ONLY, // (non-durable table) recreated upon server restart, data is lost, no transaction logging and checkpoints
92+
SCHEMA_AND_DATA // (durable table) data persists upon server restart
93+
}
94+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Data;
3+
using System.Text;
4+
using ServiceStack.Text;
5+
6+
namespace ServiceStack.OrmLite.SqlServer
7+
{
8+
public class SqlServer2016OrmLiteDialectProvider : SqlServer2014OrmLiteDialectProvider
9+
{
10+
public static new SqlServer2016OrmLiteDialectProvider Instance = new SqlServer2016OrmLiteDialectProvider();
11+
}
12+
}

src/ServiceStack.OrmLite.SqlServer/SqlServerDialect.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,14 @@ public static class SqlServer2012Dialect
1111
{
1212
public static IOrmLiteDialectProvider Provider => SqlServer2012OrmLiteDialectProvider.Instance;
1313
}
14+
15+
public static class SqlServer2014Dialect
16+
{
17+
public static IOrmLiteDialectProvider Provider => SqlServer2014OrmLiteDialectProvider.Instance;
18+
}
19+
20+
public static class SqlServer2016Dialect
21+
{
22+
public static IOrmLiteDialectProvider Provider => SqlServer2016OrmLiteDialectProvider.Instance;
23+
}
1424
}

src/ServiceStack.OrmLite.SqlServer/SqlServerOrmLiteDialectProvider.cs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,14 @@ public override IDbConnection CreateConnection(string connectionString, Dictiona
6565
{
6666
var filePath = connectionString;
6767

68-
var filePathWithExt = filePath.ToLower().EndsWith(".mdf")
68+
var filePathWithExt = filePath.EndsWithIgnoreCase(".mdf")
6969
? filePath
7070
: filePath + ".mdf";
7171

7272
var fileName = Path.GetFileName(filePathWithExt);
7373
var dbName = fileName.Substring(0, fileName.Length - ".mdf".Length);
7474

75-
connectionString = string.Format(
76-
@"Data Source=.\SQLEXPRESS;AttachDbFilename={0};Initial Catalog={1};Integrated Security=True;User Instance=True;",
77-
filePathWithExt, dbName);
75+
connectionString = $@"Data Source=.\SQLEXPRESS;AttachDbFilename={filePathWithExt};Initial Catalog={dbName};Integrated Security=True;User Instance=True;";
7876
}
7977

8078
if (options != null)
@@ -155,7 +153,7 @@ public override string GetForeignKeyOnDeleteClause(ForeignKeyConstraint foreignK
155153

156154
public override string GetForeignKeyOnUpdateClause(ForeignKeyConstraint foreignKey)
157155
{
158-
return "RESTRICT" == (foreignKey.OnDelete ?? "").ToUpper()
156+
return "RESTRICT" == (foreignKey.OnUpdate ?? "").ToUpper()
159157
? ""
160158
: base.GetForeignKeyOnUpdateClause(foreignKey);
161159
}
@@ -176,9 +174,9 @@ public override string GetDropForeignKeyConstraints(ModelDefinition modelDef)
176174
fieldDef);
177175

178176
var tableName = GetQuotedTableName(modelDef);
179-
sb.AppendLine("IF EXISTS (SELECT name FROM sys.foreign_keys WHERE name = '{0}')".Fmt(foreignKeyName));
177+
sb.AppendLine($"IF EXISTS (SELECT name FROM sys.foreign_keys WHERE name = '{foreignKeyName}')");
180178
sb.AppendLine("BEGIN");
181-
sb.AppendLine(" ALTER TABLE {0} DROP {1};".Fmt(tableName, foreignKeyName));
179+
sb.AppendLine($" ALTER TABLE {tableName} DROP {foreignKeyName};");
182180
sb.AppendLine("END");
183181
}
184182
}
@@ -199,9 +197,9 @@ public override string ToAddColumnStatement(Type modelType, FieldDefinition fiel
199197
fieldDef.DefaultValue,
200198
fieldDef.CustomFieldDefinition);
201199

202-
return string.Format("ALTER TABLE {0} ADD {1};",
203-
GetQuotedTableName(GetModel(modelType).ModelName),
204-
column);
200+
var modelName = GetQuotedTableName(GetModel(modelType).ModelName);
201+
202+
return $"ALTER TABLE {modelName} ADD {column};";
205203
}
206204

207205
public override string ToAlterColumnStatement(Type modelType, FieldDefinition fieldDef)
@@ -217,28 +215,25 @@ public override string ToAlterColumnStatement(Type modelType, FieldDefinition fi
217215
fieldDef.DefaultValue,
218216
fieldDef.CustomFieldDefinition);
219217

220-
return string.Format("ALTER TABLE {0} ALTER COLUMN {1};",
221-
GetQuotedTableName(GetModel(modelType).ModelName),
222-
column);
218+
var modelName = GetQuotedTableName(GetModel(modelType).ModelName);
219+
220+
return $"ALTER TABLE {modelName} ALTER COLUMN {column};";
223221
}
224222

225223
public override string ToChangeColumnNameStatement(Type modelType, FieldDefinition fieldDef, string oldColumnName)
226224
{
227-
var objectName = string.Format("{0}.{1}",
228-
NamingStrategy.GetTableName(GetModel(modelType).ModelName),
229-
oldColumnName);
230-
231-
return string.Format("EXEC sp_rename {0}, {1}, {2};",
232-
GetQuotedValue(objectName),
233-
GetQuotedValue(fieldDef.FieldName),
234-
GetQuotedValue("COLUMN"));
225+
var modelName = NamingStrategy.GetTableName(GetModel(modelType).ModelName);
226+
227+
var objectName = $"{modelName}.{oldColumnName}";
228+
229+
return $"EXEC sp_rename {GetQuotedValue(objectName)}, {GetQuotedValue(fieldDef.FieldName)}, {GetQuotedValue("COLUMN")};";
235230
}
236231

237232
public override string GetColumnDefinition(string fieldName, Type fieldType, bool isPrimaryKey, bool autoIncrement,
238233
bool isNullable, bool isRowVersion, int? fieldLength, int? scale, string defaultValue, string customFieldDefinition)
239234
{
240235
if (isRowVersion)
241-
return "{0} rowversion NOT NULL".Fmt(fieldName);
236+
return $"{fieldName} rowversion NOT NULL";
242237

243238
var definition = base.GetColumnDefinition(fieldName, fieldType, isPrimaryKey, autoIncrement,
244239
isNullable, isRowVersion, fieldLength, scale, defaultValue, customFieldDefinition);
@@ -261,10 +256,10 @@ public override string ToSelectStatement(ModelDefinition modelDef,
261256
return StringBuilderCache.ReturnAndFree(sb) + orderByExpression;
262257

263258
if (offset.HasValue && offset.Value < 0)
264-
throw new ArgumentException(string.Format("Skip value:'{0}' must be>=0", offset.Value));
259+
throw new ArgumentException($"Skip value:'{offset.Value}' must be>=0");
265260

266261
if (rows.HasValue && rows.Value < 0)
267-
throw new ArgumentException(string.Format("Rows value:'{0}' must be>=0", rows.Value));
262+
throw new ArgumentException($"Rows value:'{rows.Value}' must be>=0");
268263

269264
var skip = offset.HasValue ? offset.Value : 0;
270265
var take = rows.HasValue ? rows.Value : int.MaxValue;
@@ -279,9 +274,10 @@ public override string ToSelectStatement(ModelDefinition modelDef,
279274
if (take == int.MaxValue)
280275
return sql;
281276

282-
if (sql.Length < "SELECT".Length) return sql;
283-
sql = selectType + " TOP " + take + sql.Substring(selectType.Length);
284-
return sql;
277+
if (sql.Length < "SELECT".Length)
278+
return sql;
279+
280+
return $"{selectType} TOP {take + sql.Substring(selectType.Length)}";
285281
}
286282

287283
// Required because ordering is done by Windowing function
@@ -290,17 +286,12 @@ public override string ToSelectStatement(ModelDefinition modelDef,
290286
if (modelDef.PrimaryKey == null)
291287
throw new ApplicationException("Malformed model, no PrimaryKey defined");
292288

293-
orderByExpression = string.Format("ORDER BY {0}",
294-
this.GetQuotedColumnName(modelDef, modelDef.PrimaryKey));
289+
orderByExpression = $"ORDER BY {this.GetQuotedColumnName(modelDef, modelDef.PrimaryKey)}";
295290
}
296291

297-
var ret = string.Format(
298-
"SELECT * FROM (SELECT {0}, ROW_NUMBER() OVER ({1}) As RowNum {2}) AS RowConstrainedResult WHERE RowNum > {3} AND RowNum <= {4}",
299-
selectExpression.Substring(selectType.Length),
300-
orderByExpression,
301-
bodyExpression,
302-
skip,
303-
take == int.MaxValue ? take : skip + take);
292+
var row = take == int.MaxValue ? take : skip + take;
293+
294+
var ret = $"SELECT * FROM (SELECT {selectExpression.Substring(selectType.Length)}, ROW_NUMBER() OVER ({orderByExpression}) As RowNum {bodyExpression}) AS RowConstrainedResult WHERE RowNum > {skip} AND RowNum <= {row}";
304295

305296
return ret;
306297
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using NUnit.Framework;
2+
using ServiceStack.DataAnnotations;
3+
4+
namespace ServiceStack.OrmLite.SqlServerTests
5+
{
6+
[TestFixture]
7+
public class MemoryOptimizedAttributeTests : OrmLiteTestBase
8+
{
9+
[TestFixtureSetUp]
10+
public void Setup()
11+
{
12+
OrmLiteConfig.DialectProvider = SqlServer2014Dialect.Provider;
13+
14+
using (var dbConn = OpenDbConnection())
15+
{
16+
dbConn.DropTable<TypeWithMemTableNoDurability>();
17+
dbConn.DropTable<TypeWithMemTableSchemaOnlyDurability>();
18+
dbConn.DropTable<TypeWithMemTableSchemaAndDataDurability>();
19+
}
20+
}
21+
22+
[Test]
23+
public void CanCreateMemoryOptimizedTable()
24+
{
25+
using (var dbConn = OpenDbConnection())
26+
{
27+
dbConn.CreateTable<TypeWithMemTableNoDurability>(true);
28+
}
29+
}
30+
31+
[Test]
32+
public void CanCreateMemoryOptimizedTableWithSchemaOnlyDurability()
33+
{
34+
using (var dbConn = OpenDbConnection())
35+
{
36+
dbConn.CreateTable<TypeWithMemTableSchemaOnlyDurability>(true);
37+
}
38+
}
39+
40+
[Test]
41+
public void CanCreateMemoryOptimizedTableWithSchemaAndDurability()
42+
{
43+
using (var dbConn = OpenDbConnection())
44+
{
45+
dbConn.CreateTable<TypeWithMemTableSchemaAndDataDurability>(true);
46+
}
47+
}
48+
}
49+
50+
[MemoryOptimized]
51+
public class TypeWithMemTableNoDurability
52+
{
53+
[AutoIncrement]
54+
public int Id { get; set; }
55+
56+
public string Name { get; set; }
57+
}
58+
59+
[MemoryOptimized(TableDurability.SCHEMA_ONLY)]
60+
public class TypeWithMemTableSchemaOnlyDurability
61+
{
62+
[AutoIncrement]
63+
public int Id { get; set; }
64+
65+
public string Name { get; set; }
66+
}
67+
68+
[MemoryOptimized(TableDurability.SCHEMA_AND_DATA)]
69+
public class TypeWithMemTableSchemaAndDataDurability
70+
{
71+
[AutoIncrement]
72+
public int Id { get; set; }
73+
74+
public string Name { get; set; }
75+
}
76+
}

src/ServiceStack.OrmLite.SqlServerTests/ServiceStack.OrmLite.SqlServerTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
<Compile Include="Issues\DeleteWithGeoTypesIssue.cs" />
9191
<Compile Include="Issues\JamesGeoIssue.cs" />
9292
<Compile Include="Issues\SerializationTests.cs" />
93+
<Compile Include="MemoryOptimizedAttributeTests.cs" />
9394
<Compile Include="NestedTransactions.cs" />
9495
<Compile Include="EnumTests.cs" />
9596
<Compile Include="Expressions\AdditiveExpressionsTest.cs" />

0 commit comments

Comments
 (0)