-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added the concept of Primary Key Handlers #149
Conversation
|
||
namespace Nevermore.IntegrationTests.Model | ||
{ | ||
class StringTinyTypeIdTypeHandler<T> : ITypeHandler where T : TinyType<string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would avoid referencing TinyType
name anywhere in Nevermore because this might cause quite a bit of confusion. What about CustomIdType
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep 👍
|
||
namespace Nevermore.IntegrationTests.Model | ||
{ | ||
public class TinyType<T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above. e.g. CustomIdType
/// <typeparam name="TKey">The type of the Id</typeparam> | ||
/// <param name="id">The <c>Id</c> of the document to find.</param> | ||
/// <returns>The document, or <c>null</c> if the document is not found.</returns> | ||
[Pure] TDocument Load<TDocument, TKey>(TKey id) where TDocument : class; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need generic members for most of the queries, I can imagine they will be added once we have a solution to the main problem?
/// </summary> | ||
protected string IdPrefix | ||
protected IPrimaryKeyHandler? PrimaryKeyHandler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't feel right to have the primary key handler specified in the mapping. Do we really need it here?
{ | ||
class GuidPrimaryKeyHandler : PrimitivePrimaryKeyHandler<Guid> | ||
{ | ||
public override object FormatKey(string tableName, int key) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels wrong and only works because we actually don't call this method. If we did this cast would fail
return (TKey) AllocateIdUsingHandler(mapping, primitivePrimaryKeyHandler); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if FormatKey
was renamed to GetKey(tableName)
. StringPrimaryKeyHandler
could us keyAllocator
internally to get a unique id.
/// <param name="idPrefix">The function to call back to get the prefix.</param> | ||
public IIdColumnMappingBuilder Prefix(Func<string, string> idPrefix) | ||
{ | ||
if (!(PrimaryKeyHandler is null) && Direction == ColumnDirection.FromDatabase && PrimaryKeyHandler is IIdentityPrimaryKeyHandler) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a bit of type checking in a few places, I can imagine you tried to remove it but that would break too much of the existing code?
{ | ||
public override object FormatKey(string tableName, int key) | ||
{ | ||
return key; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as Guid
public Type Type => typeof(T); | ||
|
||
[return: NotNullIfNotNull("id")] | ||
public virtual object? GetPrimitiveValue(object? id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetPrimitiveValue
convert to ConvertToPrimitiveValue
?
I think we are going in the direction but some abstractions don't feel fully formed. I will try to experiment with a few approaches tomorrow. |
@@ -540,20 +577,23 @@ protected async Task<TResult[]> ReadResultsAsync<TResult>(PreparedCommand prepar | |||
{ | |||
var mapping = configuration.DocumentMaps.Resolve(typeof(TDocument)); | |||
|
|||
if (mapping.IdColumn is null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could mark IdColumn
as not nullable if owe passed it to DocumentMap
via the constructor. I can imagine this will break gazillion things in the server?
IdColumn = builtIdColumn, |
|
||
public void Delete<TDocument>(int id, DeleteOptions options = null) where TDocument : class | ||
=> Delete<TDocument>((object) id, options); | ||
=> Delete<TDocument, int>(id, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice :)
return mapping.IdColumn.PrimaryKeyHandler.GetNextKey(keyAllocator, mapping.TableName); | ||
} | ||
|
||
public string AllocateId(string tableName, string idPrefix) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about marking this method as obsolete?
/// <param name="keyAllocator">The key allocator to use getting the next id from.</param> | ||
/// <param name="tableName">The table name the key is required for.</param> | ||
/// <returns>The next key, as the type that matches the model object's Id property type. ConvertToPrimitiveValue should be called with this value if it is to be used as a Sql parameter.</returns> | ||
object GetNextKey(IKeyAllocator keyAllocator, string tableName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This abstraction is a bit leaky. Ideally, keyAllocator
and tableName
would be treated as implementation details of a given primary key handler. That being said, this is not a 5 minute job so happy to keep it as a future ToDo item :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You had to jump over a lot hurdles but we are in much better place now. There is still work to do but that's ok. Thanks!
dc6bf6f
to
fd85926
Compare
… consistency with the other property names
…ver uses this a lot for related documents determination
…rence types that are in use
…yKeyHandler, it only makes sense for the IStringBasedPrimitivePrimaryKeyHandler handlers
…builders, and take config in to make sure that every document map ends up with an IdColumn that has a PrimaryKeyHandler and specifies whether it is an Identity
… up how keys are handled in there
…eated to pass to the `KeyHandler` method for the IdColumn, to customise the id handling for a given document map
…ix, rather than the Func
093de4b
to
9d35dea
Compare
…rection correctly
…ration code, it didn't cater for custom primary key handlers. The handlers now supply it with the information it needs.
This page should be updated when this PR merges. A new page for |
This PR is based on the learnings from #146 and the updates being proposed in #145 for Identity primary key support.
It adds the concept of PrimaryKeyHandlers, which in many ways are similar to having TypeHandlers, but specific to the use cases required of primary keys, e.g. INSERTs and DELETEs operations.
Reviewing is probably easiest by commit. The first 2 build out the functionality and the 3rd updates the Customer class in the integration tests to use a CustomerId primary key field.