diff --git a/source/Nevermore.IntegrationTests/Model/TinyType.cs b/source/Nevermore.IntegrationTests/Model/CustomIdType.cs similarity index 100% rename from source/Nevermore.IntegrationTests/Model/TinyType.cs rename to source/Nevermore.IntegrationTests/Model/CustomIdType.cs diff --git a/source/Nevermore.IntegrationTests/Model/CustomPrefixIdKeyHandler.cs b/source/Nevermore.IntegrationTests/Model/CustomPrefixIdKeyHandler.cs new file mode 100644 index 00000000..7a8f80f0 --- /dev/null +++ b/source/Nevermore.IntegrationTests/Model/CustomPrefixIdKeyHandler.cs @@ -0,0 +1,11 @@ +namespace Nevermore.IntegrationTests.Model +{ + class CustomPrefixIdKeyHandler : StringCustomIdTypeIdKeyHandler + { + public const string CustomPrefix = "CustomPrefix"; + + public CustomPrefixIdKeyHandler():base(CustomPrefix) + { + } + } +} \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefix.cs b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefix.cs new file mode 100644 index 00000000..888f4c44 --- /dev/null +++ b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefix.cs @@ -0,0 +1,15 @@ +namespace Nevermore.IntegrationTests.Model +{ + public class DocumentWithCustomPrefix + { + public CustomPrefixId Id { get; set; } + public string Name { get; set; } + } + + public class CustomPrefixId : StringCustomIdType + { + internal CustomPrefixId(string value) : base(value) + { + } + } +} \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixAndStringId.cs b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixAndStringId.cs new file mode 100644 index 00000000..6665ce82 --- /dev/null +++ b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixAndStringId.cs @@ -0,0 +1,8 @@ +namespace Nevermore.IntegrationTests.Model +{ + public class DocumentWithCustomPrefixAndStringId + { + public string Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixAndStringIdMap.cs b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixAndStringIdMap.cs new file mode 100644 index 00000000..876b1991 --- /dev/null +++ b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixAndStringIdMap.cs @@ -0,0 +1,15 @@ +using Nevermore.Mapping; + +namespace Nevermore.IntegrationTests.Model +{ + public class DocumentWithCustomPrefixAndStringIdMap : DocumentMap + { + public const string CustomPrefix = "CustomPrefix"; + + public DocumentWithCustomPrefixAndStringIdMap() + { + Id().KeyHandler(new StringPrimaryKeyHandler(_ => CustomPrefix)); + Column(m => m.Name); + } + } +} \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixMap.cs b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixMap.cs new file mode 100644 index 00000000..f294808b --- /dev/null +++ b/source/Nevermore.IntegrationTests/Model/DocumentWithCustomPrefixMap.cs @@ -0,0 +1,13 @@ +using Nevermore.Mapping; + +namespace Nevermore.IntegrationTests.Model +{ + public class DocumentWithCustomPrefixMap : DocumentMap + { + public DocumentWithCustomPrefixMap() + { + Id(); + Column(m => m.Name); + } + } +} \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/Model/StringTinyTypeIdTypeHandler.cs b/source/Nevermore.IntegrationTests/Model/StringCustomIdTypeHandler.cs similarity index 100% rename from source/Nevermore.IntegrationTests/Model/StringTinyTypeIdTypeHandler.cs rename to source/Nevermore.IntegrationTests/Model/StringCustomIdTypeHandler.cs diff --git a/source/Nevermore.IntegrationTests/Model/StringTinyTypeIdKeyHandler.cs b/source/Nevermore.IntegrationTests/Model/StringCustomIdTypeIdKeyHandler.cs similarity index 54% rename from source/Nevermore.IntegrationTests/Model/StringTinyTypeIdKeyHandler.cs rename to source/Nevermore.IntegrationTests/Model/StringCustomIdTypeIdKeyHandler.cs index 19deeccd..ab2ce4f1 100644 --- a/source/Nevermore.IntegrationTests/Model/StringTinyTypeIdKeyHandler.cs +++ b/source/Nevermore.IntegrationTests/Model/StringCustomIdTypeIdKeyHandler.cs @@ -4,11 +4,17 @@ namespace Nevermore.IntegrationTests.Model { - class StringCustomIdTypeIdKeyHandler : IStringBasedPrimitivePrimaryKeyHandler + class StringCustomIdTypeIdKeyHandler : IPrimaryKeyHandler where T : StringCustomIdType { + readonly string? customPrefix; public Type Type => typeof(T); + public StringCustomIdTypeIdKeyHandler(string? customPrefix = null) + { + this.customPrefix = customPrefix; + } + public object? ConvertToPrimitiveValue(object? id) { if (!(id is StringCustomIdType stringCustomType)) @@ -19,22 +25,7 @@ class StringCustomIdTypeIdKeyHandler : IStringBasedPrimitivePrimaryKeyHandler public object GetNextKey(IKeyAllocator keyAllocator, string tableName) { var key = keyAllocator.NextId(tableName); - return CustomIdType.Create($"{GetPrefix(tableName)}-{key}")!; - } - - public void SetPrefix(Func idPrefix) - { - throw new NotImplementedException(); - } - - public string GetPrefix(string tableName) - { - return $"{tableName}s"; - } - - public void SetFormat(Func<(string idPrefix, int key), string> format) - { - throw new NotImplementedException(); + return CustomIdType.Create($"{customPrefix ?? tableName}s-{key}")!; } } } \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/RelationalTransaction/LoadFixture.cs b/source/Nevermore.IntegrationTests/RelationalTransaction/LoadFixture.cs index 9680d2c8..548be073 100644 --- a/source/Nevermore.IntegrationTests/RelationalTransaction/LoadFixture.cs +++ b/source/Nevermore.IntegrationTests/RelationalTransaction/LoadFixture.cs @@ -452,5 +452,37 @@ public void LoadManyByWrongIdType_ShouldThrowArgumentException() target.ShouldThrow().Which.Message.Should().Be("Provided Id of type 'System.String' does not match configured type of 'System.Guid'."); } } + + [Test] + public void StoreAndLoadWithCustomIdAndCustomPrefix() + { + using (var trn = Store.BeginTransaction()) + { + var document = new DocumentWithCustomPrefix() + { + Name = "test" + }; + + trn.Insert(document); + + document.Id.Value.Should().StartWith(CustomPrefixIdKeyHandler.CustomPrefix); + } + } + + [Test] + public void StoreAndLoadWithCustomPrefix() + { + using (var trn = Store.BeginTransaction()) + { + var document = new DocumentWithCustomPrefixAndStringId() + { + Name = "test" + }; + + trn.Insert(document); + + document.Id.Should().StartWith(DocumentWithCustomPrefixAndStringIdMap.CustomPrefix); + } + } } } \ No newline at end of file diff --git a/source/Nevermore.IntegrationTests/SetUp/FixtureWithRelationalStore.cs b/source/Nevermore.IntegrationTests/SetUp/FixtureWithRelationalStore.cs index 7511da85..eb391a0b 100644 --- a/source/Nevermore.IntegrationTests/SetUp/FixtureWithRelationalStore.cs +++ b/source/Nevermore.IntegrationTests/SetUp/FixtureWithRelationalStore.cs @@ -29,7 +29,9 @@ protected FixtureWithRelationalStore() new MessageWithGuidIdMap(), new DocumentWithRowVersionMap(), new DocumentWithIdentityIdMap(), - new DocumentWithIdentityIdAndRowVersionMap() + new DocumentWithIdentityIdAndRowVersionMap(), + new DocumentWithCustomPrefixMap(), + new DocumentWithCustomPrefixAndStringIdMap() }; var config = new RelationalStoreConfiguration(ConnectionString) @@ -43,6 +45,7 @@ protected FixtureWithRelationalStore() config.TypeHandlers.Register(new StringCustomIdTypeHandler()); config.PrimaryKeyHandlers.Register(new StringCustomIdTypeIdKeyHandler()); + config.PrimaryKeyHandlers.Register(new CustomPrefixIdKeyHandler()); config.InstanceTypeResolvers.Register(new ProductTypeResolver()); config.InstanceTypeResolvers.Register(new BrandTypeResolver()); diff --git a/source/Nevermore/Mapping/IIdColumnMappingBuilder.cs b/source/Nevermore/Mapping/IIdColumnMappingBuilder.cs index 0ce5555f..806b0326 100644 --- a/source/Nevermore/Mapping/IIdColumnMappingBuilder.cs +++ b/source/Nevermore/Mapping/IIdColumnMappingBuilder.cs @@ -17,18 +17,6 @@ public interface IIdColumnMappingBuilder : IColumnMappingBuilder /// IIdColumnMappingBuilder KeyHandler(IPrimaryKeyHandler primaryKeyHandler); - /// - /// Set a function that when given the TableName will return key prefix string. - /// - /// The function to call back to get the prefix. - IIdColumnMappingBuilder Prefix(Func idPrefix); - - /// - /// Set a function that format a key value, given a prefix and a key number. - /// - /// The function to call back to format the id. - IIdColumnMappingBuilder Format(Func<(string idPrefix, int key), string> format); - /// /// Builds the IdColumnMapping. /// diff --git a/source/Nevermore/Mapping/IStringBasedPrimitivePrimaryKeyHandler.cs b/source/Nevermore/Mapping/IStringBasedPrimitivePrimaryKeyHandler.cs deleted file mode 100644 index 0c499732..00000000 --- a/source/Nevermore/Mapping/IStringBasedPrimitivePrimaryKeyHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Nevermore.Mapping -{ - public interface IStringBasedPrimitivePrimaryKeyHandler : IPrimaryKeyHandler - { - /// - /// Set a function that when given the TableName will return key prefix string. - /// - /// The function to call back to get the prefix. - void SetPrefix(Func idPrefix); - - /// - /// Given a tableName, get the prefix for the key. - /// - /// - /// The key prefix for the given tableName - string GetPrefix(string tableName); - - /// - /// Set a function that format a key value, given a prefix and a key number. - /// - /// The function to call back to format the id. - void SetFormat(Func<(string idPrefix, int key), string> format); - } -} \ No newline at end of file diff --git a/source/Nevermore/Mapping/IdColumnMapping.cs b/source/Nevermore/Mapping/IdColumnMapping.cs index 5738a2e6..bfa2c9ad 100644 --- a/source/Nevermore/Mapping/IdColumnMapping.cs +++ b/source/Nevermore/Mapping/IdColumnMapping.cs @@ -44,73 +44,24 @@ public IIdColumnMappingBuilder Identity() { ValidateForIdentityUse(); - if (!(PrimaryKeyHandler is null)) - throw new InvalidOperationException($"{nameof(KeyHandler)} has already been set to a non-identity handler."); - IsIdentity = true; return this; } - void ValidateForIdentityUse() - { - if (!ValidIdentityTypes.Contains(Type)) - throw new InvalidOperationException($"The type {Type.Name} is not supported for Identity columns. Identity columns must be one of 'short', 'int' or 'long'."); - - if (hasCustomPropertyHandler) - throw new InvalidOperationException("Unable to configure an Identity Id column with a custom PropertyHandler"); - } - public IIdColumnMappingBuilder KeyHandler(IPrimaryKeyHandler primaryKeyHandler) { - if (!(PrimaryKeyHandler is null) && Direction == ColumnDirection.FromDatabase) - throw new InvalidOperationException($"{nameof(KeyHandler)} can only be called with an IIdentityPrimaryKeyHandler, once {nameof(Identity)} has been called."); - PrimaryKeyHandler = primaryKeyHandler; - return this; } - /// - /// Set a function that when given the TableName will return key prefix string. - /// - /// The function to call back to get the prefix. - public IIdColumnMappingBuilder Prefix(Func idPrefix) - { - if (Direction == ColumnDirection.FromDatabase) - throw new InvalidOperationException($"{nameof(Prefix)} cannot be set when an identity key handler has been configured."); - - if (PrimaryKeyHandler == null) - return KeyHandler(new StringPrimaryKeyHandler(idPrefix)); - - if (PrimaryKeyHandler is IStringBasedPrimitivePrimaryKeyHandler stringIdHandler) - { - stringIdHandler.SetPrefix(idPrefix); - return this; - } - - throw new InvalidOperationException($"Cannot set the Id prefix when the PrimaryKeyHandler is of type {PrimaryKeyHandler.GetType().Name}"); - } - - /// - /// Set a function that format a key value, given a prefix and a key number. - /// - /// The function to call back to format the id. - public IIdColumnMappingBuilder Format(Func<(string idPrefix, int key), string> format) + void ValidateForIdentityUse() { - if (!(PrimaryKeyHandler is null) && Direction == ColumnDirection.FromDatabase) - throw new InvalidOperationException($"{nameof(Format)} cannot be set when an identity key handler has been configured."); - - if (PrimaryKeyHandler == null) - return KeyHandler(new StringPrimaryKeyHandler(format: format)); - - if (PrimaryKeyHandler is IStringBasedPrimitivePrimaryKeyHandler stringIdHandler) - { - stringIdHandler.SetFormat(format); - return this; - } + if (!ValidIdentityTypes.Contains(Type)) + throw new InvalidOperationException($"The type {Type.Name} is not supported for Identity columns. Identity columns must be one of 'short', 'int' or 'long'."); - throw new InvalidOperationException($"Cannot set the key format when the PrimaryKeyHandler is of type {PrimaryKeyHandler.GetType().Name}"); + if (hasCustomPropertyHandler) + throw new InvalidOperationException("Unable to configure an Identity Id column with a custom PropertyHandler"); } protected override void SetCustomPropertyHandler(IPropertyHandler propertyHandler) diff --git a/source/Nevermore/Mapping/StringPrimaryKeyHandler.cs b/source/Nevermore/Mapping/StringPrimaryKeyHandler.cs index 1167ee10..299f33b2 100644 --- a/source/Nevermore/Mapping/StringPrimaryKeyHandler.cs +++ b/source/Nevermore/Mapping/StringPrimaryKeyHandler.cs @@ -3,43 +3,21 @@ namespace Nevermore.Mapping { - class StringPrimaryKeyHandler : PrimaryKeyHandler, IStringBasedPrimitivePrimaryKeyHandler + class StringPrimaryKeyHandler : PrimaryKeyHandler { - Func idPrefixFunc; - Func<(string idPrefix, int key), string> formatFunc; + readonly Func idPrefix; + readonly Func<(string idPrefix, int key), string> format; + public StringPrimaryKeyHandler(Func? idPrefix = null, Func<(string idPrefix, int key), string>? format = null) { - idPrefixFunc = idPrefix ?? (x => $"{x}s"); - formatFunc = format ?? (x => $"{x.idPrefix}-{x.key}"); - } - - /// - /// Set a function that when given the TableName will return key prefix string. - /// - /// The function to call back to get the prefix. - public void SetPrefix(Func idPrefix) - { - idPrefixFunc = idPrefix; - } - - public string GetPrefix(string tableName) - { - return idPrefixFunc(tableName); - } - - /// - /// Set a function that format a key value, given a prefix and a key number. - /// - /// The function to call back to format the id. - public void SetFormat(Func<(string idPrefix, int key), string> format) - { - formatFunc = format; + this.idPrefix = idPrefix ?? (tableName => $"{tableName}s"); + this.format = format ?? (x => $"{x.idPrefix}-{x.key}"); } public override object GetNextKey(IKeyAllocator keyAllocator, string tableName) { var nextKey = keyAllocator.NextId(tableName); - return formatFunc((GetPrefix(tableName), nextKey)); + return format((idPrefix(tableName), nextKey)); } } } \ No newline at end of file