Mango.Nop.Libraries/Mango.Nop.Core/docs/DTOS.md

75 lines
3.9 KiB
Markdown

# DTO System
> Part of `Mango.Nop.Core`. See `Mango.Nop.Core/README.md` for project overview.
## Two Mapping Strategies
### Strategy 1: `ModelDtoBase<T>` (simple DTOs)
Used by: `CustomerDto`, `MgGenericAttributeDto`
```
ModelDtoBase → ModelDtoBase<Customer> → CustomerDto
```
- Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides
- `CreateMainEntity()` uses `Activator.CreateInstance<T>()`
- No LinqToDB associations
### Strategy 2: `MgEntityBase` + `IModelDtoBase<T>` (complex DTOs)
Used by: `MgOrderDto`, `MgOrderItemDto`, `MgStockQuantityHistoryDto`
```
BaseEntity → MgEntityBase → MgOrderDto<TOrderItemDto, TProductDto> : IModelDtoBase<Order>
```
- Uses `PropertyHelper.CopyPublicValueTypeProperties()` for bulk value-type copy
- LinqToDB `[Association]` navigation properties
- Generic type parameters for child DTOs
### Strategy 3: Entity inheritance (MgProductDto)
```
BaseEntity → MgEntityBase → MgProductDto : IMgProductDto
```
- No `IModelDtoBase<Product>` (entity mapping methods are commented out)
- Direct property declarations mirroring `Product` fields
## DTO Types
| Type | Generic params | Maps to entity | Key features |
|---|---|---|---|
| `ModelDtoBase` | — | — | Abstract base, `int Id` only |
| `ModelDtoBase<TMainEntity>` | `TMainEntity : BaseEntity` | `TMainEntity` | `CreateMainEntity()`, `CopyEntityValuesToDto()`, `CopyDtoValuesToEntity()` |
| `MgOrderDto<TOrderItemDto, TProductDto>` | `TOrderItemDto : IMgOrderItemDto<TProductDto>`, `TProductDto : IMgProductDto` | `Order` | Has `Customer`, `List<TOrderItemDto>`, `List<OrderNote>` navigations. Enum wrappers: `OrderStatus`, `ShippingStatus`, `PaymentStatus` |
| `MgOrderItemDto<TProductDto>` | `TProductDto : IMgProductDto` | `OrderItem` | Has `TProductDto?` navigation. Computed `ProductName` property |
| `MgProductDto` | — | *(commented out)* | Base product DTO. Name, Price, StockQuantity, dimensions. No entity mapping methods (currently commented out) |
| `MgGenericAttributeDto` | — | `GenericAttribute` | Inherits `GenericAttribute` directly (not `ModelDtoBase`). Full bidirectional mapping |
| `MgStockQuantityHistoryDto<TProductDto>` | `TProductDto : IMgProductDto` | `StockQuantityHistory` | Has `TProductDto` navigation. Mapping methods `NotImplementedException` (stub) |
| `CustomerDto` | — | `Customer` | `Username`, `Email`, `FirstName`, `LastName`, `FullName` computed. Uses `[AcBinarySerializable]` and LinqToDB `[Table]` |
## DTO Interfaces
| Interface | Extends | Purpose |
|---|---|---|
| `IModelDtoBase` | `IEntityInt`, `IModelDtoBaseEmpty` | Marker for all DTOs with `int Id` |
| `IModelDtoBase<TMainEntity>` | `IModelDtoBase` | Bidirectional mapping contract: `CreateMainEntity()`, `CopyDtoValuesToEntity()`, `CopyEntityValuesToDto()` |
| `IMgOrderDto<TOrderItemDto, TProductDto>` | `IEntityInt`, `ISoftDeletedEntity` | Order DTO contract with navigation properties and initialization methods |
| `IMgOrderItemDto<TProductDto>` | `IEntityInt` | Order item DTO contract with product navigation |
| `IMgProductDto` | `IEntityInt`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `ISoftDeletedEntity` | Product DTO contract |
## GenericAttribute Typed Access
`GenericAttributeExtensions` provides typed access to nopCommerce's polymorphic key-value store:
| Method | Signature | Purpose |
|---|---|---|
| `GetValueOrNull<TValue>` | `(IEnumerable<GenericAttribute>, string key) -> TValue?` | Get typed value, returns null if not found |
| `GetValueOrDefault<TValue>` | `(IEnumerable<GenericAttribute>, string key, TValue default) -> TValue` | Same with default fallback |
| `TryGetValue<TValue>` | `(IEnumerable<GenericAttribute>, string key, out TValue?) -> bool` | Try-pattern for GA value |
| `AddNewGenericAttribute` | `(ICollection<GenericAttribute>, ...) -> GenericAttribute` | Create and add new GA with UTC timestamp |
**Rule:** Always use these extension methods — never parse `GenericAttribute.Value` strings manually.