Xeku.Core 0.0.0.7

dotnet add package Xeku.Core --version 0.0.0.7
                    
NuGet\Install-Package Xeku.Core -Version 0.0.0.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Xeku.Core" Version="0.0.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Xeku.Core" Version="0.0.0.7" />
                    
Directory.Packages.props
<PackageReference Include="Xeku.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Xeku.Core --version 0.0.0.7
                    
#r "nuget: Xeku.Core, 0.0.0.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Xeku.Core@0.0.0.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Xeku.Core&version=0.0.0.7
                    
Install as a Cake Addin
#tool nuget:?package=Xeku.Core&version=0.0.0.7
                    
Install as a Cake Tool

Xeku.Core

Core XAF module providing essential utilities, attributes, and file storage.

繁體中文文檔

Features

  • DataAdapters: Base classes for non-persistent object adapters supporting external data sources.
  • Business Objects: Standard XAF implementations for common needs:
    • ApplicationUser: Extends PermissionPolicyUser with DisplayName, Email, and Photo.
    • ApplicationUserLoginInfo: Support for external authentication providers (OIDC, Keycloak).
  • FileStorage: Compatible external file storage (stores files on disk instead of DB BLOB).
  • Attributes: Powerful custom attributes for XAF model automation:
    • [CloneView]: Automatically creates a copy of a ListView or DetailView in the model.
    • [XpandNavigationItem]: Easily define complex navigation structures directly on business classes.
    • DetailView Layout: Automatic localization of layout group captions based on class names.
    • Validation: Pre-styled validation rules for CRON, Email, IP, Phone, URL, and ZipCode.
  • Actions:
    • CheckableSimpleAction: A SimpleAction descendant with a togglable (checked) state, optimized for Blazor UI.

Architecture

graph TB
    subgraph "Core Utilities"
        Attributes["Custom Attributes"]
        Layout["Layout Logic"]
        Validation["Validation Rules"]
    end
    
    subgraph "Business Framework"
        BOs["Standard BOs (User, LoginInfo)"]
        FileStorage["File Storage Backend"]
        DataAdapters["Data Adapters"]
    end
    
    FileStorage --> LocalDisk["Local Disk"]
    
    style Attributes fill:#fff4e1
    style BOs fill:#fff4e1
    style LocalDisk fill:#fce4ec

Standard Business Objects

ApplicationUser

A base user class that inherits from PermissionPolicyUser and includes common fields:

  • DisplayName: Ful name of the user (defaults to UserName on save).
  • Email: Contact email address.
  • Photo: Byte array for profile pictures.
  • LoginInfo: Collection of external login details.

ApplicationUserLoginInfo

Stores link between a user and an OAuth/OpenID provider.

Installation

1. NuGet Package

dotnet add package Xeku.Core

2. Add Module Reference

// In your module constructor
RequiredModuleTypes.Add(typeof(Xeku.Core.CoreModule));

Custom Attributes

CloneView

Automatically generates a copy of a View in the XAF Model.

[CloneView(CloneViewType.ListView, "MyCustomListView")]
public class MyBusinessObject : BaseObject { ... }

XpandNavigationItem

Defines navigation paths directly on the business class.

[XpandNavigationItem("MasterData/Customers", "Customer_ListView")]
public class Customer : BaseObject { ... }

Validation Attributes

Ready-to-use validation rules based on regular expressions.

public class Contact : BaseObject {
    [EmailAddressValidation]
    public string Email { get; set; }

    [UrlValidation]
    public string Website { get; set; }
}

CheckableSimpleAction

A specialized Action that maintains a "Checked" state, rendered as a toggle button in Blazor.

public class MyController : ViewController {
    public MyController() {
        var action = new CheckableSimpleAction(this, "MyToggle", PredefinedCategory.Filters);
        action.Execute += (s, e) => {
            var toggle = (CheckableSimpleAction)s;
            // toggle.Checked holds the new state
        };
    }
}

Layout Group Title Localization

