Comprehensive documentation overhaul and expansion

- Added detailed .md docs for Core, Data, and Services projects: DTOs, NopDependencies, repositories, transactions, services, and logging bridge
- Updated all project README.md files with metadata, documentation tables, inheritance diagrams, and clearer dependency/reference explanations
- Root README.md now includes a documentation map and improved project/type tables
- Expanded and clarified `.github/copilot-instructions.md` as the single source of truth for domain rules, patterns, and conventions
- Updated ARCHITECTURE.md, CONVENTIONS.md, and GLOSSARY.md to document DTO strategies, transaction and logging patterns, project boundaries, and AyCode integration
- Added CLAUDE.md for Claude-specific code authoring guidance
- All .csproj files now include new docs as non-compilable content
- Ensured clear separation of concerns and improved AI/tooling discoverability across the codebase
This commit is contained in:
Loretta 2026-03-30 10:26:35 +02:00
parent 2303e99f95
commit 5535011df9
18 changed files with 738 additions and 89 deletions

File diff suppressed because one or more lines are too long

10
CLAUDE.md Normal file
View File

@ -0,0 +1,10 @@
# Mango.Nop Libraries — Claude Code Instructions
Before writing any code, read these files:
1. `.github/copilot-instructions.md` — Domain rules (single source of truth)
2. `docs/GLOSSARY.md` — Entity/DTO, data access, and service terminology
3. The relevant project's `README.md` for folder-specific context
These libraries depend on **AyCode.Core** (DLL references — see HintPaths in .csproj for relative location). When a type is referenced but not defined here (e.g. `IEntityInt`, `IAcLoggerBase`, `AcBinarySerializer`, `ToonDescription`), look it up in AyCode.Core source. For core framework rules see `../AyCode.Core/CLAUDE.md`.
When modifying code, update the corresponding README.md if it becomes out of sync with the code.

View File

@ -45,4 +45,9 @@
<Folder Include="ExtendedModels\" /> <Folder Include="ExtendedModels\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="docs\DTOS.md" />
<None Include="docs\NOP_DEPENDENCIES.md" />
</ItemGroup>
</Project> </Project>

View File

@ -1,41 +1,92 @@
# Mango.Nop.Core # Mango.Nop.Core
Shared domain library containing entities, DTOs, interfaces, and nopCommerce entity mirrors. **net9.0**. @project {
type = "framework"
own-dep-projects = [
"AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)"
]
}
Shared domain library containing entities, DTOs, interfaces, and nopCommerce entity mirrors. **net9.0**. Zero nopCommerce runtime dependency.
## Documentation
| Document | Topic |
|---|---|
| `DTOS.md` | DTO system — two mapping strategies, all DTO types, GenericAttribute typed access |
| `NOP_DEPENDENCIES.md` | NopDependencies pattern — mirror copies, namespace rules, file list |
## Folder Structure ## Folder Structure
| Folder | Purpose | | Folder | Purpose |
|---|---| |---|---|
| `Dtos/` | DTO classes shared across consumers: `MgOrderDto`, `MgOrderItemDto`, `MgProductDto`, `MgGenericAttributeDto`, `MgStockQuantityHistoryDto`, `CustomerDto`, `ModelDtoBase` | | `Dtos/` | DTO classes shared across consumers |
| `Entities/` | Custom entities: `MgEntityBase`, `MgStockTaking`, `MgStockTakingItem` | | `Entities/` | Custom domain entities |
| `Extensions/` | Extension methods for nopCommerce `BaseEntity` collections and `GenericAttribute` | | `Extensions/` | Extension methods for `BaseEntity` collections and `GenericAttribute` |
| `Interfaces/` | DTO interfaces (`IMgOrderDto`, `IMgProductDto`, etc.), soft-delete interfaces, foreign key markers | | `Interfaces/` | DTO interfaces, soft-delete, foreign key markers |
| `Loggers/` | `ILogger` / `Logger` — logging abstraction | | `Loggers/` | `ILogger` / `Logger` — logging abstraction wrapping AyCode logger |
| `Models/` | Login request/response models (`MgLoginModelRequest`, `MgLoginModelResponse`) | | `Models/` | Login request/response models |
| `NopDependencies/` | Mirror copies of nopCommerce entity classes — `BaseEntity`, `Customer`, `Order`, `OrderItem`, `Product`, `GenericAttribute`, enums (`OrderStatus`, `PaymentStatus`, etc.) | | `NopDependencies/` | Mirror copies of nopCommerce entity classes (same namespaces as originals) |
| `Services/` | `IMgLockService` interface | | `Services/` | `IMgLockService` interface |
| `Utils/` | `CommonHelper2` — utility methods | | `Utils/` | `CommonHelper2`email validation, type conversion utilities |
## Key Types ## Entity Hierarchy
### DTOs (Dtos/) ```
- **`MgOrderDto`** — Order DTO used across SignalR communication Nop.Core.BaseEntity (NopDependencies/, implements IBaseEntity)
- **`MgOrderItemDto`** — Order item DTO +-- MgEntityBase (Entities/, implements IEntityInt from AyCode)
- **`MgProductDto`** — Product DTO with GenericAttribute-based properties +-- MgOrderDto<TOrderItemDto, TProductDto>
- **`MgGenericAttributeDto`** — GenericAttribute DTO +-- MgOrderItemDto<TProductDto>
- **`ModelDtoBase`** / `IModelDtoBase` — base class for all DTOs +-- MgProductDto
+-- MgStockQuantityHistoryDto<TProductDto>
+-- MgStockTaking<TStockTakingItem>
+-- MgStockTakingItem<TStockTaking, TProduct>
```
### nopCommerce Mirrors (NopDependencies/) ## Key Types (not in docs/)
Mirror copies of nopCommerce entities so that `Mango.Nop.Core` can be referenced without pulling in the full nopCommerce dependency chain. Includes:
- `BaseEntity` — root entity base
- `Customer`, `CustomerRole`, `Order`, `OrderItem`, `Product`, `GenericAttribute`, `StockQuantityHistory`
- Enums: `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, etc.
### Entities (Entities/) ### Entities
- **`MgEntityBase`** — base for all custom Mango entities
- **`MgStockTaking`**, **`MgStockTakingItem`** — stocktaking entities | Type | Key features |
|---|---|
| `MgEntityBase` | Inherits `BaseEntity`, implements `IEntityInt`. `ToString()` -> `"{TypeName}; Id: {Id}"` |
| `MgStockTaking<TStockTakingItem>` | `StartDateTime`, `IsClosed`, `List<TStockTakingItem>` navigation. Implements `ITimeStampInfo` |
| `MgStockTakingItem<TStockTaking, TProduct>` | `StockTakingId`, `ProductId`, `IsMeasured`, stock quantities. Navigations to parent and product |
### Extensions
| Method | Purpose |
|---|---|
| `UpdateBaseEntityCollection` | Add/update/remove entities in a list by Id match |
### Loggers
Extends AyCode logging infrastructure — for base types see `AyCode.Core/docs/LOGGING.md`.
| Type | Inherits | Purpose |
|---|---|---|
| `ILogger` | `IAcLoggerBase` | Mango-level logger interface |
| `ILogger<TCategory>` | `ILogger` | Generic category logger interface |
| `Logger` | `AcLoggerBase` | Logger implementation with `IAcLogWriterBase[]` writers |
| `Logger<TCategory>` | `Logger` | Typed logger — category name from `typeof(TCategory).Name` |
### Models
| Type | Purpose |
|---|---|
| `MgLoginModelRequest` | `Email` + `Password` for SignalR/API login |
| `MgLoginModelResponse` | `CustomerDto?` + `ErrorMessage`. Computed `IsSuccesLogin` |
### Other
| Type | Purpose |
|---|---|
| `NopCommonConst` | Abstract const class inheriting `AcConst` (placeholder) |
| `CommonHelper2` | Email validation, IP validation, type conversion (`To<T>`), string utilities |
## Dependencies ## Dependencies
- `linq2db` — data access
- `MessagePack` — binary serialization - `linq2db` — data access, `[Association]` / `[Table]` attributes
- `Microsoft.AspNetCore.Mvc.NewtonsoftJson` — JSON serialization - `Microsoft.AspNetCore.Mvc.NewtonsoftJson` — JSON serialization
- Binary serialization: `AcBinarySerializer` (see `AyCode.Core/docs/BINARY_FORMAT.md`). SignalR transport: `AcSignalR` (see `AyCode.Core/AyCode.Services/docs/SIGNALR.md`).
- AyCode DLL references: `AyCode.Interfaces`, `AyCode.Core`, `AyCode.Utils`, `AyCode.Entities` (types only, no runtime nopCommerce dependency)

