diff --git a/src/FluentCommand.SqlServer/Merge/DataMergeColumn.cs b/src/FluentCommand.SqlServer/Merge/DataMergeColumn.cs
index fefa38b8..57a16470 100644
--- a/src/FluentCommand.SqlServer/Merge/DataMergeColumn.cs
+++ b/src/FluentCommand.SqlServer/Merge/DataMergeColumn.cs
@@ -72,10 +72,21 @@ public DataMergeColumn()
public bool IsKey { get; set; }
///
- /// Gets or sets a value indicating whether the column is ignored, not used by merge.
+ /// Gets or sets a value indicating whether the column is ignored, not used by merge.
///
///
/// true if the column is ignored; otherwise, false.
///
public bool IsIgnored { get; set; }
+
+ ///
+ /// Converts to string.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return $"Source: {SourceColumn}, Target: {TargetColumn}, NativeType: {NativeType}, Key: {IsKey}, Ignored: {IsIgnored}";
+ }
}
diff --git a/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs b/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs
index aced189c..e65aa7c4 100644
--- a/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs
+++ b/src/FluentCommand.SqlServer/Merge/DataMergeDefinition.cs
@@ -1,8 +1,5 @@
-using System.ComponentModel;
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-
using FluentCommand.Extensions;
+using FluentCommand.Reflection;
namespace FluentCommand.Merge;
@@ -116,64 +113,29 @@ public static DataMergeDefinition Create()
/// The merge definition up auto map to.
public static void AutoMap(DataMergeDefinition mergeDefinition)
{
- var entityType = typeof(TEntity);
- var properties = TypeDescriptor.GetProperties(entityType);
-
-
- var tableAttribute = Attribute.GetCustomAttribute(entityType, typeof(TableAttribute)) as TableAttribute;
- if (tableAttribute != null)
- {
- string targetTable = tableAttribute.Name;
- if (!string.IsNullOrEmpty(tableAttribute.Schema))
- targetTable = tableAttribute.Schema + "." + targetTable;
-
- mergeDefinition.TargetTable = targetTable;
- }
+ var typeAccessor = TypeAccessor.GetAccessor();
- if (string.IsNullOrEmpty(mergeDefinition.TargetTable))
- mergeDefinition.TargetTable = entityType.Name;
+ // don't overwrite existing
+ if (mergeDefinition.TargetTable.IsNullOrEmpty())
+ mergeDefinition.TargetTable = typeAccessor.TableSchema.HasValue() ? $"{typeAccessor.TableSchema}.{typeAccessor.TableName}" : typeAccessor.TableName;
- foreach (PropertyDescriptor p in properties)
+ foreach (var property in typeAccessor.GetProperties())
{
- string sourceColumn = p.Name;
- string targetColumn = sourceColumn;
- string nativeType = SqlTypeMapping.NativeType(p.PropertyType);
-
- var columnAttribute = p.Attributes
- .OfType()
- .FirstOrDefault();
-
- if (columnAttribute != null)
- {
- if (columnAttribute.Name.HasValue())
- targetColumn = columnAttribute.Name;
- if (columnAttribute.TypeName.HasValue())
- nativeType = columnAttribute.TypeName;
- }
+ string sourceColumn = property.Name;
+ string targetColumn = property.Column;
+ string nativeType = property.ColumnType ?? SqlTypeMapping.NativeType(property.MemberType);
+ // find existing map and update
var mergeColumn = mergeDefinition.Columns.FirstOrAdd(
m => m.SourceColumn == sourceColumn,
() => new DataMergeColumn { SourceColumn = sourceColumn });
mergeColumn.TargetColumn = targetColumn;
mergeColumn.NativeType = nativeType;
-
- var keyAttribute = p.Attributes
- .OfType()
- .FirstOrDefault();
-
- if (keyAttribute != null)
- {
- mergeColumn.IsKey = true;
- mergeColumn.CanUpdate = false;
- }
-
- var ignoreAttribute = p.Attributes
- .OfType()
- .FirstOrDefault();
-
- if (ignoreAttribute != null)
- mergeColumn.IsIgnored = true;
+ mergeColumn.IsKey = property.IsKey;
+ mergeColumn.CanUpdate = !property.IsKey && !property.IsDatabaseGenerated && !property.IsConcurrencyCheck;
+ mergeColumn.CanInsert = !property.IsDatabaseGenerated && !property.IsConcurrencyCheck;
+ mergeColumn.IsIgnored = property.IsNotMapped;
}
}
diff --git a/src/FluentCommand.SqlServer/SqlTypeMapping.cs b/src/FluentCommand.SqlServer/SqlTypeMapping.cs
index a280b4a0..070ee4a9 100644
--- a/src/FluentCommand.SqlServer/SqlTypeMapping.cs
+++ b/src/FluentCommand.SqlServer/SqlTypeMapping.cs
@@ -20,7 +20,11 @@ public static class SqlTypeMapping
{typeof(TimeSpan), "time"},
{typeof(DateTime), "datetime2"},
{typeof(DateTimeOffset), "datetimeoffset"},
- {typeof(Guid), "uniqueidentifier"}
+ {typeof(Guid), "uniqueidentifier"},
+ #if NET6_0_OR_GREATER
+ {typeof(DateOnly), "date"},
+ {typeof(TimeOnly), "time"},
+ #endif
};
///
diff --git a/src/FluentCommand/Reflection/IMemberInformation.cs b/src/FluentCommand/Reflection/IMemberInformation.cs
index 48e82baf..d373299b 100644
--- a/src/FluentCommand/Reflection/IMemberInformation.cs
+++ b/src/FluentCommand/Reflection/IMemberInformation.cs
@@ -33,6 +33,22 @@ public interface IMemberInformation
///
string Column { get; }
+ ///
+ /// Gets the database provider specific data type of the column the property is mapped to
+ ///
+ ///
+ /// The database provider specific data type of the column the property is mapped to
+ ///
+ string ColumnType { get; }
+
+ ///
+ /// Gets the zero-based order of the column the property is mapped to
+ ///
+ ///
+ /// The zero-based order of the column the property is mapped to
+ ///
+ int? ColumnOrder { get; }
+
///
/// Gets a value indicating that this property is the unique identify for the entity
///
diff --git a/src/FluentCommand/Reflection/MemberAccessor.cs b/src/FluentCommand/Reflection/MemberAccessor.cs
index e0243db8..2b3b3e54 100644
--- a/src/FluentCommand/Reflection/MemberAccessor.cs
+++ b/src/FluentCommand/Reflection/MemberAccessor.cs
@@ -72,6 +72,22 @@ protected MemberAccessor(MemberInfo memberInfo)
///
public string Column => _columnAttribute.Value?.Name ?? Name;
+ ///
+ /// Gets the database provider specific data type of the column the property is mapped to
+ ///
+ ///
+ /// The database provider specific data type of the column the property is mapped to
+ ///
+ public string ColumnType => _columnAttribute.Value?.TypeName;
+
+ ///
+ /// Gets the zero-based order of the column the property is mapped to
+ ///
+ ///
+ /// The zero-based order of the column the property is mapped to
+ ///
+ public int? ColumnOrder => _columnAttribute.Value?.Order;
+
///
/// Gets a value indicating that this property is the unique identify for the entity
///
@@ -170,7 +186,7 @@ public override bool Equals(object obj)
/// Returns a hash code for this instance.
///
///
- /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
///
public override int GetHashCode()
{
diff --git a/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs b/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs
index 31ed3067..67b78198 100644
--- a/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs
+++ b/test/FluentCommand.SqlServer.Tests/DataMergeGeneratorTests.cs
@@ -126,14 +126,38 @@ public void BuildMergeDataTests()
Output.WriteLine("MergeStatement:");
Output.WriteLine(sql);
}
+
[Fact]
- public void BuildMergeDataTypeTests()
+ public async System.Threading.Tasks.Task BuildTableSqlTest()
{
var definition = new DataMergeDefinition();
DataMergeDefinition.AutoMap(definition);
definition.Columns.Should().NotBeNullOrEmpty();
+ definition.TargetTable = "dbo.DataType";
+
+ var column = definition.Columns.Find(c => c.SourceColumn == "Id");
+ column.Should().NotBeNull();
+ column.IsKey = true;
+ column.CanUpdate = false;
+
+ var tableStatement = DataMergeGenerator.BuildTable(definition);
+ tableStatement.Should().NotBeNull();
+ await Verifier
+ .Verify(tableStatement)
+ .UseDirectory("Snapshots")
+ .AddScrubber(scrubber => scrubber.Replace(definition.TemporaryTable, "#MergeTable"));
+
+ }
+
+ [Fact]
+ public async System.Threading.Tasks.Task BuildMergeDataTypeTests()
+ {
+ var definition = new DataMergeDefinition();
+
+ DataMergeDefinition.AutoMap(definition);
+ definition.Columns.Should().NotBeNullOrEmpty();
definition.TargetTable = "dbo.DataType";
var column = definition.Columns.Find(c => c.SourceColumn == "Id");
@@ -144,8 +168,7 @@ public void BuildMergeDataTypeTests()
var users = new List
{
- new DataType
- {
+ new() {
Id = 1,
Name = "Test1",
Boolean = false,
@@ -154,8 +177,8 @@ public void BuildMergeDataTypeTests()
Float = 200.20F,
Double = 300.35,
Decimal = 456.12M,
- DateTime = DateTime.Now,
- DateTimeOffset = DateTimeOffset.Now,
+ DateTime = new DateTime(2024, 5, 1, 8, 0, 0),
+ DateTimeOffset = new DateTimeOffset(2024, 5, 1, 8, 0, 0, TimeSpan.FromHours(-6)),
Guid = Guid.Empty,
TimeSpan = TimeSpan.FromHours(1),
DateOnly = new DateOnly(2022, 12, 1),
@@ -166,15 +189,14 @@ public void BuildMergeDataTypeTests()
FloatNull = 200.20F,
DoubleNull = 300.35,
DecimalNull = 456.12M,
- DateTimeNull = DateTime.Now,
- DateTimeOffsetNull = DateTimeOffset.Now,
+ DateTimeNull = new DateTime(2024, 4, 1, 8, 0, 0),
+ DateTimeOffsetNull = new DateTimeOffset(2024, 4, 1, 8, 0, 0, TimeSpan.FromHours(-6)),
GuidNull = Guid.Empty,
TimeSpanNull = TimeSpan.FromHours(1),
DateOnlyNull = new DateOnly(2022, 12, 1),
TimeOnlyNull = new TimeOnly(1, 30, 0),
},
- new DataType
- {
+ new() {
Id = 2,
Name = "Test2",
Boolean = true,
@@ -183,8 +205,8 @@ public void BuildMergeDataTypeTests()
Float = 600.20F,
Double = 700.35,
Decimal = 856.12M,
- DateTime = DateTime.Now,
- DateTimeOffset = DateTimeOffset.Now,
+ DateTime = new DateTime(2024, 5, 1, 8, 0, 0),
+ DateTimeOffset = new DateTimeOffset(2024, 5, 1, 8, 0, 0, TimeSpan.FromHours(-6)),
Guid = Guid.Empty,
TimeSpan = TimeSpan.FromHours(2),
DateOnly = new DateOnly(2022, 12, 12),
@@ -194,11 +216,35 @@ public void BuildMergeDataTypeTests()
var listDataReader = new ListDataReader(users);
- var sql = DataMergeGenerator.BuildMerge(definition, listDataReader);
- sql.Should().NotBeNullOrEmpty();
+ var mergeDataStatement = DataMergeGenerator.BuildMerge(definition, listDataReader);
+ mergeDataStatement.Should().NotBeNullOrEmpty();
+ await Verifier
+ .Verify(mergeDataStatement)
+ .UseDirectory("Snapshots")
+ .AddScrubber(scrubber => scrubber.Replace(definition.TemporaryTable, "#MergeTable"));
+ }
- Output.WriteLine("MergeStatement:");
- Output.WriteLine(sql);
+ [Fact]
+ public async System.Threading.Tasks.Task BuildMergeDataTableTests()
+ {
+ var definition = new DataMergeDefinition();
+
+ DataMergeDefinition.AutoMap(definition);
+ definition.Columns.Should().NotBeNullOrEmpty();
+ definition.TargetTable = "dbo.DataType";
+
+ var column = definition.Columns.Find(c => c.SourceColumn == "Id");
+ column.Should().NotBeNull();
+
+ column.IsKey = true;
+ column.CanUpdate = false;
+
+ var mergeStatement = DataMergeGenerator.BuildMerge(definition);
+ mergeStatement.Should().NotBeNull();
+ await Verifier
+ .Verify(mergeStatement)
+ .UseDirectory("Snapshots")
+ .AddScrubber(scrubber => scrubber.Replace(definition.TemporaryTable, "#MergeTable"));
}
[Fact]
diff --git a/test/FluentCommand.SqlServer.Tests/FluentCommand.SqlServer.Tests.csproj b/test/FluentCommand.SqlServer.Tests/FluentCommand.SqlServer.Tests.csproj
index 8c450334..6cb99f93 100644
--- a/test/FluentCommand.SqlServer.Tests/FluentCommand.SqlServer.Tests.csproj
+++ b/test/FluentCommand.SqlServer.Tests/FluentCommand.SqlServer.Tests.csproj
@@ -38,6 +38,7 @@
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTableTests.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTableTests.verified.txt
new file mode 100644
index 00000000..b97fc1f2
--- /dev/null
+++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTableTests.verified.txt
@@ -0,0 +1,124 @@
+MERGE INTO [dbo].[DataType] AS t
+USING
+(
+ SELECT
+ [Id],
+ [Name],
+ [Boolean],
+ [Short],
+ [Long],
+ [Float],
+ [Double],
+ [Decimal],
+ [DateTime],
+ [DateTimeOffset],
+ [Guid],
+ [TimeSpan],
+ [DateOnly],
+ [TimeOnly],
+ [BooleanNull],
+ [ShortNull],
+ [LongNull],
+ [FloatNull],
+ [DoubleNull],
+ [DecimalNull],
+ [DateTimeNull],
+ [DateTimeOffsetNull],
+ [GuidNull],
+ [TimeSpanNull],
+ [DateOnlyNull],
+ [TimeOnlyNull]
+ FROM [#MergeTable]
+)
+AS s
+ON
+(
+ t.[Id] = s.[Id]
+)
+WHEN NOT MATCHED BY TARGET THEN
+ INSERT
+ (
+ [Id],
+ [Name],
+ [Boolean],
+ [Short],
+ [Long],
+ [Float],
+ [Double],
+ [Decimal],
+ [DateTime],
+ [DateTimeOffset],
+ [Guid],
+ [TimeSpan],
+ [DateOnly],
+ [TimeOnly],
+ [BooleanNull],
+ [ShortNull],
+ [LongNull],
+ [FloatNull],
+ [DoubleNull],
+ [DecimalNull],
+ [DateTimeNull],
+ [DateTimeOffsetNull],
+ [GuidNull],
+ [TimeSpanNull],
+ [DateOnlyNull],
+ [TimeOnlyNull]
+ )
+ VALUES
+ (
+ s.[Id],
+ s.[Name],
+ s.[Boolean],
+ s.[Short],
+ s.[Long],
+ s.[Float],
+ s.[Double],
+ s.[Decimal],
+ s.[DateTime],
+ s.[DateTimeOffset],
+ s.[Guid],
+ s.[TimeSpan],
+ s.[DateOnly],
+ s.[TimeOnly],
+ s.[BooleanNull],
+ s.[ShortNull],
+ s.[LongNull],
+ s.[FloatNull],
+ s.[DoubleNull],
+ s.[DecimalNull],
+ s.[DateTimeNull],
+ s.[DateTimeOffsetNull],
+ s.[GuidNull],
+ s.[TimeSpanNull],
+ s.[DateOnlyNull],
+ s.[TimeOnlyNull]
+ )
+WHEN MATCHED THEN
+ UPDATE SET
+ t.[Name] = s.[Name],
+ t.[Boolean] = s.[Boolean],
+ t.[Short] = s.[Short],
+ t.[Long] = s.[Long],
+ t.[Float] = s.[Float],
+ t.[Double] = s.[Double],
+ t.[Decimal] = s.[Decimal],
+ t.[DateTime] = s.[DateTime],
+ t.[DateTimeOffset] = s.[DateTimeOffset],
+ t.[Guid] = s.[Guid],
+ t.[TimeSpan] = s.[TimeSpan],
+ t.[DateOnly] = s.[DateOnly],
+ t.[TimeOnly] = s.[TimeOnly],
+ t.[BooleanNull] = s.[BooleanNull],
+ t.[ShortNull] = s.[ShortNull],
+ t.[LongNull] = s.[LongNull],
+ t.[FloatNull] = s.[FloatNull],
+ t.[DoubleNull] = s.[DoubleNull],
+ t.[DecimalNull] = s.[DecimalNull],
+ t.[DateTimeNull] = s.[DateTimeNull],
+ t.[DateTimeOffsetNull] = s.[DateTimeOffsetNull],
+ t.[GuidNull] = s.[GuidNull],
+ t.[TimeSpanNull] = s.[TimeSpanNull],
+ t.[DateOnlyNull] = s.[DateOnlyNull],
+ t.[TimeOnlyNull] = s.[TimeOnlyNull]
+;
\ No newline at end of file
diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTypeTests.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTypeTests.verified.txt
new file mode 100644
index 00000000..072d2f14
--- /dev/null
+++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildMergeDataTypeTests.verified.txt
@@ -0,0 +1,102 @@
+MERGE INTO [dbo].[DataType] AS t
+USING
+(
+ VALUES
+ (1, 'Test1', 0, 2, 200, 200.2, 300.35, 456.12, '2024-05-01 08:00:00Z', '2024-05-01 14:00:00Z', '00000000-0000-0000-0000-000000000000', '01:00:00', '2022-12-01', '01:30:00.000000', 0, 2, 200, 200.2, 300.35, 456.12, '2024-04-01 08:00:00Z', '2024-04-01 14:00:00Z', '00000000-0000-0000-0000-000000000000', '01:00:00', '2022-12-01', '01:30:00.000000'),
+ (2, 'Test2', 1, 3, 400, 600.2, 700.35, 856.12, '2024-05-01 08:00:00Z', '2024-05-01 14:00:00Z', '00000000-0000-0000-0000-000000000000', '02:00:00', '2022-12-12', '06:30:00.000000', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
+)
+AS s
+(
+ [Id], [Name], [Boolean], [Short], [Long], [Float], [Double], [Decimal], [DateTime], [DateTimeOffset], [Guid], [TimeSpan], [DateOnly], [TimeOnly], [BooleanNull], [ShortNull], [LongNull], [FloatNull], [DoubleNull], [DecimalNull], [DateTimeNull], [DateTimeOffsetNull], [GuidNull], [TimeSpanNull], [DateOnlyNull], [TimeOnlyNull]
+)
+ON
+(
+ t.[Id] = s.[Id]
+)
+WHEN NOT MATCHED BY TARGET THEN
+ INSERT
+ (
+ [Id],
+ [Name],
+ [Boolean],
+ [Short],
+ [Long],
+ [Float],
+ [Double],
+ [Decimal],
+ [DateTime],
+ [DateTimeOffset],
+ [Guid],
+ [TimeSpan],
+ [DateOnly],
+ [TimeOnly],
+ [BooleanNull],
+ [ShortNull],
+ [LongNull],
+ [FloatNull],
+ [DoubleNull],
+ [DecimalNull],
+ [DateTimeNull],
+ [DateTimeOffsetNull],
+ [GuidNull],
+ [TimeSpanNull],
+ [DateOnlyNull],
+ [TimeOnlyNull]
+ )
+ VALUES
+ (
+ s.[Id],
+ s.[Name],
+ s.[Boolean],
+ s.[Short],
+ s.[Long],
+ s.[Float],
+ s.[Double],
+ s.[Decimal],
+ s.[DateTime],
+ s.[DateTimeOffset],
+ s.[Guid],
+ s.[TimeSpan],
+ s.[DateOnly],
+ s.[TimeOnly],
+ s.[BooleanNull],
+ s.[ShortNull],
+ s.[LongNull],
+ s.[FloatNull],
+ s.[DoubleNull],
+ s.[DecimalNull],
+ s.[DateTimeNull],
+ s.[DateTimeOffsetNull],
+ s.[GuidNull],
+ s.[TimeSpanNull],
+ s.[DateOnlyNull],
+ s.[TimeOnlyNull]
+ )
+WHEN MATCHED THEN
+ UPDATE SET
+ t.[Name] = s.[Name],
+ t.[Boolean] = s.[Boolean],
+ t.[Short] = s.[Short],
+ t.[Long] = s.[Long],
+ t.[Float] = s.[Float],
+ t.[Double] = s.[Double],
+ t.[Decimal] = s.[Decimal],
+ t.[DateTime] = s.[DateTime],
+ t.[DateTimeOffset] = s.[DateTimeOffset],
+ t.[Guid] = s.[Guid],
+ t.[TimeSpan] = s.[TimeSpan],
+ t.[DateOnly] = s.[DateOnly],
+ t.[TimeOnly] = s.[TimeOnly],
+ t.[BooleanNull] = s.[BooleanNull],
+ t.[ShortNull] = s.[ShortNull],
+ t.[LongNull] = s.[LongNull],
+ t.[FloatNull] = s.[FloatNull],
+ t.[DoubleNull] = s.[DoubleNull],
+ t.[DecimalNull] = s.[DecimalNull],
+ t.[DateTimeNull] = s.[DateTimeNull],
+ t.[DateTimeOffsetNull] = s.[DateTimeOffsetNull],
+ t.[GuidNull] = s.[GuidNull],
+ t.[TimeSpanNull] = s.[TimeSpanNull],
+ t.[DateOnlyNull] = s.[DateOnlyNull],
+ t.[TimeOnlyNull] = s.[TimeOnlyNull]
+;
\ No newline at end of file
diff --git a/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildTableSqlTest.verified.txt b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildTableSqlTest.verified.txt
new file mode 100644
index 00000000..fd77918c
--- /dev/null
+++ b/test/FluentCommand.SqlServer.Tests/Snapshots/DataMergeGeneratorTests.BuildTableSqlTest.verified.txt
@@ -0,0 +1,29 @@
+CREATE TABLE [#MergeTable]
+(
+ [Id] int NULL,
+ [Name] nvarchar(MAX) NULL,
+ [Boolean] bit NULL,
+ [Short] smallint NULL,
+ [Long] bigint NULL,
+ [Float] real NULL,
+ [Double] float NULL,
+ [Decimal] decimal NULL,
+ [DateTime] datetime2 NULL,
+ [DateTimeOffset] datetimeoffset NULL,
+ [Guid] uniqueidentifier NULL,
+ [TimeSpan] time NULL,
+ [DateOnly] date NULL,
+ [TimeOnly] time NULL,
+ [BooleanNull] bit NULL,
+ [ShortNull] smallint NULL,
+ [LongNull] bigint NULL,
+ [FloatNull] real NULL,
+ [DoubleNull] float NULL,
+ [DecimalNull] decimal NULL,
+ [DateTimeNull] datetime2 NULL,
+ [DateTimeOffsetNull] datetimeoffset NULL,
+ [GuidNull] uniqueidentifier NULL,
+ [TimeSpanNull] time NULL,
+ [DateOnlyNull] date NULL,
+ [TimeOnlyNull] time NULL
+)