Fixes sql server DateTimeOffset mapping. Makes Custom DB types much more flexible and usable by 3rd parties.

This commit is contained in:
Shannon
2021-07-27 10:15:45 -06:00
parent d218cb4599
commit 499a0608ec
10 changed files with 122 additions and 73 deletions

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
{
/// <summary>
/// Allows for specifying custom DB types that are not natively mapped.
/// </summary>
public struct SpecialDbType : IEquatable<SpecialDbType>
{
private readonly string _dbType;
public SpecialDbType(string dbType)
{
if (string.IsNullOrWhiteSpace(dbType))
{
throw new ArgumentException($"'{nameof(dbType)}' cannot be null or whitespace.", nameof(dbType));
}
_dbType = dbType;
}
public SpecialDbType(SpecialDbTypes specialDbTypes)
=> _dbType = specialDbTypes.ToString();
public static SpecialDbType NTEXT { get; } = new SpecialDbType(SpecialDbTypes.NTEXT);
public static SpecialDbType NCHAR { get; } = new SpecialDbType(SpecialDbTypes.NCHAR);
public static SpecialDbType NVARCHARMAX { get; } = new SpecialDbType(SpecialDbTypes.NVARCHARMAX);
public override bool Equals(object obj) => obj is SpecialDbType types && Equals(types);
public bool Equals(SpecialDbType other) => _dbType == other._dbType;
public override int GetHashCode() => 1038481724 + EqualityComparer<string>.Default.GetHashCode(_dbType);
public override string ToString() => _dbType.ToString();
// Make this directly castable to string
public static implicit operator string(SpecialDbType dbType) => dbType.ToString();
// direct equality operators with SpecialDbTypes enum
public static bool operator ==(SpecialDbTypes x, SpecialDbType y) => x.ToString() == y;
public static bool operator !=(SpecialDbTypes x, SpecialDbType y) => x.ToString() != y;
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
{
@@ -12,13 +12,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
public class SpecialDbTypeAttribute : Attribute
{
public SpecialDbTypeAttribute(SpecialDbTypes databaseType)
{
DatabaseType = databaseType;
}
=> DatabaseType = new SpecialDbType(databaseType);
public SpecialDbTypeAttribute(string databaseType)
=> DatabaseType = new SpecialDbType(databaseType);
/// <summary>
/// Gets or sets the <see cref="SpecialDbTypes"/> for this column
/// Gets or sets the <see cref="SpecialDbType"/> for this column
/// </summary>
public SpecialDbTypes DatabaseType { get; private set; }
public SpecialDbType DatabaseType { get; private set; }
}
}

View File

@@ -1,13 +1,12 @@
namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations
{
/// <summary>
/// Enum with the two special types that has to be supported because
/// of the current umbraco db schema.
/// Known special DB types required for Umbraco.
/// </summary>
public enum SpecialDbTypes
{
NTEXT,
NCHAR,
NVARCHARMAX
NVARCHARMAX,
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Data;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
@@ -12,9 +12,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions
//When DbType isn't set explicitly the Type will be used to find the right DbType in the SqlSyntaxProvider.
//This type is typically used as part of an initial table creation
public Type PropertyType { get; set; }
//Only used for special cases as part of an initial table creation
public bool HasSpecialDbType { get; set; }
public SpecialDbTypes DbType { get; set; }
/// <summary>
/// Used for column types that cannot be natively mapped.
/// </summary>
public SpecialDbType? CustomDbType { get; set; }
public virtual int Seeding { get; set; }
public virtual int Size { get; set; }
public virtual int Precision { get; set; }

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Reflection;
using NPoco;
@@ -75,8 +75,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions
var databaseTypeAttribute = propertyInfo.FirstAttribute<SpecialDbTypeAttribute>();
if (databaseTypeAttribute != null)
{
definition.HasSpecialDbType = true;
definition.DbType = databaseTypeAttribute.DatabaseType;
definition.CustomDbType = databaseTypeAttribute.DatabaseType;
}
else
{

View File

@@ -24,15 +24,16 @@ namespace Umbraco.Cms.Infrastructure.Persistence
IEnumerable<IProviderSpecificMapperFactory> providerSpecificMapperFactories)
{
_getFactory = getFactory;
_embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x=>x.ProviderName);
_syntaxProviders = syntaxProviders.ToDictionary(x=>x.ProviderName);
_bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x=>x.ProviderName);
_providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x=>x.ProviderName);
_embeddedDatabaseCreators = embeddedDatabaseCreators.ToDictionary(x => x.ProviderName);
_syntaxProviders = syntaxProviders.ToDictionary(x => x.ProviderName);
_bulkSqlInsertProviders = bulkSqlInsertProviders.ToDictionary(x => x.ProviderName);
_providerSpecificMapperFactories = providerSpecificMapperFactories.ToDictionary(x => x.ProviderName);
}
public DbProviderFactory CreateFactory(string providerName)
{
if (string.IsNullOrEmpty(providerName)) return null;
if (string.IsNullOrEmpty(providerName))
return null;
return _getFactory(providerName);
}
@@ -40,7 +41,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName)
{
if(!_syntaxProviders.TryGetValue(providerName, out var result))
if (!_syntaxProviders.TryGetValue(providerName, out var result))
{
throw new InvalidOperationException($"Unknown provider name \"{providerName}\"");
}
@@ -51,7 +52,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName)
{
if(!_bulkSqlInsertProviders.TryGetValue(providerName, out var result))
if (!_bulkSqlInsertProviders.TryGetValue(providerName, out var result))
{
return new BasicBulkSqlInsertProvider();
}
@@ -61,7 +62,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
public void CreateDatabase(string providerName)
{
if(_embeddedDatabaseCreators.TryGetValue(providerName, out var creator))
if (_embeddedDatabaseCreators.TryGetValue(providerName, out var creator))
{
creator.Create();
}
@@ -69,7 +70,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
public NPocoMapperCollection ProviderSpecificMappers(string providerName)
{
if(_providerSpecificMapperFactories.TryGetValue(providerName, out var mapperFactory))
if (_providerSpecificMapperFactories.TryGetValue(providerName, out var mapperFactory))
{
return mapperFactory.Mappers;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
@@ -6,6 +6,7 @@ using NPoco;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence
{
@@ -68,22 +69,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence
foreach (var col in _columnDefinitions)
{
SqlDbType sqlDbType;
if (col.HasSpecialDbType)
if (col.CustomDbType.HasValue)
{
//get the SqlDbType from the 'special type'
switch (col.DbType)
switch (col.CustomDbType)
{
case SpecialDbTypes.NTEXT:
case var x when x == SpecialDbType.NTEXT:
sqlDbType = SqlDbType.NText;
break;
case SpecialDbTypes.NCHAR:
case var x when x == SpecialDbType.NCHAR:
sqlDbType = SqlDbType.NChar;
break;
case SpecialDbTypes.NVARCHARMAX:
case var x when x == SpecialDbType.NVARCHARMAX:
sqlDbType = SqlDbType.NVarChar;
break;
default:
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException("The custom DB type " + col.CustomDbType + " is not supported for bulk import statements.");
}
}
else if (col.Type.HasValue)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text.RegularExpressions;
@@ -28,7 +28,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
string GetQuotedName(string name);
bool DoesTableExist(IDatabase db, string tableName);
string GetIndexType(IndexTypes indexTypes);
string GetSpecialDbType(SpecialDbTypes dbTypes);
string GetSpecialDbType(SpecialDbType dbType);
string CreateTable { get; }
string DropTable { get; }
string AddColumn { get; }

View File

@@ -54,38 +54,36 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
public Regex AliasRegex { get; }
public string GetWildcardPlaceholder()
{
return "%";
}
public string GetWildcardPlaceholder() => "%";
public string StringLengthNonUnicodeColumnDefinitionFormat = "VARCHAR({0})";
public string StringLengthUnicodeColumnDefinitionFormat = "NVARCHAR({0})";
public string DecimalColumnDefinitionFormat = "DECIMAL({0},{1})";
public string StringLengthNonUnicodeColumnDefinitionFormat { get; } = "VARCHAR({0})";
public string StringLengthUnicodeColumnDefinitionFormat { get; } = "NVARCHAR({0})";
public string DecimalColumnDefinitionFormat { get; } = "DECIMAL({0},{1})";
public string DefaultValueFormat = "DEFAULT ({0})";
public int DefaultStringLength = 255;
public int DefaultDecimalPrecision = 20;
public int DefaultDecimalScale = 9;
public string DefaultValueFormat { get; } = "DEFAULT ({0})";
public int DefaultStringLength { get; } = 255;
public int DefaultDecimalPrecision { get; } = 20;
public int DefaultDecimalScale { get; } = 9;
//Set by Constructor
public string StringColumnDefinition;
public string StringLengthColumnDefinitionFormat;
public string StringColumnDefinition { get; }
public string StringLengthColumnDefinitionFormat { get; }
public string AutoIncrementDefinition = "AUTOINCREMENT";
public string IntColumnDefinition = "INTEGER";
public string LongColumnDefinition = "BIGINT";
public string GuidColumnDefinition = "GUID";
public string BoolColumnDefinition = "BOOL";
public string RealColumnDefinition = "DOUBLE";
public string DecimalColumnDefinition;
public string BlobColumnDefinition = "BLOB";
public string DateTimeColumnDefinition = "DATETIME";
public string TimeColumnDefinition = "DATETIME";
public string AutoIncrementDefinition { get; protected set; } = "AUTOINCREMENT";
public string IntColumnDefinition { get; } = "INTEGER";
public string LongColumnDefinition { get; } = "BIGINT";
public string GuidColumnDefinition { get; protected set; } = "GUID";
public string BoolColumnDefinition { get; protected set; } = "BOOL";
public string RealColumnDefinition { get; protected set; } = "DOUBLE";
public string DecimalColumnDefinition { get; protected set; }
public string BlobColumnDefinition { get; protected set; } = "BLOB";
public string DateTimeColumnDefinition { get; } = "DATETIME";
public string TimeColumnDefinition { get; protected set; } = "DATETIME";
protected IList<Func<ColumnDefinition, string>> ClauseOrder { get; }
protected DbTypes DbTypeMap = new DbTypes();
protected DbTypes DbTypeMap { get; } = new DbTypes();
protected void InitColumnTypeMap()
{
DbTypeMap.Set<string>(DbType.String, StringColumnDefinition);
@@ -100,8 +98,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
DbTypeMap.Set<DateTime?>(DbType.DateTime, DateTimeColumnDefinition);
DbTypeMap.Set<TimeSpan>(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set<TimeSpan?>(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set<DateTimeOffset>(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set<DateTimeOffset?>(DbType.Time, TimeColumnDefinition);
DbTypeMap.Set<DateTimeOffset>(DbType.DateTimeOffset, TimeColumnDefinition);
DbTypeMap.Set<DateTimeOffset?>(DbType.DateTimeOffset, TimeColumnDefinition);
DbTypeMap.Set<byte>(DbType.Byte, IntColumnDefinition);
DbTypeMap.Set<byte?>(DbType.Byte, IntColumnDefinition);
@@ -193,17 +191,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
return indexType;
}
public virtual string GetSpecialDbType(SpecialDbTypes dbTypes)
public virtual string GetSpecialDbType(SpecialDbType dbType)
{
if (dbTypes == SpecialDbTypes.NCHAR)
if (dbType == SpecialDbType.NCHAR)
{
return "NCHAR";
return SpecialDbType.NCHAR;
}
else if (dbTypes == SpecialDbTypes.NTEXT)
else if (dbType == SpecialDbType.NTEXT)
{
return "NTEXT";
return SpecialDbType.NTEXT;
}
else if (dbTypes == SpecialDbTypes.NVARCHARMAX)
else if (dbType == SpecialDbType.NVARCHARMAX)
{
return "NVARCHAR(MAX)";
}
@@ -470,14 +468,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.SqlSyntax
if (column.Type.HasValue == false && string.IsNullOrEmpty(column.CustomType) == false)
return column.CustomType;
if (column.HasSpecialDbType)
if (column.CustomDbType.HasValue)
{
if (column.Size != default(int))
if (column.Size != default)
{
return $"{GetSpecialDbType(column.DbType)}({column.Size})";
return $"{GetSpecialDbType(column.CustomDbType.Value)}({column.Size})";
}
return GetSpecialDbType(column.DbType);
return GetSpecialDbType(column.CustomDbType.Value);
}
var type = column.Type.HasValue

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
@@ -300,10 +300,14 @@ where table_name=@0 and column_name=@1", tableName, columnName).FirstOrDefault()
GetQuotedTableName(index.TableName), columns);
}
public override string GetSpecialDbType(SpecialDbTypes dbTypes)
public override string GetSpecialDbType(SpecialDbType dbTypes)
{
if (dbTypes == SpecialDbTypes.NVARCHARMAX) // SqlCE does not have nvarchar(max) for now
// SqlCE does not have nvarchar(max) for now
if (dbTypes == SpecialDbType.NVARCHARMAX)
{
return "NTEXT";
}
return base.GetSpecialDbType(dbTypes);
}
public override SqlDbType GetSqlDbType(DbType dbType)