View File

@ -0,0 +1,74 @@
# 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.

View File

@ -0,0 +1,46 @@
# NopDependencies Pattern
> Part of `Mango.Nop.Core`. See `Mango.Nop.Core/README.md` for project overview.
## Why
`Mango.Nop.Core` has **zero nopCommerce runtime dependency**. This allows it to be referenced by projects that don't have the full nopCommerce stack (e.g. Blazor/MAUI clients that only need DTOs and interfaces).
To achieve this, `NopDependencies/` contains **mirror copies** of nopCommerce entity classes with the **same namespace** as the originals:
```csharp
// In NopDependencies/BaseEntity.cs — same namespace as nopCommerce
namespace Nop.Core;
public abstract partial class BaseEntity : IBaseEntity
{
public int Id { get; set; }
}
```
At compile time, projects referencing only `Mango.Nop.Core` get these mirror types. Projects with the full nopCommerce stack get the real types — they are type-compatible because they share the same namespace and shape.
## Rules
- **DO NOT modify** files in `NopDependencies/` unless the nopCommerce version changes
- **DO NOT change namespaces** — they must match the original nopCommerce types exactly
- All mirror classes are `partial` — they can be extended but should not be modified directly
## File List
| File | Namespace | Type |
|---|---|---|
| `BaseEntity.cs` | `Nop.Core` | `BaseEntity` (abstract, `IBaseEntity`) + `IBaseEntity` interface |
| `Catalogs/Customer.cs` | `Nop.Core.Domain.Customers` | `Customer` |
| `Catalogs/CustomerRole.cs` | `Nop.Core.Domain.Customers` | `CustomerRole` |
| `Catalogs/Order.cs` | `Nop.Core.Domain.Orders` | `Order` |
| `Catalogs/OrderItem.cs` | `Nop.Core.Domain.Orders` | `OrderItem` |
| `Catalogs/OrderNote.cs` | `Nop.Core.Domain.Orders` | `OrderNote` |
| `Catalogs/Product.cs` | `Nop.Core.Domain.Catalog` | `Product` |
| `Catalogs/GenericAttribute.cs` | `Nop.Core.Domain.Common` | `GenericAttribute` |
| `Catalogs/StockQuantityHistory.cs` | `Nop.Core.Domain.Catalog` | `StockQuantityHistory` |
| `Catalogs/DiscountMapping.cs` | `Nop.Core.Domain.Catalog` | `DiscountMapping` |
| `Catalogs/DiscountProductMapping.cs` | `Nop.Core.Domain.Catalog` | `DiscountProductMapping` |
**Enums:** `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, `ManageInventoryMethod`, `BackorderMode`, `LowStockActivity`, `GiftCardType`, `RentalPricePeriod`, `RecurringProductCyclePeriod`, `DownloadActivationType`, `TaxDisplayType`, `VatNumberStatus`
**Interfaces:** `ISoftDeletedEntity`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `IDiscountSupported`

View File

@ -44,4 +44,10 @@
<HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Utils.dll</HintPath> <HintPath>..\..\..\..\..\..\Aycode\Source\AyCode.Core\AyCode.Services.Server\bin\FruitBank\$(Configuration)\net9.0\AyCode.Utils.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="docs\REPOSITORIES.md" />
<None Include="docs\TRANSACTIONS.md" />
</ItemGroup>
</Project> </Project>

View File

@ -1,7 +1,21 @@
# Mango.Nop.Data # Mango.Nop.Data
@project {
type = "framework"
own-dep-projects = [
"AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)"
]
}
Data access layer with repository base classes and DB context abstractions. **net9.0**. Data access layer with repository base classes and DB context abstractions. **net9.0**.
## Documentation
| Document | Topic |
|---|---|
| `REPOSITORIES.md` | MgDbTableBase, MgDtoDbTableBase — CRUD hooks, timestamps, delete rules, event bridging |
| `TRANSACTIONS.md` | MgDbContextBase — 4 transaction methods, lock strategy, callback contract |
## Folder Structure ## Folder Structure
| Folder | Purpose | | Folder | Purpose |
@ -9,15 +23,38 @@ Data access layer with repository base classes and DB context abstractions. **ne
| `Interfaces/` | Repository interfaces: `IMgDalBase`, `IMgDbContextBase`, `IMgDbTableBase` | | `Interfaces/` | Repository interfaces: `IMgDalBase`, `IMgDbContextBase`, `IMgDbTableBase` |
| `Repositories/` | Repository base implementations: `MgDalBase`, `MgDbContextBase`, `MgDbTableBase`, `MgDtoDbTableBase` | | `Repositories/` | Repository base implementations: `MgDalBase`, `MgDbContextBase`, `MgDbTableBase`, `MgDtoDbTableBase` |
## Key Types ## Inheritance Chains
- **`MgDalBase`** — Data Access Layer base class ### Repository hierarchy
- **`MgDbContextBase`** — Database context base for nopCommerce integration ```
- **`MgDbTableBase`** — Table-level repository base (entity → DB operations) nopCommerce EntityRepository<TEntity> (Nop.Data)
- **`MgDtoDbTableBase`** — DTO-aware table base — handles DTO ↔ entity mapping +-- MgDbTableBase<TEntity>
+-- MgDtoDbTableBase<TDtoEntity, TMainEntity>
```
### Context hierarchy
```
MgDbContextBase (abstract, implements IMgDbContextBase)
+-- [Consumer DbContexts in plugins]
```
### DAL hierarchy
```
MgDalBase<TDbContext> (implements IMgDalBase<TDbContext>)
+-- [Consumer DALs in plugins]
```
## Interfaces
| Interface | Purpose |
|---|---|
| `IMgDbTableBase` | Marker interface for repository classes |
| `IMgDbContextBase` | Contract: `Logger`, `DataProvider`, `Orders`, `Products`, 4 transaction methods |
| `IMgDalBase` | `Name` property |
| `IMgDalBase<TDbContext>` | `Context`, `MutextLock` — typed access to DB context |
## Dependencies ## Dependencies
- `Mango.Nop.Core` (ProjectReference) - `Mango.Nop.Core` (ProjectReference)
- `Nop.Core`, `Nop.Data` (nopCommerce ProjectReferences) - `Nop.Core`, `Nop.Data` (nopCommerce ProjectReferences)
- `MessagePack`, `Microsoft.AspNetCore.Mvc.NewtonsoftJson` - `Microsoft.AspNetCore.Mvc.NewtonsoftJson`

View File

@ -0,0 +1,42 @@
# Repository Pattern
> Part of `Mango.Nop.Data`. See `Mango.Nop.Data/README.md` for project overview.
> For transaction patterns see `docs/TRANSACTIONS.md`.
## MgDbTableBase\<TEntity\>
Repository base wrapping nopCommerce `EntityRepository<TEntity>`.
**Constructor:**
```csharp
MgDbTableBase(IEventPublisher, INopDataProvider, IShortTermCacheManager, IStaticCacheManager, AppSettings)
```
### Features
| Feature | Detail |
|---|---|
| `GetAll()` | Returns `IQueryable<TEntity>` from `Table` property |
| Automatic timestamps | `ITimeStampCreated.Created` set on insert; `ITimeStampModified.Modified` set on insert/update |
| CRUD hooks | `OnInsert(entity)`, `OnUpdate(entity)`, `OnDelete(entity)` — virtual, overridable |
| Cache clear | `OnUpdate` clears `IStaticCacheManager` (currently clears all — TODO noted in code) |
| All CRUD overrides | Overrides all sync/async `Insert`, `Update`, `Delete` methods to call hooks before `base.*` |
| `DeleteAsync(int entityId)` | Convenience: load by id then delete |
| `DeleteAsync(predicate, bool publishEvent)` | When `publishEvent=true`, loads entities first then deletes with events |
## MgDtoDbTableBase\<TDtoEntity, TMainEntity\>
DTO-aware repository for when the DTO entity (`TDtoEntity`) maps to a different main nopCommerce entity (`TMainEntity`). This is needed because LinqToDB tables are registered for DTOs, but nopCommerce events must fire on the main entity type.
### Features
| Feature | Detail |
|---|---|
| `GetMainEntityById(id)` | Loads `TMainEntity` from `INopDataProvider.GetTable<TMainEntity>()` |
| `DeleteMainEntityById(id)` | Deletes the **main entity** (not the DTO) and publishes `EntityDeletedEvent<TMainEntity>` |
| Delete overrides | **All Delete methods throw** — forces callers to use `DeleteMainEntityById()` instead |
| Event bridging | `EntityInsertedEvent<TDtoEntity>` -> loads main entity -> publishes `EntityInsertedEvent<TMainEntity>` |
| Event bridging | `EntityUpdatedEvent<TDtoEntity>` -> loads main entity -> publishes `EntityUpdatedEvent<TMainEntity>` |
| Event bridging | `EntityDeletedEvent<TDtoEntity>` -> **throws** (must use `DeleteMainEntityById`) |
**Critical rule:** Never call `Delete` on a `MgDtoDbTableBase` repository directly. Always use `DeleteMainEntityById(int id)`.

View File

@ -0,0 +1,53 @@
# Transaction Pattern
> Part of `Mango.Nop.Data`. See `Mango.Nop.Data/README.md` for project overview.
> For repository base classes see `docs/REPOSITORIES.md`.
## MgDbContextBase
Abstract database context base. NOT an EF Core DbContext — wraps `INopDataProvider` and `IRepository<T>` nopCommerce repos.
**Constructor:**
```csharp
MgDbContextBase(IRepository<Product>, IRepository<Order>, IRepository<OrderItem>, INopDataProvider, IMgLockService, ILogger)
```
### Standard Repositories
| Property | Type |
|---|---|
| `Orders` | `IRepository<Order>` |
| `OrderItems` | `IRepository<OrderItem>` |
| `Products` | `IRepository<Product>` |
| `DataProvider` | `INopDataProvider` (LinqToDB raw queries) |
| `Logger` | Mango `ILogger` |
| `LockService` | `IMgLockService` (global `SemaphoreSlim`) |
### 4 Transaction Methods
| Method | Lock | Async |
|---|---|---|
| `Transaction(callback)` | No | No |
| `TransactionSafe(callback)` | `SemaphoreSlim` | No |
| `TransactionAsync(callback)` | No | Yes (thread pool) |
| `TransactionSafeAsync(callback)` | `SemaphoreSlim` | Yes (thread pool) |
**Callback contract:** `Func<TransactionScope, (Task<)bool(>)>` — return `true` to commit (`Complete()`), `false` to rollback.
**Isolation level:** `ReadCommitted`
**Error handling:** Catches exceptions, logs, returns `false` (unless `throwException = true`).
**TransactionSafe variants:** Use `LockService.SemaphoreSlim` for global serialization. Use for order creation, stock adjustment — any operation where concurrent modifications would corrupt data.
**Async variants:** Run on thread pool via `TaskHelper.ToThreadPoolTask()`.
## MgDalBase\<TDbContext\>
Data Access Layer orchestrator. Thin wrapper exposing:
| Property | Type | Purpose |
|---|---|---|
| `Name` | `string` | DAL instance name |
| `Context` | `TDbContext` | The DB context (typed) |
| `MutextLock` | `Mutex` | Cross-process locking |

View File

@ -47,4 +47,10 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="docs\SERVICES.md" />
<None Include="docs\LOGGING_BRIDGE.md" />
</ItemGroup>
</Project> </Project>

View File

@ -1,17 +1,27 @@
# Mango.Nop.Services # Mango.Nop.Services
Service base classes for nopCommerce plugin development — background tasks, session management, events, locking. **net9.0**. @project {
type = "framework"
own-dep-projects = [
"AyCode.Core, AyCode.Core.Server, AyCode.Entities, AyCode.Entities.Server, AyCode.Interfaces, AyCode.Interfaces.Server, AyCode.Utils (in AyCode.Core repo)"
]
}
## Key Types Service base classes for nopCommerce plugin development — background tasks, session management, events, locking, logging. **net9.0**.
| Type | Purpose | ## Documentation
| Document | Topic |
|---|---| |---|---|
| `MgBackgroundServiceBase` / `IMgBackgroundService` | Base for hosted background services in nopCommerce | | `SERVICES.md` | MgBackgroundServiceBase, MgSessionServiceBase, MgEventConsumerBase, MgLockServiceBase |
| `MgSessionServiceBase` / `IMgSessionService` | Session management base — user session state | | `LOGGING_BRIDGE.md` | NopLogWriter — AyCode-to-nopCommerce log bridge, TransactionScope(Suppress) |
| `MgSessionItemBase` / `IMgSessionItem` | Individual session item base |
| `MgEventConsumerBase` | Base for nopCommerce event consumers (entity insert/update/delete) | ## Folder Structure
| `MgLockServiceBase` | Distributed lock service base |
| `NopLogWriter` | Logging bridge — writes to nopCommerce log system | | Folder | Purpose |
|---|---|
| `Loggers/` | `NopLogWriter`, `NopLoggerMsSqlNopDataProvider` — AyCode -> nopCommerce log bridge |
| *(root)* | `MgBackgroundServiceBase`, `MgSessionServiceBase`, `MgEventConsumerBase`, `MgLockServiceBase`, interfaces |
## Dependencies ## Dependencies

View File

@ -0,0 +1,40 @@
# NopLogWriter — Logging Bridge
> Part of `Mango.Nop.Services`. See `Mango.Nop.Services/README.md` for project overview.
> For AyCode base logging types (`AcLogItemWriterBase`, `AcLogItem`, log levels) see `AyCode.Core/docs/LOGGING.md`.
## Overview
Bridges AyCode logging to nopCommerce's `Nop.Core.Domain.Logging.Log` table via direct DB insert.
## NopLogWriter
| Feature | Detail |
|---|---|
| Inherits | `AcLogItemWriterBase<AcLogItem>` (AyCode.Core) |
| Log level mapping | AyCode `Detail/Trace/Debug/Info` -> nopCommerce `Information`; `Suggest/Warning` -> `Warning`; `Error` -> `Error` |
| Direct DB insert | Uses `NopLoggerMsSqlNopDataProvider` with `TransactionScope(Suppress)` to avoid transaction conflicts |
**Constructor:** `(INopLoggerMsSqlNopDataProvider, CommonSettings, CustomerSettings, IWebHelper, Nop.Services.Logging.ILogger, string? categoryName)`
## NopLoggerMsSqlNopDataProvider
Extends `MsSqlNopDataProvider`. Provides isolated DB access for log writes.
| Method | Purpose |
|---|---|
| `InsertLogItem<T>()` | Sync insert with own `TransactionScope(Suppress, ReadUncommitted)` |
| `InsertLogItemAsync<T>()` | Async insert with own `TransactionScope(Suppress, ReadUncommitted)` |
The `TransactionScope(Suppress)` is critical — without it, log writes would participate in the caller's transaction, causing nesting conflicts and potential deadlocks.
## Integration Point
```
Application code -> Mango.Nop.Core.Loggers.ILogger (extends IAcLoggerBase)
-> Logger<TCategory> (extends AcLoggerBase, delegates to IAcLogWriterBase[])
-> NopLogWriter -> Nop Log table (direct SQL insert)
-> [Other AyCode log writers: console, file, SignalR, etc.]
```
**Exception:** `MgBackgroundServiceBase` uses `Nop.Services.Logging.ILogger` directly (nopCommerce logger), not the Mango wrapper.

View File

@ -0,0 +1,71 @@
# Service Base Classes
> Part of `Mango.Nop.Services`. See `Mango.Nop.Services/README.md` for project overview.
> For logging bridge see `docs/LOGGING_BRIDGE.md`.
## MgBackgroundServiceBase
Abstract hosted background service. Inherits `Microsoft.Extensions.Hosting.BackgroundService`.
**Constructor:** `(ILogger, IServiceProvider, int executeIntervalMs)`
| Feature | Detail |
|---|---|
| Loop pattern | `ExecuteAsync` loops: `Task.Delay(ExecuteIntervalMs)` -> `OnExecuteAsync()`, with pause support |
| `OnExecuteAsync(CancellationToken)` | Abstract — subclass implements the actual work |
| `Pause(bool)` | Pauses/resumes the loop without stopping the service |
| `ExecuteIntervalMs` | Configurable interval between iterations |
| Exception handling | Catches and logs errors per iteration, never crashes the loop |
| Logging | Uses nopCommerce `Nop.Services.Logging.ILogger` (**not** `Mango.Nop.Core.Loggers.ILogger`) |
**Interface:** `IMgBackgroundService : IHostedService, IDisposable`
## MgSessionServiceBase\<TSessionItem\>
In-memory session management using `ConcurrentDictionary<string, TSessionItem>`.
| Method | Signature | Purpose |
|---|---|---|
| `GetOrCreateSessionItem` | `(string sessionId) -> TSessionItem?` | Get existing or create new via `Activator.CreateInstance` |
| `TryAddSessionItem` | `(TSessionItem) -> bool` | Add if not exists, throws if duplicate |
| `TryGetSessionItem` | `(string sessionId, out TSessionItem) -> bool` | Try-pattern lookup |
| `TryRemoveSessionItem` | `(string sessionId, out TSessionItem) -> bool` | Remove and return |
| `TryGetSessionItemBySignlaRConnectionId` | `(string connectionId, out TSessionItem?) -> bool` | Find session by SignalR connection |
| `Count` | `() -> int` | Active session count |
**Interface:** `IMgSessionService<TSessionItem> where TSessionItem : IMgSessionItem`
## MgSessionItemBase
Base session item. Primary constructor: `(string sessionKey)`.
| Property | Type | Purpose |
|---|---|---|
| `SessionId` | `string` | Unique session identifier |
| `SignaRConnectionId` | `string?` | Associated SignalR connection ID |
| `RequestCount` | `int` | Request counter |
**Interface:** `IMgSessionItem``SessionId`, `SignaRConnectionId`, `RequestCount`
## MgEventConsumerBase
Abstract nopCommerce event consumer. Subscribes to:
| Event | Handler |
|---|---|
| `EntityUpdatedEvent<Product>` | `HandleEventAsync(...)` — virtual, empty default |
| `EntityInsertedEvent<Product>` | `HandleEventAsync(...)` — virtual, empty default |
| `CustomerRegisteredEvent` | `HandleEventAsync(...)` — virtual, empty default |
| `OrderPlacedEvent` | `HandleEventAsync(...)` — virtual, empty default |
| `PageRenderingEvent` | `HandleEventAsync(...)` — virtual, empty default |
| `ProductSearchEvent` | `HandleEventAsync(...)` — virtual, empty default |
Built-in helper: `CheckAndUpdateProductManageInventoryMethodToManageStock(Product)` — ensures product has `ManageStock` inventory method.
**Constructor:** `(IMgDbContextBase ctx, IHttpContextAccessor, IEnumerable<IAcLogWriterBase> logWriters)`
## MgLockServiceBase
Simple in-process lock using `SemaphoreSlim(1)`. Implements `IMgLockService` (defined in `Mango.Nop.Core.Services`).
Used by `MgDbContextBase.TransactionSafe*` variants for global serialization.

View File

@ -9,17 +9,43 @@ Shared nopCommerce extension libraries providing domain entities, DTOs, data acc
| Project | Purpose | Key Types | | Project | Purpose | Key Types |
|---|---|---| |---|---|---|
| [Mango.Nop.Core](Mango.Nop.Core/README.md) | Domain entities, DTOs, interfaces, nopCommerce entity mirrors | `MgOrderDto`, `MgProductDto`, `MgEntityBase`, `GenericAttribute`, `BaseEntity` | | `Mango.Nop.Core` | Domain entities, DTOs, interfaces, nopCommerce entity mirrors (zero nopCommerce runtime dep) | `MgEntityBase`, `ModelDtoBase<T>`, `MgOrderDto<,>`, `MgOrderItemDto<>`, `MgProductDto`, `CustomerDto`, `MgGenericAttributeDto`, `MgStockTaking<>`, `GenericAttributeExtensions` |
| [Mango.Nop.Data](Mango.Nop.Data/README.md) | Data access layer — repository base classes, DB context | `MgDalBase`, `MgDbContextBase`, `MgDbTableBase`, `MgDtoDbTableBase` | | `Mango.Nop.Data` | Data access layer — repository base classes, DB context, transactions | `MgDbTableBase<T>`, `MgDtoDbTableBase<,>`, `MgDbContextBase`, `MgDalBase<T>` |
| [Mango.Nop.Services](Mango.Nop.Services/README.md) | Service base classes — background services, session, events, locking | `MgBackgroundServiceBase`, `MgSessionServiceBase`, `MgEventConsumerBase`, `NopLogWriter` | | `Mango.Nop.Services` | Service base classes — background services, session, events, locking, logging | `MgBackgroundServiceBase`, `MgSessionServiceBase<T>`, `MgEventConsumerBase`, `MgLockServiceBase`, `NopLogWriter` |
## Dependency Graph ## Dependency Graph
See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for full dependency graph and project roles. See `docs/ARCHITECTURE.md` for full dependency graph, project roles, DTO mapping strategies, and transaction patterns.
`Mango.Nop.Core` has zero nopCommerce runtime dependency (uses mirror copies in `NopDependencies/`). `Mango.Nop.Data` and `.Services` depend on nopCommerce. ```
AyCode.Core (DLL references)
Mango.Nop.Core (net9.0, zero nopCommerce runtime dependency)
Mango.Nop.Data (net9.0) → Nop.Core, Nop.Data
Mango.Nop.Services (net9.0) → Nop.Core, Nop.Data, Nop.Services, Nop.Web.Framework
```
## Reference Modes ## Reference Modes
- **Full stack** (ProjectReference, all 3 libraries) — for nopCommerce plugins that need entity access, data layer, and services. - **Full stack** (ProjectReference, all 3 libraries) — for nopCommerce plugins that need entity access, data layer, and services.
- **Types only** (DLL reference, `Mango.Nop.Core` only) — for projects that only need DTOs, entities, and interfaces without the nopCommerce runtime dependency. - **Types only** (DLL reference, `Mango.Nop.Core` only) — for projects that only need DTOs, entities, and interfaces without the nopCommerce runtime dependency.
## Documentation Map
| File | Purpose |
|---|---|
| `.github/copilot-instructions.md` | **Domain rules** — single source of truth for all conventions |
| `docs/ARCHITECTURE.md` | Dependency graph, project roles, DTO strategies, transaction patterns, logging architecture |
| `docs/CONVENTIONS.md` | Naming, patterns, project boundaries, AyCode integration points |
| `docs/GLOSSARY.md` | Term definitions — entity/DTO system, data access, services, AyCode types |
| `Mango.Nop.Core/README.md` | Core project overview — entity hierarchy, loggers, extensions, models |
| `Mango.Nop.Core/docs/DTOS.md` | DTO system — two mapping strategies, all DTO types, GenericAttribute access |
| `Mango.Nop.Core/docs/NOP_DEPENDENCIES.md` | NopDependencies pattern — mirror copies, namespace rules, file list |
| `Mango.Nop.Data/README.md` | Data project overview — repository and context hierarchy, interfaces |
| `Mango.Nop.Data/docs/REPOSITORIES.md` | MgDbTableBase, MgDtoDbTableBase — CRUD hooks, timestamps, delete rules |
| `Mango.Nop.Data/docs/TRANSACTIONS.md` | MgDbContextBase — 4 transaction methods, lock strategy, callback contract |
| `Mango.Nop.Services/README.md` | Services project overview |
| `Mango.Nop.Services/docs/SERVICES.md` | MgBackgroundServiceBase, MgSessionServiceBase, MgEventConsumerBase, MgLockServiceBase |
| `Mango.Nop.Services/docs/LOGGING_BRIDGE.md` | NopLogWriter — AyCode-to-nopCommerce log bridge |

View File

@ -18,28 +18,28 @@ Mango.Nop.Services (net9.0) → Nop.Core, Nop.Data, Nop.Services, Nop.Web.Framew
### Mango.Nop.Core — Domain Layer ### Mango.Nop.Core — Domain Layer
Zero nopCommerce runtime dependency. Contains: Zero nopCommerce runtime dependency. Contains:
- **DTOs** (`Dtos/`) — `ModelDtoBase<TMainEntity>` with bidirectional entity mapping (`CopyEntityValuesToDto`, `CreateMainEntity`) - **DTOs** (`Dtos/`) — `ModelDtoBase<TMainEntity>` with bidirectional entity mapping (`CopyEntityValuesToDto`, `CopyDtoValuesToEntity`, `CreateMainEntity`)
- **Entities** (`Entities/`) — `MgEntityBase` (inherits `BaseEntity`, implements `IEntityInt`) - **Entities** (`Entities/`) — `MgEntityBase` (inherits `BaseEntity`, implements `IEntityInt`)
- **NopDependencies** (`NopDependencies/`) — mirror copies of nopCommerce entities (`BaseEntity`, `Customer`, `Order`, `Product`, `GenericAttribute`, enums). Allows referencing without full nopCommerce dependency chain. - **NopDependencies** (`NopDependencies/`) — mirror copies of nopCommerce entities (`BaseEntity`, `Customer`, `Order`, `Product`, `GenericAttribute`, enums). Allows referencing without full nopCommerce dependency chain.
- **Extensions** (`Extensions/`) — `GenericAttributeExtensions` for typed attribute access, `CollectionExtensionsNopBaseEntity` for collection operations - **Extensions** (`Extensions/`) — `GenericAttributeExtensions` for typed attribute access, `CollectionExtensionsNopBaseEntity` for collection operations
- **Interfaces** (`Interfaces/`) — DTO interfaces, soft-delete, foreign key markers - **Interfaces** (`Interfaces/`) — DTO interfaces, soft-delete, foreign key markers
- **Models** (`Models/`) — Login request/response - **Models** (`Models/`) — Login request/response
- **Loggers** (`Loggers/`) — `ILogger` abstraction - **Loggers** (`Loggers/`) — `ILogger` abstraction wrapping AyCode `IAcLoggerBase`
### Mango.Nop.Data — Data Access Layer ### Mango.Nop.Data — Data Access Layer
Wraps nopCommerce data infrastructure with custom base classes: Wraps nopCommerce data infrastructure with custom base classes:
- **`MgDbTableBase<TEntity>`** — extends nopCommerce `EntityRepository<TEntity>`, adds `GetAll()`, timestamp handling (`ITimeStampCreated`, `ITimeStampModified`), event publishing - **`MgDbTableBase<TEntity>`** — extends nopCommerce `EntityRepository<TEntity>`, adds `GetAll()`, timestamp handling (`ITimeStampCreated`, `ITimeStampModified`), event publishing
- **`MgDtoDbTableBase`** — DTO-aware repository with entity ↔ DTO mapping - **`MgDtoDbTableBase<TDtoEntity, TMainEntity>`** — DTO-aware repository. **Critical: Delete operations throw — must use `DeleteMainEntityById()`.** Event bridging: DTO events → main entity events
- **`MgDbContextBase`** — EF Core context base for custom tables - **`MgDbContextBase`** — DB context base with `Transaction/TransactionSafe/TransactionAsync/TransactionSafeAsync` methods. TransactionSafe variants use global `SemaphoreSlim` lock
- **`MgDalBase`** — Data Access Layer orchestrator - **`MgDalBase<TDbContext>`** — Data Access Layer orchestrator with `Context`, `Name`, `MutextLock`
### Mango.Nop.Services — Service Layer ### Mango.Nop.Services — Service Layer
Service base classes for nopCommerce plugin development: Service base classes for nopCommerce plugin development:
- **`MgBackgroundServiceBase`** — hosted background task base - **`MgBackgroundServiceBase`** — hosted background task with configurable interval, pause support, per-iteration error handling
- **`MgSessionServiceBase`** / `MgSessionItemBase` — session management - **`MgSessionServiceBase<TSessionItem>`** / `MgSessionItemBase`in-memory session management via `ConcurrentDictionary`, SignalR connection tracking
- **`MgEventConsumerBase`** — nopCommerce entity event handler base - **`MgEventConsumerBase`** — nopCommerce entity event handler base (Product insert/update, CustomerRegistered, OrderPlaced, PageRendering, ProductSearch)
- **`MgLockServiceBase`** — distributed locking - **`MgLockServiceBase`** — `SemaphoreSlim(1)` lock wrapper
- **`NopLogWriter`** — logging bridge to nopCommerce log system - **`NopLogWriter`** — logging bridge: AyCode log levels → nopCommerce `Log` table via direct DB insert with `TransactionScope(Suppress)`
## NopDependencies Pattern ## NopDependencies Pattern
@ -56,6 +56,70 @@ public abstract partial class BaseEntity : IBaseEntity
This allows `Mango.Nop.Core` to be referenced by projects that don't have a direct nopCommerce dependency (e.g., Blazor/MAUI clients), while maintaining type compatibility with the real nopCommerce entities at the server level. This allows `Mango.Nop.Core` to be referenced by projects that don't have a direct nopCommerce dependency (e.g., Blazor/MAUI clients), while maintaining type compatibility with the real nopCommerce entities at the server level.
**Files in NopDependencies:**
- `BaseEntity.cs` + `IBaseEntity` — root entity base (`Nop.Core`)
- `Catalogs/``Customer`, `CustomerRole`, `Order`, `OrderItem`, `OrderNote`, `Product`, `GenericAttribute`, `StockQuantityHistory`, `DiscountMapping`, `DiscountProductMapping`
- Enums — `OrderStatus`, `PaymentStatus`, `ShippingStatus`, `ProductType`, `ManageInventoryMethod`, `BackorderMode`, `LowStockActivity`, `GiftCardType`, `RentalPricePeriod`, `RecurringProductCyclePeriod`, `DownloadActivationType`, `TaxDisplayType`, `VatNumberStatus`
- Interfaces — `ISoftDeletedEntity`, `ILocalizedEntity`, `ISlugSupported`, `IAclSupported`, `IStoreMappingSupported`, `IDiscountSupported`
## DTO Mapping Strategies
Two patterns coexist:
### Strategy 1: `ModelDtoBase<T>` (simple DTOs)
Used by: `CustomerDto`, `MgGenericAttributeDto`
```
ModelDtoBase → ModelDtoBase<Customer> → CustomerDto
```
- Manual `CopyEntityValuesToDto`/`CopyDtoValuesToEntity` overrides
- 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 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
## Transaction Pattern
`MgDbContextBase` provides 4 transaction methods:
| Method | Lock | Async |
|---|---|---|
| `Transaction(callback)` | No | No |
| `TransactionSafe(callback)` | `SemaphoreSlim` | No |
| `TransactionAsync(callback)` | No | Yes (thread pool) |
| `TransactionSafeAsync(callback)` | `SemaphoreSlim` | Yes (thread pool) |
**Callback contract:** `Func<TransactionScope, (Task<)bool(>)>` — return `true` to commit, `false` to rollback.
**Isolation level:** `ReadCommitted`
**Error handling:** Catches exceptions, logs, returns `false` (unless `throwException = true`)
## Logging Architecture
Base logging infrastructure (`IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase`, `AcLogItem`) is defined in AyCode.Core — see `AyCode.Core/docs/LOGGING.md` for base types, log levels, and writer contracts.
This layer provides the nopCommerce-specific bridge:
```
Application code → Mango.Nop.Core.Loggers.ILogger (extends IAcLoggerBase)
→ Logger<TCategory> (extends AcLoggerBase, delegates to IAcLogWriterBase[])
→ NopLogWriter (in Services, extends AcLogItemWriterBase<AcLogItem>) → Nop Log table (direct SQL insert)
→ [Other AyCode log writers: console, file, SignalR, etc. — see AyCode.Core/docs/LOGGING.md]
```
**Important:** `MgBackgroundServiceBase` uses nopCommerce's `Nop.Services.Logging.ILogger` directly (not the Mango wrapper). Other services use `Mango.Nop.Core.Loggers.ILogger`.
## Reference Modes ## Reference Modes
``` ```

View File

@ -7,17 +7,79 @@
- **`Dto` suffix** for DTOs wrapping nopCommerce entities: `MgOrderDto`, `MgProductDto`, `MgOrderItemDto`. - **`Dto` suffix** for DTOs wrapping nopCommerce entities: `MgOrderDto`, `MgProductDto`, `MgOrderItemDto`.
- **`DbTable` suffix** for repository classes: `MgDbTableBase`, `MgDtoDbTableBase`. - **`DbTable` suffix** for repository classes: `MgDbTableBase`, `MgDtoDbTableBase`.
- **`Base` suffix** for abstract base classes: `MgEntityBase`, `ModelDtoBase`, `MgBackgroundServiceBase`. - **`Base` suffix** for abstract base classes: `MgEntityBase`, `ModelDtoBase`, `MgBackgroundServiceBase`.
- **`Nop` prefix** for nopCommerce bridge types: `NopLogWriter`, `NopLoggerMsSqlNopDataProvider`, `NopCommonConst`.
## Patterns ## Patterns
- **DTO bidirectional mapping**`ModelDtoBase<TMainEntity>` provides `CopyEntityValuesToDto(entity)` and `CreateMainEntity()`. Override both in concrete DTOs. ### DTO Bidirectional Mapping
- **NopDependencies mirror** — entity classes in `NopDependencies/` use the **same namespace** as the original nopCommerce types. Do not change namespaces. - `ModelDtoBase<TMainEntity>` provides `CopyEntityValuesToDto(entity)`, `CopyDtoValuesToEntity(entity)`, and `CreateMainEntity()`. Override all three in concrete DTOs.
- **GenericAttribute typed access** — use `GenericAttributeExtensions.GetValueOrDefault<T>()` / `GetValueOrNull<T>()` instead of raw string parsing. - Simple DTOs (e.g. `CustomerDto`) — manual property-by-property copy in overrides.
- **Repository base chain**`MgDbTableBase<TEntity>``EntityRepository<TEntity>` (nopCommerce). Override virtual methods, don't replace the chain. - Complex DTOs (e.g. `MgOrderDto`) — use `PropertyHelper.CopyPublicValueTypeProperties(source, target)` for automatic value-type property copy, then manually set reference/navigation properties.
- **Timestamp interfaces** — entities implementing `ITimeStampCreated` / `ITimeStampModified` get automatic timestamp management in `MgDbTableBase`.
### LinqToDB Associations
- DTOs with navigation properties use `[Association(ThisKey, OtherKey, CanBeNull)]` from LinqToDB.
- `ThisKey` = local FK property name, `OtherKey` = target entity property name (typically `Id` or via interface member like `nameof(IMgProductDto.Id)`).
### NopDependencies Mirror
- Entity classes in `NopDependencies/` use the **same namespace** as the original nopCommerce types. Do not change namespaces.
- All mirror classes are `partial` — they can be extended but should not be modified directly.
### GenericAttribute Typed Access
- Use `GenericAttributeExtensions.GetValueOrDefault<T>()` / `GetValueOrNull<T>()` / `TryGetValue<T>()` instead of raw string parsing.
- Use `AddNewGenericAttribute()` to create new attributes with automatic UTC timestamp.
### Repository Base Chain
- `MgDbTableBase<TEntity>``EntityRepository<TEntity>` (nopCommerce). Override virtual `OnInsert`/`OnUpdate`/`OnDelete` hooks, don't replace the chain.
- `MgDtoDbTableBase<TDtoEntity, TMainEntity>``MgDbTableBase<TDtoEntity>`. **Never call Delete directly** — always use `DeleteMainEntityById(int id)`.
### Timestamp Interfaces
- Entities implementing `ITimeStampCreated` get `Created = DateTime.UtcNow` on insert.
- Entities implementing `ITimeStampModified` get `Modified = DateTime.UtcNow` on insert and update.
- `ITimeStampInfo` combines both (`Creator`, `Created`, `Modified`).
### Transaction Pattern
- Use `MgDbContextBase.TransactionSafeAsync()` for operations needing global lock (order creation, stock adjustment).
- Callback returns `bool``true` commits, `false` rolls back.
- Exceptions are caught and logged by default (return `false`). Pass `throwException: true` only for fail-fast scenarios.
### Event Consumer Pattern
- Inherit `MgEventConsumerBase` and override relevant `HandleEventAsync` methods.
- Base class handles DI of `IMgDbContextBase`, `IHttpContextAccessor`, and `IAcLogWriterBase[]`.
- Base provides `CheckAndUpdateProductManageInventoryMethodToManageStock(Product)` helper.
### Session Pattern
- Inherit `MgSessionServiceBase<TSessionItem>` and `MgSessionItemBase`.
- Sessions stored in `ConcurrentDictionary<string, TSessionItem>` — thread-safe, in-memory only.
- Session items track `SessionId`, `SignaRConnectionId`, `RequestCount`.
### Logging
- Base logging infrastructure (`IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase`) — see `AyCode.Core/docs/LOGGING.md`.
- Services create loggers via `new Logger<TCategory>(logWriters.ToArray())` in constructor.
- `logWriters` injected as `IEnumerable<IAcLogWriterBase>` — typically contains `NopLogWriter` + other writers.
- **Exception:** `MgBackgroundServiceBase` uses `Nop.Services.Logging.ILogger` directly (nopCommerce logger), not the Mango wrapper.
### Serialization Attributes
- `[ToonDescription(...)]` — AyCode metadata for AI/doc tooling. `Purpose`, `BusinessRule`, `TypeRelation`, `RelatedTypes` properties.
- `[AcBinarySerializable(false, true, false, true)]` — AcBinarySerializer config (see `AyCode.Core/docs/BINARY_FORMAT.md`). Parameters control serialization behavior for AcSignalR transport (see `AyCode.Core/AyCode.Services/docs/SIGNALR.md`).
- LinqToDB `[Table(Name = "...")]` and `[Association(...)]` — DB mapping.
## Project Boundaries ## Project Boundaries
- `Mango.Nop.Core` — NO nopCommerce runtime dependency. Only NopDependencies mirrors. - `Mango.Nop.Core` — NO nopCommerce runtime dependency. Only NopDependencies mirrors. No `Nop.Data`, `Nop.Services` references.
- `Mango.Nop.Data` — depends on nopCommerce data layer (`Nop.Core`, `Nop.Data`). - `Mango.Nop.Data` — depends on nopCommerce data layer (`Nop.Core`, `Nop.Data`). No `Nop.Services` reference.
- `Mango.Nop.Services` — depends on full nopCommerce stack (includes `Nop.Services`, `Nop.Web.Framework`). - `Mango.Nop.Services` — depends on full nopCommerce stack (includes `Nop.Services`, `Nop.Web.Framework`).
## AyCode Integration Points
Key AyCode types used across these libraries:
- `IEntityInt` (AyCode.Interfaces.Entities) — `int Id` entity contract
- `IBaseEntity` — defined locally in NopDependencies, mirrors AyCode's concept
- `IAcLoggerBase`, `IAcLogWriterBase`, `AcLoggerBase`, `AcLogItemWriterBase` — logging infrastructure (see `AyCode.Core/docs/LOGGING.md`)
- `IAcModelDtoBaseEmpty` — DTO marker interface
- `IAcSoftRemoveEntity`, `IAcSoftRemoveEntityInt` — soft-delete contracts
- `IForeignKey`, `IForeignCollection<T>` — FK marker interfaces
- `ITimeStampCreated`, `ITimeStampModified`, `ITimeStampInfo` — timestamp contracts
- `PropertyHelper.CopyPublicValueTypeProperties()` — reflection-based property copy
- `TaskHelper.ToThreadPoolTask()` — wraps async work in thread pool
- `AcConst` — abstract constants base
- `ToonDescription` — metadata attribute for AI tooling

View File

@ -6,27 +6,58 @@ Terminology for the Mango.Nop Libraries. Read this before working on unfamiliar
| Term | Definition | | Term | Definition |
|---|---| |---|---|
| **BaseEntity** | nopCommerce root entity base with `int Id`. Mirror copy in `NopDependencies/`. | | **BaseEntity** | nopCommerce root entity base with `int Id`. Mirror copy in `NopDependencies/`. Namespace: `Nop.Core`. |
| **MgEntityBase** | Custom entity base — inherits `BaseEntity`, implements `IEntityInt` (AyCode). Used for domain-specific entities (e.g., `MgStockTaking`). | | **IBaseEntity** | Interface for `BaseEntity` (defined alongside it in NopDependencies). Only `int Id`. |
| **ModelDtoBase\<T\>** | Abstract DTO base with bidirectional mapping: `CopyEntityValuesToDto()` reads from entity, `CreateMainEntity()` creates entity from DTO. | | **MgEntityBase** | Custom entity base — inherits `BaseEntity`, implements `IEntityInt` (AyCode). `ToString()``"{TypeName}; Id: {Id}"`. |
| **ModelDtoBase** | Non-generic DTO base — only `int Id`. Implements `IModelDtoBase`. |
| **ModelDtoBase\<T\>** | Generic DTO base with bidirectional mapping: `CopyEntityValuesToDto()` reads from entity, `CopyDtoValuesToEntity()` writes to entity, `CreateMainEntity()` creates entity from DTO. Uses `Activator.CreateInstance<T>()`. |
| **IModelDtoBase** | `IEntityInt` + `IModelDtoBaseEmpty`. Marker for all DTOs with `int Id`. |
| **IModelDtoBase\<T\>** | Contract: `CreateMainEntity()`, `CopyDtoValuesToEntity()`, `CopyEntityValuesToDto()`. |
| **NopDependencies** | Mirror copies of nopCommerce entity classes in `Mango.Nop.Core`. Same namespaces as originals. Eliminates full nopCommerce dependency for consumers that only need types. | | **NopDependencies** | Mirror copies of nopCommerce entity classes in `Mango.Nop.Core`. Same namespaces as originals. Eliminates full nopCommerce dependency for consumers that only need types. |
| **GenericAttribute** | nopCommerce polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID, `Key` = attribute name, `Value` = string value. | | **GenericAttribute** | nopCommerce polymorphic key-value store. `KeyGroup` = owner type name, `EntityId` = owner ID, `Key` = attribute name, `Value` = string value. `CreatedOrUpdatedDateUTC` tracks last change. |
| **PropertyHelper.CopyPublicValueTypeProperties** | AyCode utility — reflection-based copy of all public value-type properties between two objects. Used by complex DTOs (`MgOrderDto`, `MgOrderItemDto`). |
| **ToonDescription** | AyCode metadata attribute for AI/doc tooling. Properties: `Purpose`, `BusinessRule`, `TypeRelation`, `RelatedTypes`. |
| **AcBinarySerializable** | AyCode binary serialization attribute (AcBinarySerializer, see `AyCode.Core/docs/BINARY_FORMAT.md`). Configures how a type is serialized for AcSignalR transport (see `AyCode.Core/AyCode.Services/docs/SIGNALR.md`). |
## Data Access ## Data Access
| Term | Definition | | Term | Definition |
|---|---| |---|---|
| **MgDbTableBase\<T\>** | Repository base wrapping nopCommerce `EntityRepository<T>`. Adds `GetAll()`, automatic timestamp management, event publishing. | | **MgDbTableBase\<T\>** | Repository base wrapping nopCommerce `EntityRepository<T>`. Adds `GetAll()` (`IQueryable<T>`), automatic timestamp management via `ITimeStampCreated`/`ITimeStampModified` interfaces, CRUD hooks (`OnInsert`, `OnUpdate`, `OnDelete`). |
| **MgDtoDbTableBase** | DTO-aware repository — handles DTO ↔ entity mapping on top of `MgDbTableBase`. | | **MgDtoDbTableBase\<TDto, TMain\>** | DTO-aware repository. **Delete operations throw** — must use `DeleteMainEntityById()`. Bridges DTO events → main entity events (`EntityInsertedEvent`, `EntityUpdatedEvent`). |
| **MgDbContextBase** | EF Core `DbContext` base for custom (non-nopCommerce) tables. | | **MgDbContextBase** | Database context base. Exposes `IRepository<Order>`, `IRepository<OrderItem>`, `IRepository<Product>`, `INopDataProvider`. Provides 4 transaction methods (`Transaction`, `TransactionSafe`, `TransactionAsync`, `TransactionSafeAsync`). |
| **MgDalBase** | Data Access Layer orchestrator — coordinates multiple repositories. | | **MgDalBase\<T\>** | Data Access Layer orchestrator — `Name`, `Context` (typed `TDbContext`), `MutextLock` (cross-process `Mutex`). |
| **TransactionSafe** | Transaction with global `SemaphoreSlim` lock. Prevents concurrent modifications. Use for order creation, stock changes. |
| **EntityRepository\<T\>** | nopCommerce built-in repository base (`Nop.Data`). `MgDbTableBase` extends this. |
| **INopDataProvider** | nopCommerce interface for raw LinqToDB data access. Used by `MgDbContextBase` and `MgDtoDbTableBase`. |
## Services ## Services
| Term | Definition | | Term | Definition |
|---|---| |---|---|
| **MgBackgroundServiceBase** | Base for hosted background services within nopCommerce. | | **MgBackgroundServiceBase** | Base for hosted background services. Loop: `Task.Delay(interval)``OnExecuteAsync()`. Supports pause. Per-iteration exception handling. Uses nopCommerce `ILogger`. |
| **MgSessionServiceBase** | Session state management — tracks user sessions. | | **MgSessionServiceBase\<T\>** | In-memory session management via `ConcurrentDictionary<string, T>`. Thread-safe. Methods: `GetOrCreateSessionItem`, `TryAddSessionItem`, `TryGetSessionItem`, `TryRemoveSessionItem`, `TryGetSessionItemBySignlaRConnectionId`. |
| **MgEventConsumerBase** | Base for nopCommerce entity event consumers (insert/update/delete notifications). | | **MgSessionItemBase** | Session item with `SessionId`, `SignaRConnectionId`, `RequestCount`. Primary constructor: `(string sessionKey)`. |
| **MgLockServiceBase** | Distributed lock service — prevents concurrent operations on shared resources. | | **MgEventConsumerBase** | nopCommerce event consumer base. Subscribes to: `EntityUpdatedEvent<Product>`, `EntityInsertedEvent<Product>`, `CustomerRegisteredEvent`, `OrderPlacedEvent`, `PageRenderingEvent`, `ProductSearchEvent`. All handlers virtual with empty default. |
| **NopLogWriter** | Logging bridge — writes to nopCommerce's built-in log system. | | **MgLockServiceBase** | `SemaphoreSlim(1)` wrapper. Implements `IMgLockService`. Used by `MgDbContextBase.TransactionSafe*`. |
| **NopLogWriter** | Bridges AyCode logging (see `AyCode.Core/docs/LOGGING.md`) to nopCommerce `Log` table.
| **NopLoggerMsSqlNopDataProvider** | Extends `MsSqlNopDataProvider`. Provides `InsertLogItem<T>()` / `InsertLogItemAsync<T>()` with own `TransactionScope(Suppress, ReadUncommitted)` to avoid transaction nesting. |
## AyCode Types (external, from DLL references)
| Term | Definition |
|---|---|
| **IEntityInt** | `int Id` entity contract (AyCode.Interfaces.Entities) |
| **IAcLoggerBase** | Logger interface — see `AyCode.Core/docs/LOGGING.md` (`AyCode.Core.Loggers`) |
| **IAcLogWriterBase** | Log writer interface — pluggable output sink (see `AyCode.Core/docs/LOGGING.md`) |
| **AcLoggerBase** | Logger base class with category name and writer array (see `AyCode.Core/docs/LOGGING.md`) |
| **AcLogItemWriterBase\<T\>** | Log writer base for structured log items (see `AyCode.Core/docs/LOGGING.md`) |
| **IAcModelDtoBaseEmpty** | Empty DTO marker interface |
| **IAcSoftRemoveEntity** | Soft-delete contract (AyCode) |
| **IForeignKey** | FK marker interface |
| **IForeignCollection\<T\>** | FK collection marker |
| **ITimeStampCreated** | `Created` datetime property |
| **ITimeStampModified** | `Modified` datetime property |
| **ITimeStampInfo** | Combines `Creator` (int), `Created`, `Modified` |
| **AcConst** | Abstract constants base class |
| **TaskHelper.ToThreadPoolTask** | Wraps `Func<Task<T>>` into `Task.Run` on thread pool |