Typed ids to plain id

My model is something like
// BaseEntity<UserId> has UserId Id
public class User : BaseEntity<UserId> {
public required string Name { get; init; ]
}
// BaseEntity<UserId> has UserId Id
public class User : BaseEntity<UserId> {
public required string Name { get; init; ]
}
How would I transform that UserId into a type that's valid in the database, like the Guid or int that's inside of the UserId?
23 Replies
Sun「無用」
Sun「無用」OP7mo ago
And will typed ids be problematic in the long run? That's just a pattern I saw and that could be applied in my applications, dunno if it's good or not
Joschi
Joschi7mo ago
You can use EFCore Value Converters for this:
c#
internal sealed class UserIdConverter: ValueConverter<UserId, string>
{
public UserIdConverter() : this(null) { }
public UserIdConverter(ConverterMappingHints? mappingHints = default)
: base(
id => id.Id, // From C# object to database
databaseObject => new UserId(id), // or however your UserIds are created
mappingHints
) { }
}
c#
internal sealed class UserIdConverter: ValueConverter<UserId, string>
{
public UserIdConverter() : this(null) { }
public UserIdConverter(ConverterMappingHints? mappingHints = default)
: base(
id => id.Id, // From C# object to database
databaseObject => new UserId(id), // or however your UserIds are created
mappingHints
) { }
}
And then register it in the DBContext
c#
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<UserId>().HaveConversion<UserIdConverter>(); // Now this converter will be used for every UserId property

base.ConfigureConventions(configurationBuilder);
}
c#
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<UserId>().HaveConversion<UserIdConverter>(); // Now this converter will be used for every UserId property

base.ConfigureConventions(configurationBuilder);
}
Sun「無用」
Sun「無用」OP7mo ago
nani question... what.
Joschi
Joschi7mo ago
I just assumed you use EFCore
Sun「無用」
Sun「無用」OP7mo ago
yea, it's ef I dunno what all of that is though
Sun「無用」
Sun「無用」OP7mo ago
yep, I'm still lost :d
Joschi
Joschi7mo ago
So EFCore has the capability to convert your dotnet types like your custom UserId object to an database compatible type like string or uuid or whatever. This is done by defining how this conversion is done in a class inheriting from ValueConverter<T, TPrimitive>. So if UserId wraps a Guid you would need to define a class UserIdConverter: ValueConverter<UserId, Guid> and implement it. Then to tell EFCore that this exists and that you use it, you override the OnConfigureConventions Method in your DbContext and tell EFCore that it should use this conversion for each property of a given type. Like UserId. Clear so far?
Sun「無用」
Sun「無用」OP7mo ago
is that just a weird mapper?
Joschi
Joschi7mo ago
You could call it a mapper I guess.
Sun「無用」
Sun「無用」OP7mo ago
also what's even that ConfigureConventions thing? I've never seen that and it's not on the link you've sent
Joschi
Joschi7mo ago
You got some class inheriting from DbContext right?
Sun「無用」
Sun「無用」OP7mo ago
yea ofc
Joschi
Joschi7mo ago
DbContext has a method called ConfigureConventions, which you can override. In there you can define settings and conventions which then apply to all models in the context.
Joschi
Joschi7mo ago
Sun「無用」
Sun「無用」OP7mo ago
note to self: don't used typed ids that's kinda weird and complex for absolutely no reason
Joschi
Joschi7mo ago
I think that's the wrong takeaway. Typed IDs are great, they just require some setup to use and you need to know about some of their caveats. There are source generators which do all the code repition for you like https://github.com/andrewlock/StronglyTypedId
GitHub
GitHub - andrewlock/StronglyTypedId: A Rosyln-powered generator for...
A Rosyln-powered generator for strongly-typed IDs. Contribute to andrewlock/StronglyTypedId development by creating an account on GitHub.
Joschi
Joschi7mo ago
It's also not just typed IDs. Whenever you start to encapsulate primitives into value objects, you will need ValueConverters in C#.
Sun「無用」
Sun「無用」OP7mo ago
and that'll be a problem for me since I'll need to define that in every context, right? I plan to have 2 contexts, so yea
Joschi
Joschi7mo ago
You could just have an extension method containing all the shared config. In the end you are correct. Strongly typed IDs require some thinking and additional code to set up in the first place. It is easier to just use incrementing integers as Ids everywhere. And if the trade off of better readability and type safety isn't worth the hassle in your opinion, then don't use them.
Sun「無用」
Sun「無用」OP7mo ago
readability? kinda? no? it is type safe, yes, but I still think it's too complex just for type safety I'll try it in testing for a bit, but I'll probably not use it thx for the help
Joschi
Joschi7mo ago
Readability is big part. Consider a method signature like this. public async Task UpdateComment(int userId, int blogId, int commentId, string newContent) And compare that to public async Task UpdateComment(UserId userId, BlogId blogId, CommentId commentId, string newContent) The second one is way more readable in my opinion. It is clear what each parameter does, without depending on the name of the variable to tell you. In this case the first one is fine and readable, but consider someone else writing code and not being very good with namings. public async Task UpdateComment(int user, int postId, int postResponseId, string test)
Sun「無用」
Sun「無用」OP7mo ago
I'm just thinking that we're in modern day, where you can just ctrl+k(or whatever your keybind is) and see the param names. That's why I only think of the type safety of it, I don't see any difference between these two even if the dev is bad at naming, some name will still be there too, so that shouldn't be a problem, unless you're a go developer (single named variables)
Want results from more Discord servers?
Add your server