Automatically localizes layout group captions when they match the class name. This ensures that UI groups remain consistent with domain object translations.

This feature is active by default once the module is registered.

MessageService

A utility service for displaying toast messages in XAF applications across both Blazor and WinForms.

Usage

using Xeku.Core.Services;

// Show success message (green)
MessageService.Success(Application, "Operation completed!");

// Show info message (blue)
MessageService.Info(Application, "Additional information here.");

// Show warning message (yellow)
MessageService.Warning(Application, "Please verify your input.");

// Show error message (red)
MessageService.Error(Application, "Something went wrong.");

All messages auto-dismiss after 5 seconds. Position is Top for Web and Toast for Win.

SecurityHelper

A platform-agnostic utility for retrieving the current logged-in user, solving issues where SecuritySystem.CurrentUserId may fail in Web API environments.

Background

In XAF Blazor and WinForms, you can directly use the static SecuritySystem.CurrentUserId to get the current user. However, in Web API (ASP.NET Core) environments, due to multi-threaded async processing, this static property may not return the correct value within XPO's OnSaving.

Usage

using Xeku.Core.Helpers;
using Xeku.Core.BusinessObjects;

public class MyBusinessObject : BaseObject
{
    // ...

    public override void AfterConstruction()
    {
        base.AfterConstruction();
        
        // Use SecurityHelper instead of static SecuritySystem.CurrentUserId
        Owner = SecurityHelper.GetCurrentUser(Session);
    }
}

How It Works

SecurityHelper.GetCurrentUser() tries in order:

  1. DI method: Get ISecurityStrategyBase from Session.ServiceProvider (Web API compatible)
  2. Static method: Fall back to SecuritySystem.CurrentUserId (Blazor/WinForms)

This ensures the current user is correctly retrieved regardless of platform.

User Friendly ID & Sequence Generator

Provides a mechanism to generate thread-safe, database-transaction-level sequential numbers for persistent objects (e.g., Form Numbers).

Base Class: UserFriendlyIdPersistentObject

Inherit from this class to automatically gain a SequentialNumber property.

using Xeku.Core.BusinessObjects;
 
public class MyOrder : UserFriendlyIdPersistentObject
{
    public MyOrder(Session session) : base(session) { }
    
    protected override void OnSaving()
    {
        base.OnSaving();
        // The SequentialNumber is automatically generated on first save
    }
}

Required Initialization

To use this feature, you MUST initialize the SequenceGenerator in your application's Startup.cs (Blazor) or Program.cs (Win/WebAPI) within the AddSecuredXpo or AddXpo configuration:

builder.ObjectSpaceProviders
    .AddSecuredXpo((serviceProvider, options) => {
        // ... existing configuration ...
        options.UseSharedDataStoreProvider = true;
 
        // Important: Initialize SequenceGenerator
        string connectionString = Configuration.GetConnectionString("ConnectionString");
        IXpoDataStoreProvider dataStoreProvider = XPObjectSpaceProvider.GetDataStoreProvider(connectionString, null, true);
        Xeku.Core.Helpers.SequenceGenerator.Initialize(dataStoreProvider);
    });

Failure to initialize the SequenceGenerator will result in NullReferenceException when saving objects that inherit from UserFriendlyIdPersistentObject.

Serilog Tracing

Replaces the default XAF eXpressAppFramework.log with Serilog-based file logging featuring:

  • Daily rolling files: One log file per day
  • 90-day retention: Configurable retention period
  • Cross-platform: Works on Blazor, WebApi, and WinForms

Default Log Location

{ApplicationDirectory}/logs/log-YYYYMMDD.txt

Usage

Initialize at the very beginning of your application entry point:

using Xeku.Core.Services;

public static int Main(string[] args)
{
    SerilogTracingSetup.Initialize();
    // ... rest of your application startup
}

Disabling XAF Default Log Files

To ensure only Serilog generates logs and avoid creating duplicate eXpressAppFramework.log files, apply the following configurations based on your project type:

WinForms (App.config)

Add TraceLogLocation to the appSettings section:

