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
<PackageReference Include="Xeku.Core" Version="0.0.0.7" />
<PackageVersion Include="Xeku.Core" Version="0.0.0.7" />
<PackageReference Include="Xeku.Core" />
paket add Xeku.Core --version 0.0.0.7
#r "nuget: Xeku.Core, 0.0.0.7"
#:package Xeku.Core@0.0.0.7
#addin nuget:?package=Xeku.Core&version=0.0.0.7
#tool nuget:?package=Xeku.Core&version=0.0.0.7
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: ExtendsPermissionPolicyUserwithDisplayName,Email, andPhoto.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:
- DI method: Get
ISecurityStrategyBasefromSession.ServiceProvider(Web API compatible) - 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.csautomatically 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
INavTreeNodeappears 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);
}
NavTarget Reference
| 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.
Related Packages
- Xeku.Core.WebApi - WebApi
- Xeku.Core.Win - WinForms platform integration
- Xeku.Core.Blazor - Blazor Server integration
License
MIT License
| Product | Versions 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. |
-
net8.0
- DevExpress.ExpressApp (>= 25.2.3)
- DevExpress.ExpressApp.Security.Xpo (>= 25.2.3)
- DevExpress.ExpressApp.Xpo (>= 25.2.3)
- DevExpress.Persistent.Base (>= 25.2.3)
- DevExpress.Persistent.BaseImpl.Xpo (>= 25.2.3)
- Microsoft.Data.SqlClient (>= 6.1.2)
- Serilog (>= 4.2.0)
- Serilog.Sinks.File (>= 6.0.0)
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.