update
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
# 🔧 Mapster - مشکلات رایج و راهحلها
|
||||
|
||||
> **آخرین بروزرسانی**: ۷ دی ۱۴۰۴
|
||||
> **نسخه Mapster**: 7.4.0
|
||||
|
||||
---
|
||||
|
||||
## 📋 فهرست مشکلات
|
||||
|
||||
1. [Protobuf Int64Value Mapping](#1-protobuf-int64value-mapping)
|
||||
2. [MediatR Unit to Empty](#2-mediatr-unit-to-empty)
|
||||
3. [Repeated Fields (List) Mapping](#3-repeated-fields-list-mapping)
|
||||
4. [Property Name Mismatch](#4-property-name-mismatch)
|
||||
5. [Nullable Types](#5-nullable-types)
|
||||
|
||||
---
|
||||
|
||||
## 1. Protobuf Int64Value Mapping
|
||||
|
||||
### مشکل
|
||||
فیلدهای `google.protobuf.Int64Value` (یا `StringValue`, `BoolValue` و غیره) که wrapper types هستند، در mapping مستقیم کار نمیکنند.
|
||||
|
||||
### نشانهها
|
||||
- مقدار همیشه `0` یا `null` میشود
|
||||
- Value در client ست شده ولی در server نادرست دریافت میشود
|
||||
|
||||
### Proto:
|
||||
```protobuf
|
||||
import "google/protobuf/wrappers.proto";
|
||||
|
||||
message GetMyWeeklyBalancesRequest {
|
||||
google.protobuf.Int64Value week_definition_id = 3;
|
||||
}
|
||||
```
|
||||
|
||||
### ❌ کد اشتباه:
|
||||
```csharp
|
||||
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
|
||||
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId); // WRONG!
|
||||
```
|
||||
|
||||
### ✅ کد صحیح:
|
||||
```csharp
|
||||
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 کار نمیکند
|
||||
|
||||
### ❌ کد اشتباه:
|
||||
```csharp
|
||||
// No mapping defined - will fail at runtime
|
||||
return await _mediator.Send(command).Adapt<Empty>();
|
||||
```
|
||||
|
||||
### ✅ راهحل:
|
||||
```csharp
|
||||
// در 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:
|
||||
```protobuf
|
||||
message GetAllAppVersionsResponse {
|
||||
repeated AppVersionItem items = 1;
|
||||
}
|
||||
```
|
||||
|
||||
### ❌ کد اشتباه:
|
||||
```csharp
|
||||
config.NewConfig<List<AppVersionItemDto>, GetAllAppVersionsResponse>()
|
||||
.Map(dest => dest.Items, src => src); // WRONG - Items is read-only
|
||||
```
|
||||
|
||||
### ✅ کد صحیح:
|
||||
```csharp
|
||||
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 کار نمیکند
|
||||
|
||||
### مثال:
|
||||
```protobuf
|
||||
message MetaData {
|
||||
int32 total_page = 2; // -> TotalPage in C#
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
public class MetaDataDto
|
||||
{
|
||||
public int TotalPages { get; set; } // WRONG: should be TotalPage
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ راهحل 1 - اصلاح نام:
|
||||
```csharp
|
||||
public class MetaDataDto
|
||||
{
|
||||
public int TotalPage { get; set; } // Match proto
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ راهحل 2 - Explicit mapping:
|
||||
```csharp
|
||||
config.NewConfig<MetaData, MetaDataDto>()
|
||||
.Map(dest => dest.TotalPages, src => src.TotalPage);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Nullable Types
|
||||
|
||||
### مشکل
|
||||
Nullable types در C# نیاز به handling خاص دارند.
|
||||
|
||||
### Proto با nullable:
|
||||
```protobuf
|
||||
google.protobuf.Int64Value nullable_id = 1;
|
||||
```
|
||||
|
||||
### ✅ در DTO:
|
||||
```csharp
|
||||
public long? NullableId { get; set; }
|
||||
```
|
||||
|
||||
### ✅ Mapping:
|
||||
```csharp
|
||||
.Map(dest => dest.NullableId,
|
||||
src => src.NullableId != null ? (long?)src.NullableId.Value : null)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Best Practices
|
||||
|
||||
### 1. همیشه Explicit Mapping برای Complex Types
|
||||
```csharp
|
||||
// بهتر است همیشه explicit باشد
|
||||
config.NewConfig<SourceType, DestType>()
|
||||
.Map(dest => dest.Prop1, src => src.Prop1)
|
||||
.Map(dest => dest.Prop2, src => src.Prop2);
|
||||
```
|
||||
|
||||
### 2. استفاده از MapWith برای Custom Logic
|
||||
```csharp
|
||||
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 ها
|
||||
```csharp
|
||||
[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 |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 منابع
|
||||
|
||||
- [Mapster Documentation](https://github.com/MapsterMapper/Mapster)
|
||||
- [Protobuf Well-Known Types](https://protobuf.dev/reference/csharp/api-docs/class/google/protobuf/well-known-types/)
|
||||
- [CHANGELOG-2025-12-27.md](../CHANGELOG-2025-12-27.md) - جزئیات بیشتر
|
||||
Reference in New Issue
Block a user