Datový model (EF Core)¶
Kompletní přehled entit a jejich vztahů v databázi EVA.
Schéma: dbo (PostgreSQL schéma)
Primární klíče: vždy long OID
Správa majetku¶
Article ──→ ArticleCategory (FK: CategoryId)
Article ──→ ArticleType (FK: TypeId)
Article ──→ ArticleState (FK: StateId)
Article ──→ ArticleDisposalMethod (FK: DisposalMethodId, nullable)
Article ──→ ArticleDisposalReason (FK: DisposalReasonId, nullable)
Article ──→ User (FK: UserId, nullable — přiřazený uživatel)
Article ──→ User (FK: ResponsibleUserId, nullable — odpovědná osoba)
Article — hlavní entita¶
public class Article
{
public long OID { get; set; }
public string Code { get; set; } // Inventární číslo
public string Name { get; set; } // Název
public long CategoryId { get; set; }
public long TypeId { get; set; }
public long StateId { get; set; }
public decimal Price { get; set; }
public string? SerialNumber { get; set; }
public string? RfId { get; set; }
public long? UserId { get; set; }
public long? ResponsibleUserId { get; set; }
public bool IsDisposal { get; set; }
public DateTime? DisposalDT { get; set; }
public long? DisposalMethodId { get; set; }
public long? DisposalReasonId { get; set; }
public bool Deleted { get; set; }
// Audit fields
public DateTime? CreatedDT { get; set; }
public DateTime? ModifiedDT { get; set; }
public string? CreatedBy { get; set; }
public string? ModifiedBy { get; set; }
// Navigation
public virtual ArticleCategory Category { get; set; }
public virtual ArticleType Type { get; set; }
public virtual ArticleState State { get; set; }
}
Lokace a umístění majetku¶
Location ──→ Location (self-reference: ParentLocationId, nullable = kořen)
ArticleLocation ──→ Article (FK: ArticleId)
ArticleLocation ──→ Location (FK: LocationId)
Location — hierarchický strom¶
public class Location
{
public long OID { get; set; }
public string Name { get; set; }
public string? Code { get; set; }
public long? ParentLocationId { get; set; }
public virtual Location? ParentLocation { get; set; }
public virtual ICollection<Location> ChildLocations { get; set; }
}
ArticleLocation — vazba artikl–lokace¶
public class ArticleLocation
{
public long OID { get; set; }
public long ArticleId { get; set; }
public long LocationId { get; set; }
public int Quantity { get; set; }
public virtual Article Article { get; set; }
public virtual Location Location { get; set; }
}
Inventarizace¶
Inventory ──→ InventoryState (FK: InventoryStateId)
Inventory ──→ Location (FK: LocationId, nullable)
InventoryItem ──→ Inventory (FK: InventoryId)
InventoryItem ──→ Article (FK: ArticleId)
InventoryItem ──→ Location (FK: LocationId, nullable — původní umístění)
InventoryItem ──→ Location (FK: InventoryLocationId, nullable — inventurní umístění)
InventoryItem ──→ ArticleDisposalMethod (FK: DisposalMethodId, nullable)
InventoryItem ──→ ArticleDisposalReason (FK: DisposalReasonId, nullable)
PhotoArticleInventory ──→ InventoryItem (FK: InventoryItemId)
Přesuny majetku (Transfers)¶
ArticleTransfer ──→ ArticleTransferItem (1:N)
ArticleTransferItem ──→ Article
ArticleTransferItem ──→ Location (zdrojová)
ArticleTransferItem ──→ Location (cílová)
Identita a uživatelé¶
ApplicationUser (ASP.NET Identity)
ApplicationRole (ASP.NET Identity)
ApplicationUserRole (junction)
User (doménový uživatel — oddělený od Identity)
Dva uživatelské modely
ApplicationUser je ASP.NET Core Identity uživatel (přihlašování do Manageru, JWT). User je doménový uživatel evidovaný v EVA pro přiřazení majetku a inventur. Tyto dvě entity jsou propojeny přes ApplicationUser.UserId.
Multitenancy — izolace dat¶
Při Multitenancy:Enabled = true je každá doménová entita izolována per-tenant pomocí EF Core Global Query Filters.
Interface ITenantAware¶
Implementují ho všechny doménové entity (Article, Location, Inventory, ArticleTransfer, User, Printer, …). Systémové entity (CurrencyType, PrinterType, PrintRequestState, …) ITenantAware neimplementují — jsou sdílené mezi tenanty.
ICurrentTenantAccessor¶
public interface ICurrentTenantAccessor
{
long? TenantID { get; }
bool IsMultitenancyEnabled { get; }
}
Implementace:
- ClaimsPrincipalTenantAccessor (Eva.Manager) — čte claim tenant_id z ASP.NET Identity
- HttpContextTenantAccessor (Eva.Mobile.WebApi) — čte claim tenant_id z JWT tokenu
EF Core Global Query Filters¶
EvaDataContext.OnModelCreating přidá pro každou ITenantAware entitu automatický filtr:
// Zjednodušeně — aplikováno přes reflexi pro všechny ITenantAware entity
modelBuilder.Entity<Article>()
.HasQueryFilter(e => !_tenantAccessor.IsMultitenancyEnabled
|| e.TenantID == _tenantAccessor.TenantID);
Filtr se přidává pouze při IsMultitenancyEnabled = true. Při false (výchozí) se chování nemění.
Dapper dotazy
EF Core Global Query Filters se nevztahují na Dapper dotazy. Tam je nutné přidat WHERE "TenantID" = @tenantId ručně — viz InventoryDataService a ArticleTransferDataService.
Entita Tenant¶
[Table("Tenant", Schema = "dbo")]
public class Tenant
{
public long OID { get; set; }
public string Name { get; set; } // Název organizace
public string Slug { get; set; } // URL identifikátor (unique)
public string? Email { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDT { get; set; }
public bool Deleted { get; set; }
}
Konvence databázového modelu¶
| Konvence | Příklad |
|---|---|
| PK | long OID |
| FK číselné | long CategoryId |
| FK nullable | long? UserId |
| Navigační vlastnost | virtual ArticleCategory Category |
| Soft delete | bool Deleted |
| Audit | CreatedDT, ModifiedDT, CreatedBy, ModifiedBy |
| Schéma | [Table("Article", Schema = "dbo")] |