0

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?

  • A friendly note on how to ask good questions. The general guidance is that you (a) provide a good summary of the problem that includes software/component versions, the full error message + full stack trace; (b) describe what you've tried to fix the problem, details of investigation you've done; and (c) minimal sample code that replicates the problem. Cheers! – Erick Ramirez Aug 04 '22 at 23:10
  • I can't use your code locally so I can't reproduce the issue. There is no definition for types "Email, "FirstName", "RefreshToken", "PasswordHash" "InvalidUserIdException". If these types are not necessary to reproduce the issue then don't add these fields or just use native types like string and Exception. – João Reis Aug 08 '22 at 13:57
  • Also what do you mean by "I have VO for most of my fields", what does VO mean? – João Reis Aug 08 '22 at 13:58
  • @JoãoReis VO for me is class/record with one property inside to avoid primitive obsession and it is safer to use them for eg when order of parameters change it will not compile. I will remove unnecessary parts of my code :) – Adrian Franczak Aug 08 '22 at 14:15

1 Answers1

1

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.

João Reis
  • 712
  • 3
  • 9
  • what about adding overload for public Map Column with string as first parameter to get field by name? – Adrian Franczak Aug 08 '22 at 20:39
  • The driver automatically infers columns if you don't specify them and even with the default behavior it still doesn't pick up private fields or properties without setters because this is by design. We could allow for opt in mapping of private fields using something like what you suggested, feel free to create a JIRA on https://datastax-oss.atlassian.net/jira/software/c/projects/CSHARP/issues but keep in mind that something like this is a low priority and might not get picked up any time soon. – João Reis Aug 09 '22 at 00:55