<appSettings>
  <add key="eXpressAppFrameworkTraceLevel" value="3" />
  <add key="TraceLogLocation" value="None" />
</appSettings>
ASP.NET Core / WebApi (appsettings.json)

Add TraceLogLocation to the DevExpress:ExpressApp section:

{
  "DevExpress": {
    "ExpressApp": {
      "TraceLogLocation": "None"
    }
  }
}

Custom Configuration

// Custom log directory and retention
SerilogTracingSetup.Initialize(
    logDirectory: @"D:\Logs\MyApp",
    retainedFileCountLimit: 30,  // Keep 30 days
    fileNamePrefix: "myapp"      // Results in myapp-YYYYMMDD.txt
);

Shutdown

Call on application exit to ensure all logs are flushed:

SerilogTracingSetup.Close();

Log Viewer

Provides an in-app read-only log viewer accessible under Administration menu.

Features

  • View log files directly in XAF UI (supports Blazor and WinForms)
  • Lazy Loading: ListView only loads metadata, content is loaded when DetailView opens
  • Live Refresh: Click "Refresh Log" button to reload latest content
  • Admin-only: Updater.cs automatically configures Navigation Permission, non-admin roles cannot access Administration menu
  • Located at: Administration > Log Viewer

Architecture

graph LR
    ListView[ListView<br/>Metadata Only] --> DetailView[DetailView<br/>Lazy Load Content]
    DetailView --> RefreshLog[Refresh Log<br/>Reload from Disk]
Component Description
LogFileInfo Non-persistent object with LoadContent() method
LogViewerAdapter Inherits NonPersistentObjectAdapterBase, GetObjects excludes Content
LogViewerController Loads content when DetailView opens
Updater.cs Automatically configures Navigation Permission for Administration menu

WebApi Endpoint

The log viewer also exposes authenticated API endpoints:

GET /api/logs           # List all log files
GET /api/logs/{fileName} # Get log file content

FileStorage (External File Storage)

Provides XAF-compatible external file storage. Files are stored on disk instead of database BLOB.

Default Storage Location

Files are stored by default in:

{ApplicationDirectory}/FileStorage/{Oid}-{FileName}

For example, Blazor Server apps store files in:

bin/Debug/net8.0/FileStorage/

Usage

Create a BusinessObject with an ExternalFileData property:

using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl;
using DevExpress.Xpo;
using Xeku.Core.FileStorage;

[DefaultClassOptions]
public class CustomerDocument : BaseObject
{
    public CustomerDocument(Session session) : base(session) { }
    
    public string Title { get; set; }
    
    [Aggregated]
    [ExpandObjectMembers(ExpandObjectMembers.Never)]
    public ExternalFileData? Attachment { get; set; }
}

Custom Storage Path

// Set at application startup (Startup.cs or Program.cs)
ExternalFileData.DefaultBackend = new LocalStorageBackend("D:\\AppFiles");

Custom Storage Backend

Implement IStorageBackend to support other storage (Azure Blob, S3, etc.):

public class AzureBlobStorageBackend : IStorageBackend
{
    // Implement Save, Load, Delete, Exists, GetStoragePath
}

ExternalFileData.DefaultBackend = new AzureBlobStorageBackend();

Dynamic Navigation (INavTreeNode)

Automatically create navigation menu items from hierarchical Business Objects implementing INavTreeNode.

Features

  • Interface-based: Any BO implementing INavTreeNode appears in navigation panel
  • Auto-grouping: Uses BO's localized class name ([XafDisplayName]) as group title
  • Real-time updates: Menu updates when data changes
  • Custom navigation targets: Navigate to DetailView, filtered ListView, or any View

Architecture

graph LR
    subgraph "Business Objects"
        BO1[Category]
        BO2[QuickLink]
    end
    
    subgraph "Navigation Panel"
        G1[Category Group]
        G2[QuickLink Group]
    end
    
    BO1 -->|INavTreeNode| G1
    BO2 -->|INavTreeNode| G2
    
    G1 --> DetailView[DetailView]
    G1 --> ListView[Filtered ListView]

