MapsterMapper / Mapster

A fast, fun and stimulating object to object Mapper
MIT License
4.31k stars 328 forks source link

Cannot convert immutable type of StronglyTypedId #441

Open OFark opened 2 years ago

OFark commented 2 years ago

Hi,

So I'm getting this error:

Mapster.CompileException: Error while compiling source=Domain.Orders.Models.Order destination=Infrastructure.SQL.Orders.Models.DB_Order type=Map ---> Mapster.CompileException: Error while compiling source=System.Nullable1[Domain.Orders.Models.OrderId] destination=System.Nullable1[System.Int32] type=Map ---> System.InvalidOperationException: Cannot convert immutable type, please consider using 'MapWith' method to create mapping

When I'm trying to do this: var dbOrder = order.Adapt<DB_Order>();

DB_Order looks like:

internal record DB_Order
{    
    public int Id { get; init; }
    public string? OrderNumber { get; init; } 
    ...

Order looks like:

public record Order {
    public OrderId Id { get; init; }
    public OrderNumber? OrderNumber { get; init; }

OrderId is a StronglyTypedId struct backed with an Int.

I'm converting an Immutable type into a int, so I don't know why I'm getting this error I have added a whole host of Map and MapWiths to no effect:

 public async Task<Order> CreateOrderAsync(Order order, CancellationToken token = default)
    {
        TypeAdapterConfig<int, OrderId>
            .NewConfig()
            .ConstructUsing(x => new OrderId(x));

        TypeAdapterConfig<OrderId, int>
            .NewConfig()
            .MapWith(x => x.Value);

        var dbOrder = order.Adapt<DB_Order>(); <-- error

and I've tried:

public async Task<Order> CreateOrderAsync(Order order, CancellationToken token = default)
    {

        var conf = TypeAdapterConfig<Order, DB_Order>
            .NewConfig()
            .Map(dbOrder => dbOrder.Id, order => order.Id.Value).Config;

        var dbOrder = order.Adapt<DB_Order>(conf); <-- error

Any ideas?

If you are unfamiliar with StronglyTypedIds it generates this:

readonly partial struct OrderId : System.IComparable<OrderId>, System.IEquatable<OrderId>
    {
        public int Value { get; }

        public OrderId(int value)
        {
            Value = value;
        }

        public static readonly OrderId Empty = new OrderId(0);

        public bool Equals(OrderId other) => this.Value.Equals(other.Value);
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            return obj is OrderId other && Equals(other);
        }

        public override int GetHashCode() => Value.GetHashCode();

        public override string ToString() => Value.ToString();
        public static bool operator ==(OrderId a, OrderId b) => a.Equals(b);
        public static bool operator !=(OrderId a, OrderId b) => !(a == b);
        public int CompareTo(OrderId other) => Value.CompareTo(other.Value);

        class OrderIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<OrderId>
        {
            public override OrderId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
            {
                return new OrderId(reader.GetInt32());
            }

            public override void Write(System.Text.Json.Utf8JsonWriter writer, OrderId value, System.Text.Json.JsonSerializerOptions options)
            {
                writer.WriteNumberValue(value.Value);
            }
        }
    }
OFark commented 2 years ago

I figured it out I had another nullable OrderId further down the model. I didn't realise that you needed to cover both OrderId and OrderId?