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

Commit 56cf3da

Browse files
authored
Merge pull request #562 from KevinHoward/master
[OrmLite.SqlServer] Added COLLATE Column option, Memory-Optimized Table Option, & FileTable Table Option with Unit Tests
2 parents 011d011 + 8f53368 commit 56cf3da

9 files changed

+604
-227
lines changed

src/ServiceStack.OrmLite.SqlServer/SqlServer2012OrmLiteDialectProvider.cs

Lines changed: 162 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System.Data;
1+
using System;
2+
using System.Data;
23
using System.Text;
4+
using ServiceStack.DataAnnotations;
35
using ServiceStack.Text;
46

57
namespace ServiceStack.OrmLite.SqlServer
@@ -42,14 +44,129 @@ public override string ToSelectStatement(ModelDefinition modelDef,
4244
return StringBuilderCache.ReturnAndFree(sb);
4345
}
4446

45-
public override void AppendFieldCondition(StringBuilder sqlFilter, FieldDefinition fieldDef, IDbCommand cmd)
47+
public override string GetColumnDefinition(FieldDefinition fieldDef)
4648
{
47-
if (!isSpatialField(fieldDef))
49+
// https://msdn.microsoft.com/en-us/library/ms182776.aspx
50+
if (fieldDef.IsRowVersion)
51+
return $"{fieldDef.FieldName} rowversion NOT NULL";
52+
53+
var fieldDefinition = fieldDef.CustomFieldDefinition ??
54+
GetColumnTypeDefinition(fieldDef.ColumnType, fieldDef.FieldLength, fieldDef.Scale);
55+
56+
var sql = StringBuilderCache.Allocate();
57+
sql.Append($"{GetQuotedColumnName(fieldDef.FieldName)} {fieldDefinition}");
58+
59+
if (fieldDef.FieldType == typeof(string))
4860
{
49-
base.AppendFieldCondition(sqlFilter, fieldDef, cmd);
61+
// https://msdn.microsoft.com/en-us/library/ms184391.aspx
62+
var collation = fieldDef.PropertyInfo.FirstAttribute<SqlServerCollateAttribute>()?.Collation;
63+
if (!string.IsNullOrEmpty(collation))
64+
{
65+
sql.Append($" COLLATE {collation}");
66+
}
5067
}
51-
else
68+
69+
if (fieldDef.IsPrimaryKey)
70+
{
71+
sql.Append(" PRIMARY KEY");
72+
if (fieldDef.AutoIncrement)
73+
{
74+
sql.Append(" ").Append(AutoIncrementDefinition);
75+
}
76+
}
77+
else
78+
{
79+
sql.Append(fieldDef.IsNullable ? " NULL" : " NOT NULL");
80+
}
81+
82+
var defaultValue = GetDefaultValue(fieldDef);
83+
if (!string.IsNullOrEmpty(defaultValue))
84+
{
85+
sql.AppendFormat(DefaultValueFormat, defaultValue);
86+
}
87+
88+
return StringBuilderCache.ReturnAndFree(sql);
89+
}
90+
91+
public override string ToCreateTableStatement(Type tableType)
92+
{
93+
var sbColumns = StringBuilderCache.Allocate();
94+
var sbConstraints = StringBuilderCacheAlt.Allocate();
95+
var sbTableOptions = StringBuilderCacheAlt.Allocate();
96+
97+
var fileTableAttrib = tableType.FirstAttribute<SqlServerFileTableAttribute>();
98+
99+
var modelDef = GetModel(tableType);
100+
101+
if (fileTableAttrib == null)
102+
{
103+
foreach (var fieldDef in modelDef.FieldDefinitions)
104+
{
105+
if (fieldDef.CustomSelect != null)
106+
continue;
107+
108+
var columnDefinition = GetColumnDefinition(fieldDef);
109+
110+
if (columnDefinition == null)
111+
continue;
112+
113+
if (sbColumns.Length != 0)
114+
sbColumns.Append(", \n ");
115+
116+
sbColumns.Append(columnDefinition);
117+
118+
if (fieldDef.ForeignKey == null || OrmLiteConfig.SkipForeignKeys)
119+
continue;
120+
121+
var refModelDef = GetModel(fieldDef.ForeignKey.ReferenceType);
122+
sbConstraints.Append(
123+
$", \n\n CONSTRAINT {GetQuotedName(fieldDef.ForeignKey.GetForeignKeyName(modelDef, refModelDef, NamingStrategy, fieldDef))} " +
124+
$"FOREIGN KEY ({GetQuotedColumnName(fieldDef.FieldName)}) " +
125+
$"REFERENCES {GetQuotedTableName(refModelDef)} ({GetQuotedColumnName(refModelDef.PrimaryKey.FieldName)})");
126+
127+
sbConstraints.Append(GetForeignKeyOnDeleteClause(fieldDef.ForeignKey));
128+
sbConstraints.Append(GetForeignKeyOnUpdateClause(fieldDef.ForeignKey));
129+
}
130+
}
131+
else
52132
{
133+
if (fileTableAttrib.FileTableDirectory != null || fileTableAttrib.FileTableCollateFileName != null)
134+
{
135+
sbTableOptions.Append(" WITH (");
136+
137+
if (fileTableAttrib.FileTableDirectory != null)
138+
{
139+
sbTableOptions.Append($" FILETABLE_DIRECTORY = N'{fileTableAttrib.FileTableDirectory}'\n");
140+
}
141+
142+
if (fileTableAttrib.FileTableCollateFileName != null)
143+
{
144+
if (fileTableAttrib.FileTableDirectory != null)
145+
sbTableOptions.Append(" ,");
146+
147+
sbTableOptions.Append($" FILETABLE_COLLATE_FILENAME = {fileTableAttrib.FileTableCollateFileName ?? "database_default" }\n");
148+
}
149+
sbTableOptions.Append(")");
150+
}
151+
}
152+
153+
var sql = $"CREATE TABLE {GetQuotedTableName(modelDef)} ";
154+
sql += (fileTableAttrib != null)
155+
? $"\n AS FILETABLE{StringBuilderCache.ReturnAndFree(sbTableOptions)};"
156+
: $"\n(\n {StringBuilderCache.ReturnAndFree(sbColumns)}{StringBuilderCacheAlt.ReturnAndFree(sbConstraints)} \n){StringBuilderCache.ReturnAndFree(sbTableOptions)}; \n";
157+
158+
return sql;
159+
}
160+
161+
public override void AppendFieldCondition(StringBuilder sqlFilter, FieldDefinition fieldDef, IDbCommand cmd)
162+
{
163+
if (isSpatialField(fieldDef))
164+
{
165+
// Append condition statement to determine if SqlGeometry or SqlGeography type is Equal
166+
// using the type's STEquals method
167+
//
168+
// SqlGeometry: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeometry.stequals.aspx
169+
// SqlGeography: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeography.stequals.aspx
53170
sqlFilter
54171
.Append(GetQuotedColumnName(fieldDef.FieldName))
55172
.Append(".STEquals(")
@@ -58,23 +175,57 @@ public override void AppendFieldCondition(StringBuilder sqlFilter, FieldDefiniti
58175

59176
AddParameter(cmd, fieldDef);
60177
}
178+
else
179+
{
180+
base.AppendFieldCondition(sqlFilter, fieldDef, cmd);
181+
}
61182
}
62183

63184
public override void AppendNullFieldCondition(StringBuilder sqlFilter, FieldDefinition fieldDef)
64185
{
65-
if (!isSpatialField(fieldDef))
66-
{
67-
base.AppendNullFieldCondition(sqlFilter, fieldDef);
68-
}
69-
else
186+
if (hasIsNullProperty(fieldDef))
70187
{
188+
// Append condition statement to determine if SqlHierarchyId, SqlGeometry, or SqlGeography type is NULL
189+
// using the type's IsNull property
190+
//
191+
// SqlHierarchyId: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlhierarchyid.isnull.aspx
192+
// SqlGeometry: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeometry.isnull.aspx
193+
// SqlGeography: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeography.isnull.aspx
71194
sqlFilter
72195
.Append(GetQuotedColumnName(fieldDef.FieldName))
73196
.Append(".IsNull = 1");
74197
}
198+
else
199+
{
200+
base.AppendNullFieldCondition(sqlFilter, fieldDef);
201+
}
75202
}
76203

77-
private bool isSpatialField(FieldDefinition fieldDef) =>
204+
internal bool isSpatialField(FieldDefinition fieldDef) =>
78205
fieldDef.FieldType.Name == "SqlGeography" || fieldDef.FieldType.Name == "SqlGeometry";
206+
207+
internal bool hasIsNullProperty(FieldDefinition fieldDef) =>
208+
isSpatialField(fieldDef) || fieldDef.FieldType.Name == "SqlHierarchyId";
209+
}
210+
}
211+
212+
213+
// TODO: Move to ServiceStack.Interfaces
214+
namespace ServiceStack.DataAnnotations
215+
{
216+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
217+
public class SqlServerFileTableAttribute : AttributeBase
218+
{
219+
public SqlServerFileTableAttribute() { }
220+
221+
public SqlServerFileTableAttribute(string directory, string collateFileName = null)
222+
{
223+
FileTableDirectory = directory;
224+
FileTableCollateFileName = collateFileName;
225+
}
226+
227+
public string FileTableDirectory { get; internal set; }
228+
229+
public string FileTableCollateFileName { get; internal set; }
79230
}
80231
}

0 commit comments

Comments
 (0)