Files
docs/archive/03-BACKEND/MAPSTER-COMMON-ISSUES.md
T
masoodafar-web 5965b98728 update
2026-01-03 18:27:49 +03:30

6.0 KiB
Raw Blame History

🔧 Mapster - مشکلات رایج و راه‌حل‌ها

آخرین بروزرسانی: ۷ دی ۱۴۰۴
نسخه Mapster: 7.4.0


📋 فهرست مشکلات

  1. Protobuf Int64Value Mapping
  2. MediatR Unit to Empty
  3. Repeated Fields (List) Mapping
  4. Property Name Mismatch
  5. Nullable Types

1. Protobuf Int64Value Mapping

مشکل

فیلدهای google.protobuf.Int64Value (یا StringValue, BoolValue و غیره) که wrapper types هستند، در mapping مستقیم کار نمی‌کنند.

نشانه‌ها

  • مقدار همیشه 0 یا null می‌شود
  • Value در client ست شده ولی در server نادرست دریافت می‌شود

Proto:

import "google/protobuf/wrappers.proto";

message GetMyWeeklyBalancesRequest {
    google.protobuf.Int64Value week_definition_id = 3;
}

کد اشتباه:

config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
    .Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId);  // WRONG!

کد صحیح:

config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
    .Map(dest => dest.WeekDefinitionId, 
         src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : null);

توضیح

Int64Value یک class wrapper است نه primitive type. باید .Value را extract کنید.


2. MediatR Unit to Empty

مشکل

MediatR.Unit نمی‌تواند به google.protobuf.WellKnownTypes.Empty map شود.

نشانه‌ها

  • Exception: No mapping found for MediatR.Unit
  • gRPC call با void return کار نمی‌کند

کد اشتباه:

// No mapping defined - will fail at runtime
return await _mediator.Send(command).Adapt<Empty>();

راه‌حل:

// در GeneralMapping.cs یا هر Profile
config.NewConfig<MediatR.Unit, Google.Protobuf.WellKnownTypes.Empty>()
    .MapWith(_ => new Google.Protobuf.WellKnownTypes.Empty());

محل فایل:

BackOffice.BFF.WebApi/Common/Mappings/GeneralMapping.cs


3. Repeated Fields (List) Mapping

مشکل

فیلدهای repeated در protobuf به property RepeatedField<T> تبدیل می‌شوند که add-only هستند.

نشانه‌ها

  • لیست همیشه خالی
  • Exception: Cannot set RepeatedField

Proto:

message GetAllAppVersionsResponse {
    repeated AppVersionItem items = 1;
}

کد اشتباه:

config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
    .Map(dest => dest.Items, src => src);  // WRONG - Items is read-only

کد صحیح:

config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
    .MapWith(src => CreateResponse(src));

private static GetAllAppVersionsResponse CreateResponse(List<AppVersionItemDto> items)
{
    var response = new GetAllAppVersionsResponse();
    foreach (var item in items)
    {
        response.Items.Add(item.Adapt<AppVersionItem>());
    }
    return response;
}

4. Property Name Mismatch

مشکل

نام property در DTO با نام در proto message یکی نیست.

نشانه‌ها

  • فیلد همیشه null یا default value
  • Auto-mapping کار نمی‌کند

مثال:

message MetaData {
    int32 total_page = 2;  // -> TotalPage in C#
}
public class MetaDataDto
{
    public int TotalPages { get; set; }  // WRONG: should be TotalPage
}

راه‌حل 1 - اصلاح نام:

public class MetaDataDto
{
    public int TotalPage { get; set; }  // Match proto
}

راه‌حل 2 - Explicit mapping:

config.NewConfig<MetaData, MetaDataDto>()
    .Map(dest => dest.TotalPages, src => src.TotalPage);

5. Nullable Types

مشکل

Nullable types در C# نیاز به handling خاص دارند.

Proto با nullable:

google.protobuf.Int64Value nullable_id = 1;

در DTO:

public long? NullableId { get; set; }

Mapping:

.Map(dest => dest.NullableId, 
     src => src.NullableId != null ? (long?)src.NullableId.Value : null)

🎯 Best Practices

1. همیشه Explicit Mapping برای Complex Types

// بهتر است همیشه explicit باشد
config.NewConfig<SourceType, DestType>()
    .Map(dest => dest.Prop1, src => src.Prop1)
    .Map(dest => dest.Prop2, src => src.Prop2);

2. استفاده از MapWith برای Custom Logic

config.NewConfig<Source, Dest>()
    .MapWith(src => new Dest
    {
        // full control
    });

3. فایل Profile مجزا برای هر Domain

Common/Mappings/
├── CommissionProfile.cs
├── NetworkProfile.cs
├── ClubProfile.cs
└── GeneralMapping.cs  // for common types like Unit -> Empty

4. تست Mapping ها

[Fact]
public void Should_Map_Request_To_Query()
{
    // Arrange
    var request = new GetMyWeeklyBalancesRequest { WeekDefinitionId = 7 };
    
    // Act
    var query = request.Adapt<GetMyWeeklyBalancesQuery>();
    
    // Assert
    Assert.Equal(7, query.WeekDefinitionId);
}

📁 فایل‌های Profile در پروژه

پروژه مسیر محتوا
CMS WebApi/Common/Mappings/ CommissionProfile, AppVersionProfile
BackOffice.BFF Application/Common/Mappings/ CommissionProfile
BackOffice.BFF WebApi/Common/Mappings/ GeneralMapping
FrontOffice.BFF WebApi/Common/Mappings/ CommissionProfile

🔗 منابع