I'm trying to migrate one of my modules from Postgres (with EF) to Cassandra. Here is my best try for Cassandra mappings:
internal sealed class UserMappings : Mappings
{
public UserMappings()
{
For<User>().TableName("users")
.PartitionKey(x => x.Id)
.ClusteringKey(x => x.Id)
.Column(x => x.Id, x => x.WithDbType<Guid>().WithName("id"))
// I want to add mappings for password Hash here
}
}
The first problem is that I use VO for completive safety but want to store primitives in database. Example VO for entity id:
public record UserId
{
public Guid Value { get; }
public UserId(Guid value)
{
if (value == Guid.Empty) throw new Exception("Invalid UserId");
Value = value;
}
public static implicit operator Guid(UserId id) => id.Value;
public static implicit operator UserId(Guid id) => new(id);
}
Secondly, my entity has private fields and I don't know how to map them to the database.
internal class User
{
private User()
{
}
public User(/*...*/)
{
//...
}
private string _passwordHash;
public UserId Id { get; }
//...
}
Also is public parameterless constructor required?
It sounds like you want to have some business logic in the classes that you are using to map to your database. I would recommend creating new classes that have only public properties and no logic whatsoever (i.e. POCOs) and then mapping these objects into your domain objects either "manually" or with a library like AutoMapper. This has the benefit of keeping your domain objects separate from the database schema.
The DataStax C# driver mapper will not be able to map private fields or properties that don't have a setter. It is able to map properties with a private
setter though so you might want to leverage that instead.
Also keep in mind that you will need to provide a custom TypeConverter
to the Mapper
or Table
objects if you use custom types in your mapping. You might get away with not implementing a TypeConveter if you have implicit operators to convert these types (like your UserId
class) but I'm not 100% sure.
On the constructor issue, I think having a private empty constructor is enough.