AyCode.Blazor/AyCode.Blazor.Components/docs/MGGRID/MGGRID_CRUD.md

4.6 KiB

MgGrid — Lifecycle & CRUD

Part of the MgGrid system. See README.md for overview and component hierarchy.

Lifecycle

1. OnInitializedAsync()
   ├── Validate Logger, SignalRClient (throw if null)
   ├── Create SignalRCrudTags from message tag parameters
   ├── Create TSignalRDataSource via Activator.CreateInstance(SignalRClient, crudTags, ContextIds)
   ├── Set DataSource.FilterText
   ├── Bind grid Data to data source inner list
   └── Subscribe to: OnDataSourceLoaded, OnDataSourceItemChanged, OnSyncingStateChanged

2. SetParametersAsyncCore() [first time]
   ├── Set KeyFieldName = "Id"
   ├── Wire 6 DxGrid events → internal handlers (see MGGRID_PARAMETERS.md)
   ├── Add OnCustomizeElement handler (edit row highlighting, detail cell styling)
   └── Set defaults: TextWrapEnabled=false, AllowSelectRowByClick=true, etc.

3. OnParametersSet() [first time]
   ├── Set GridName default: "{TDataItem.Name}Grid"
   ├── Set AutoSaveLayoutName default: "Grid{TDataItem.Name}"
   ├── Wire layout auto-loading/saving handlers
   └── Register with GridWrapper via GridWrapper.RegisterGrid(this)

4. OnAfterRenderAsync(firstRender: true)
   ├── If DataSource parameter was provided: LoadDataSource(dataSourceParam, sync, notify)
   └── Else: LoadDataSourceAsync(notify) — fires SignalR GetAll request

CRUD Operations

Adding Items

await grid.AddDataItem(item);       // local add, sync later
await grid.AddDataItemAsync(item);  // immediate server sync

await grid.InsertDataItem(0, item);      // insert at index, sync later
await grid.InsertDataItemAsync(0, item); // insert at index, immediate sync

Other CRUD Methods

Method Description
UpdateDataItem(item) Local update, sync later
UpdateDataItemAsync(item) Immediate server sync
AddOrUpdateDataItem(item) Add if new, update if existing
RemoveDataItem(item) Remove by entity reference
RemoveDataItem(id) Remove by ID
ReloadDataSourceAsync() Re-fetch all data from server
ForceRenderAsync() Force grid re-initialization via new render key

ID Generation for New Items

New items get temporary client-side IDs until the server assigns real ones:

TId Type Strategy Example
Guid Guid.NewGuid() a1b2c3d4-...
int -1 * AcDomain.NextUniqueInt32 -1, -2, -3, ...

Convention: Negative integer IDs indicate unsaved items. The server replaces them with real auto-increment IDs.

Edit Flow (Inline)

User clicks Edit → OnEditStart → OnCustomizeEditModel
  ├── For new items: assign temp ID, set parent FK if detail grid
  ├── Set GridEditState = New/Edit
  ├── Fire OnGridCustomizeEditModel callback
  ├── Re-read the edit model after the callback — the consumer may have created/replaced it
  │     (official DevExpress pattern for types without a parameterless ctor); any instance the
  │     pre-callback block has not seen gets the same temp-ID/parent-FK initialization
  └── Notify InfoPanel: SetEditMode() — skipped with a Logger.Warning when no edit model exists
        (e.g. stale focused row after a datasource reload; see MGGRID_ISSUES.md#acblazor-grid-i-n7k2)

User clicks Save → OnItemSaving
  ├── Fire OnGridEditModelSaving callback (can cancel)
  ├── If new: AddDataItemAsync / InsertDataItemAsync — the local add is immediate; the row appears
  │     at the sort position of its temp ID until the server confirms
  ├── If existing: UpdateDataItemAsync
  ├── Reset GridEditState = None
  └── Clear InfoPanel edit mode

Server confirm (async, per item) → OnDataSourceItemChanged
  ├── The response is applied to the SAME instance in place (CopyTo — external references stay
  │     valid; no collection notification fires)
  └── Grid.Reload() re-processes the local view (sort/filter/page) — a new row jumps to its final
        sorted position (real ID) without a data re-query

User clicks Cancel → OnEditCanceling
  ├── Reset GridEditState = None
  └── Clear InfoPanel edit mode

Edit Row Highlighting

When GridEditState != None, the focused row and its cells get background-color: #fffbeb (warm yellow) via OnCustomizeElement.

Disposal

DisposeAsync(): sets _isDisposed = true (every async callback checks it and bails), unsubscribes the three datasource events (OnDataSourceLoaded, OnDataSourceItemChanged, OnSyncingStateChanged) and OnCustomizeElement, then GC.SuppressFinalize(this).