Basic Usage

Implement INavTreeNode interface on your BO:

using Xeku.Core.Navigation;
using DevExpress.Persistent.Base.General;

[XafDisplayName("Menu Category")]  // This becomes the navigation group title
public class MenuCategory : BaseObject, INavTreeNode
{
    public MenuCategory(Session session) : base(session) { }

    // === ITreeNode (required) ===
    public string Name { get; set; }
    ITreeNode ITreeNode.Parent => Parent;
    public IBindingList Children => ChildItems;

    // === INavTreeNode (required) ===
    string? INavTreeNode.MenuImageName => "BO_Category";
    bool INavTreeNode.IsNavigationVisible => IsActive;
    int INavTreeNode.NavigationSortOrder => SortOrder;

    public NavTarget? GetNavigationTarget()
    {
        return null;  // Default: navigate to this item's DetailView
    }

    // === Your properties ===
    public MenuCategory? Parent { get; set; }
    public int SortOrder { get; set; }
    public bool IsActive { get; set; } = true;
    
    [Association]
    public XPCollection<MenuCategory> ChildItems => GetCollection<MenuCategory>(nameof(ChildItems));
}

Custom Navigation Targets

The GetNavigationTarget() method controls where clicking a menu item navigates to:

Option 1: Default DetailView (return null)
public NavTarget? GetNavigationTarget()
{
    // Clicking this menu item opens its DetailView
    return null;
}
Option 2: Navigate to Specific View
public NavTarget? GetNavigationTarget()
{
    // Navigate to a dashboard or custom view
    return NavTarget.View("MyDashboard");
}
Option 3: Navigate to Filtered ListView ⭐ Most Common

This is the most powerful option - clicking a category shows related items:

public NavTarget? GetNavigationTarget()
{
    // Clicking "Work Notes" category shows all Notes in that category
    return NavTarget.FilteredListView(
        typeof(Note),
        $"[Category.Oid] = '{Oid:D}'"
    );
}
Option 4: Navigate to Specific Object's DetailView
public NavTarget? GetNavigationTarget()
{
    // Navigate to a specific Note's DetailView
    return NavTarget.DetailView(typeof(Note), someNoteKey);
}
Method Description
NavTarget.View(viewId) Navigate to any View by ID
NavTarget.FilteredListView(type, criteria) Navigate to ListView with filter
NavTarget.FilteredListView(viewId, criteria) Navigate to specific ListView with filter
NavTarget.DetailView(type, key) Navigate to object's DetailView

Real-World Example: Note Categories

[XafDisplayName("Note Category")]
public class NoteCategory : BaseObject, INavTreeNode
{
    // ... ITreeNode implementation ...

    public NavTarget? GetNavigationTarget()
    {
        // When user clicks a category in navigation,
        // show all notes belonging to this category
        return NavTarget.FilteredListView(
            typeof(Note),
            $"[Category.Oid] = '{Oid:D}'"
        );
    }
}

Result: Navigation panel shows "Note Category" group with all categories. Clicking "Work" category displays only work-related notes.

License

MIT License

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (17)

Showing the top 5 NuGet packages that depend on Xeku.Core:

Package Downloads
Xeku.Cache

XAF module providing unified caching abstraction with Memory and Redis backend support.

Xeku.Editors

XAF Editors module.

Xeku.Core.WebApi

XAF WebApi integration for Xeku.Core with DataService Plugin architecture and exception logging middleware.

Xeku.ExcelImport

XAF Excel Import module for DevExpress XAF applications. Provides Excel import and template export functionality with model-based configuration.

Xeku.Triggers

XAF module providing trigger rules and webhook execution for CRUD operations.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.0.7 951 1/28/2026
0.0.0.6 814 1/21/2026
0.0.0.5 608 1/15/2026
0.0.0.4 615 1/13/2026
0.0.0.3 446 1/9/2026
0.0.0.2 411 1/8/2026