Skip to content

Latest commit

 

History

History
215 lines (172 loc) · 7.85 KB

File metadata and controls

215 lines (172 loc) · 7.85 KB

Remote Factory Integration

← Properties | ↑ Guides | Validation →

Neatoo entities integrate with RemoteFactory for factory generation and client-server execution. This guide covers how Neatoo entity state interacts with factory operations.

For RemoteFactory documentation (factory attributes, service injection, remote execution, authorization, setup), see:

Save Routing Based on Entity State

When Save() is called, the factory routes to the appropriate method based on Neatoo entity state:

Entity State Factory Routes To After Completion
IsNew == true [Insert] method IsNew = false, IsModified = false
IsNew == false && IsModified == true [Update] method IsModified = false
IsDeleted == true [Delete] method Entity cannot be modified further

This routing is determined by Neatoo's state properties, not RemoteFactory configuration.

Entity State During Factory Operations

Create Operations

[Create]
public void Create()
{
    // After Create completes:
    // - IsNew = true (entity not yet persisted)
    // - IsModified = false (initial state is clean)
    // - IsPaused = false (validation rules active)
    Id = 0;
    Name = "";
    Department = "";
}

snippet source | anchor

Fetch Operations

[Remote, Fetch]
internal async Task Fetch(int id, [Service] ISkillRemoteFactoryRepository repo)
{
    // During Fetch:
    // - IsPaused = true (validation and modification tracking suspended)
    // - Property assignments use LoadValue semantics (no IsModified change)

    var data = await repo.FetchAsync(id);
    Id = data.Id;
    Name = data.Name;
    Department = data.Department;

    // After Fetch completes:
    // - IsNew = false (entity was loaded from persistence)
    // - IsModified = false (loaded state is considered clean)
    // - IsPaused = false (validation resumes)
}

snippet source | anchor

Save Operations

Before save executes, check IsSavable (available on aggregate roots through IEntityRoot):

/// <summary>
/// IsSavable combines multiple state checks before persistence.
/// </summary>
public static async Task<bool> CheckSavableBeforeSave(SkillRfIntegrationRoot entity)
{
    // IsSavable = IsModified && IsValid && !IsBusy && !IsChild
    if (!entity.IsSavable)
    {
        // Don't persist - one or more conditions failed:
        // - !IsModified: No changes to save
        // - !IsValid: Validation failed
        // - IsBusy: Async rules still running
        // - IsChild: Must save through parent aggregate
        return false;
    }

    // Safe to persist
    return true;
}

snippet source | anchor

After [Insert] or [Update] completes:

  • IsNew = false (after Insert)
  • IsModified = false - Changes have been persisted

Child Entity State Cascade

Child entities within an aggregate have their state cascade to the parent:

Child State Effect on Parent
IsModified = true Parent IsModified = true
IsValid = false Parent IsValid = false
IsBusy = true Parent IsBusy = true

Child entities have IsChild = true and must save through the aggregate root. Their interfaces extend IEntityBase (not IEntityRoot), so IsSavable and Save() are not accessible to consumers.

// Child entities do NOT use [Remote] - they persist through the aggregate root
[Create]
public void Create()
{
    // IsChild = true (set when added to parent collection)
    // IsSavable/Save() not accessible on IEntityBase (child interface)
}

[Fetch]
public void Fetch(int id, string value)
{
    Id = id;
    Value = value;
}

// Insert/Update/Delete called by parent's Save() - no [Remote] needed
[Insert]
public void Insert() { /* Persist through aggregate root */ }

[Update]
public void Update() { /* Persist through aggregate root */ }

[Delete]
public void Delete() { /* Persist through aggregate root */ }

snippet source | anchor

DeletedList Lifecycle

When items are removed from an EntityListBase:

  1. New items (IsNew = true): Discarded entirely (never persisted)
  2. Existing items (IsNew = false):
    • MarkDeleted() called, IsDeleted = true
    • Added to DeletedList for persistence during Save
    • [Delete] method called for each during aggregate Save

After Save completes, DeletedList is cleared.

/// <summary>
/// DeletedList lifecycle for removed items.
/// </summary>
public static void DeletedListLifecycle(
    SkillRfIntegrationRoot parent,
    ISkillRfIntegrationChildFactory childFactory)
{
    // Step 1: New items are discarded when removed (never persisted)
    var newChild = childFactory.Create();
    parent.Children.Add(newChild);
    parent.Children.Remove(newChild);  // Discarded - never goes to DeletedList

    // Step 2: Existing items go to DeletedList when removed
    var existingChild = childFactory.Fetch(1, "existing");
    parent.Children.Add(existingChild);
    parent.Children.Remove(existingChild);
    // Now: existingChild.IsDeleted = true
    // Now: parent.Children.DeletedCount = 1

    // Step 3: During Save(), [Delete] called for each DeletedList item
    // Step 4: After Save(), DeletedList is cleared
}

snippet source | anchor

Serialization State Transfer

When entities cross client-server boundaries:

State Serialized? Notes
Property values Yes All registered properties
IsNew Yes Preserved across boundary
IsDeleted Yes Preserved across boundary
IsModified Yes Preserved across boundary
IsChild Yes Preserved across boundary
DeletedList items Yes For pending deletes
Validation messages No Rules re-run after deserialization
IsBusy No Reset on deserialization

After deserialization, validation rules execute to establish IsValid state on the client.


See also:


UPDATED: 2026-03-02