ChienDeepEyes.ResultLib 2.5.0

dotnet add package ChienDeepEyes.ResultLib --version 2.5.0
                    
NuGet\Install-Package ChienDeepEyes.ResultLib -Version 2.5.0
                    
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="ChienDeepEyes.ResultLib" Version="2.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ChienDeepEyes.ResultLib" Version="2.5.0" />
                    
Directory.Packages.props
<PackageReference Include="ChienDeepEyes.ResultLib" />
                    
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 ChienDeepEyes.ResultLib --version 2.5.0
                    
#r "nuget: ChienDeepEyes.ResultLib, 2.5.0"
                    
#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 ChienDeepEyes.ResultLib@2.5.0
                    
#: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=ChienDeepEyes.ResultLib&version=2.5.0
                    
Install as a Cake Addin
#tool nuget:?package=ChienDeepEyes.ResultLib&version=2.5.0
                    
Install as a Cake Tool

ResultLib - Result Pattern Library

Thư viện đóng gói Result pattern với xử lý MessageForUser và SystemError, tích hợp log hệ thống, global exception handler, và ErrorOr.

📦 Cài đặt

dotnet add package ResultLib

Hoặc reference trực tiếp:

dotnet add reference ../ResultLib/ResultLib.csproj

🚀 Bắt đầu nhanh

1. Configure Log (2 dòng ở Program.cs)

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// Dùng ILoggerFactory (khuyên dùng) — log ra console / file / Seq / Elastic...
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
Log.Configure(loggerFactory);

Hoặc dùng EF Core để lưu log vào DB:

builder.Services.AddSingleton<ILogService, EfCoreLogService>();
var logService = app.Services.GetRequiredService<ILogService>();
Log.Configure(logService);

2. Bật Global Exception Handler (1 dòng)

// Chống unhandled exception — tự log + trả message chung cho user
app.UseResultLibExceptionHandler();

3. Dùng trong Controller

using ResultLib;

// Thành công
return Result.ResultOk;
return user.ToResultOk();

// Lỗi cho user (hiển thị trực tiếp)
return "Email không hợp lệ".ToResultErrorForUser();

// Lỗi hệ thống (log + message chung)
return Log.WriteErrorLogToResultSystem("Database timeout");

// Tự động xử lý: MessageForUser → show, không có prefix → log + message chung
return Log.ProcessAndToResultError(msg);

// Chuyển Result thành IActionResult (dùng trực tiếp trong controller)
return result.ToIActionResult();

Sử dụng ResultFilter (tùy chọn)

Thay vì gọi ToIActionResult() thủ công, bạn có thể dùng ResultFilter để tự động set status code từ Result:

// Trong Program.cs
builder.Services.AddResultFilter();
builder.Services.AddControllers(options => options.Filters.Add<ResultFilter>());
// Trong Controller — không cần gọi ToIActionResult()
[HttpGet("{id}")]
public Result GetUser(int id)
{
    var user = _userService.GetUser(id);
    if (user == null)
        return Result.NotFound("Không tìm thấy người dùng");
    return Result.Ok(user);
}


🎯 Core Concept

MessageForUser vs SystemError

Loại Message Mô tả Ví dụ
MessageForUser Lỗi an toàn, hiển thị cho user "Email không hợp lệ".ToMessageForUser()
SystemError Lỗi nội bộ, user thấy message chung + log ID "DB timeout".ToUnexpectedError()

Prefix System

  • [MessageForUser] — đánh dấu message dành cho user
  • [MoreDetailLog] — thêm chi tiết log (ẩn với user, chỉ dùng để debug)

🔧 LogOptions — Tùy chỉnh hành vi log

Mặc định, chỉ log hệ thống. User errors (có prefix MessageForUser) không bị log.

// Log cả user error (nếu muốn)
Log.Configure(logService, new LogOptions { LogUserErrors = true });

Hoặc dùng ILoggerFactory:

Log.Configure(loggerFactory, new LogOptions { LogUserErrors = true });

📖 Cách sử dụng chi tiết

1. Kết quả thành công

return Result.ResultOk;
return user.ToResultOk();
return 42.ToResultOk();

2. Lỗi cho User

// Có prefix → user thấy message này, KHÔNG log DB
return "Email không được để trống".ToResultErrorForUser();

// Hoặc dùng prefix thủ công
return "Email không hợp lệ".ToMessageForUser().ToResultError();

3. Lỗi hệ thống

// Cách 1: Ghi log + trả message chung
return Log.WriteErrorLogToResultSystem("Database timeout");

// Cách 2: Ghi log riêng rồi tạo Result
var logId = Log.WriteErrorLog("Database timeout", new { userId });
return logId.ToResultErrorForSystem();
// User thấy: "Đã xảy ra lỗi. Vui lòng thử lại sau [#a3f8c2]"

4. ProcessError — Tự động phân loại

// Tự động: MessageForUser → show, không có prefix → log + message chung
string msg = _service.DoSomething();
if (msg.HasError())
    return Log.ProcessAndToResultError(msg);

5. Meaningful Log Messages

// Log có context để debug dễ hơn
var msg = "User not found"
    .ToMeaningfulMessage("UserService.GetById", new { userId });
return Log.ProcessAndToResultError(msg);

// Combo: Message cho user + Log detail
return Log.ProcessAndToResultError(
    "Không tìm thấy người dùng".ToUserMeaningfulMessage("UserService.GetById", new { userId })
);

6. DomainException

// Ném trong service/business logic
throw new DomainException("Không tìm thấy đơn hàng");

// Global exception handler sẽ bắt → trả 400 + message cho user

7. Tích hợp với ErrorOr

using ErrorOr;

public ErrorOr<User> GetUser(int id)
{
    if (id <= 0)
        return Error.Validation("User.InvalidId", "ID phải lớn hơn 0".ToMessageForUser());
    return user;
}

// Trong Controller — tự động xử lý MessageForUser / SystemError
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    var result = _userService.GetUser(id).ToResult();
    return result.ToIActionResult();
}

🛠️ Các Extension Methods chính

Kết quả & Chuyển đổi

Method Mô tả
obj.ToResultOk() Trả về Result thành công (HTTP 200)
msg.ToResultError() Strip prefix nếu có, trả Result error (HTTP 400)
msg.ToResultErrorForUser() Thêm prefix MessageForUser, trả Result error
msg.ToResultErrorForSystem() Trả Result error với message chung + log ID
result.ToIActionResult() Chuyển Result thành IActionResult (giữ đủ trường, status theo Result.StatusCode)

ResultFilter (tùy chọn)

Method / Class Mô tả
services.AddResultFilter() Đăng ký ResultFilter tự động set status code từ Result
ResultFilter IAsyncResultFilter — tự động set Response.StatusCode khi action trả Result

MessageHelper

Method Mô tả
msg.ToMessageForUser() Thêm prefix [MessageForUser]
msg.IsMessageForUser() Kiểm tra có prefix MessageForUser không
msg.GetMessageForUser() Lấy message cho user (bỏ prefix)
msg.ToGeneralSystemErrorMessage() Tạo message chung kèm log ID
msg.ToMeaningfulMessage(prefix, params) Thêm chi tiết log vào message
msg.ToUserMeaningfulMessage(prefix, params) Combo: prefix user + log detail

Exception Extensions

Method Mô tả
ex.ToUnexpectedError() Tạo Error.Unexpected với full stack trace
ex.ToFailureError() Tạo Error.Failure với full stack trace

Log Static Class

Method Mô tả
Log.Configure(ILogService) Đăng ký log service
Log.Configure(ILoggerFactory) Đăng ký với ILogger (dễ dùng nhất)
Log.WriteErrorLog(msg) Ghi log, trả log ID
Log.ProcessError(msg) Tự động phân loại + log
Log.ProcessAndToResultError(msg) ProcessError + trả Result error
Log.WriteErrorLogToResultSystem(msg) Log + trả Result error hệ thống
Log.WriteErrorLogToResultUser(msg) Log + trả Result error user (chỉ log khi LogUserErrors=true)

Tích hợp

Method Mô tả
ErrorOr<T>.ToResult() Chuyển ErrorOr<T> sang Result
ErrorOr<T>.ToResult(successMessage) Chuyển ErrorOr<T> sang Result với custom message thành công (lưu ý: bỏ qua data T)
ErrorOr<Success>.ToResult() Chuyển ErrorOr<Success> sang Result
ValidationFailure.ToResult() Chuyển ValidationFailure sang Result

📁 Cấu trúc files

ResultLib/
├── Core/
│   ├── Result.cs                  # Core Result class
│   ├── ResultExtensions.cs        # ToResultOk, ToResultError...
│   └── DomainException.cs         # Domain exception cho business errors
├── Logging/
│   ├── LogService.cs              # ILogService + Log static class
│   ├── LoggerLogService.cs        # ILogger-based implementation
│   ├── LogOptions.cs              # Log configuration options
│   └── MessageHelper.cs           # MessageForUser / SystemError helpers
├── Extensions/
│   ├── ErrorOrIntegration.cs      # ErrorOr → Result mapping
│   ├── ExceptionExtensions.cs     # ToUnexpectedError, ToFailureError
│   ├── FluentValidationIntegration.cs # ValidationFailure → Result
│   ├── ResultFilter.cs            # IAsyncResultFilter — tự động set status code từ Result
│   ├── ResultLibApplicationBuilderExtensions.cs  # UseResultLibExceptionHandler
│   ├── ResultLibServiceCollectionExtensions.cs   # AddResultLibExceptionHandler, AddResultFilter
│   ├── ResultToIActionResultExtensions.cs        # Result → IActionResult
│   └── ExceptionHandler/
│       └── ResultLibExceptionHandler.cs  # Global exception handler (IExceptionHandler)
└── ResultLib.csproj

🔄 Flow xử lý Error

Error xảy ra
      │
      ▼
Log.ProcessError(msg) / Log.ProcessAndToResultError(msg)
      │
      ├──► msg có [MessageForUser]?
      │         │
      │         YES ──► Trả message cho user (KHÔNG log DB)
      │         │       VD: "Email không hợp lệ"
      │         │
      │         NO ───► Ghi log DB + Trả message chung cho user
      │                 VD: "Đã xảy ra lỗi. Vui lòng thử lại sau [#123]"
      │
      ▼
   Result { IsError, StatusCode, ErrorMessage }

Global Exception Handler Flow

Unhandled Exception
      │
      ▼
ResultLibExceptionHandler (IExceptionHandler)
      │
      ▼
Log.ProcessError(exception details)
      │
      ├──► System error → log DB + trả message chung
      │
      ▼
   HTTP 500 + Result error

🗃️ SQL Script tạo bảng LogError (nếu dùng EF Core)

IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'LogError')
BEGIN
    CREATE TABLE [dbo].[LogError]
    (
        [Id] BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
        [CreateDate] DATETIME NOT NULL DEFAULT (GETDATE()),
        [LogContent] NVARCHAR(4000) NOT NULL DEFAULT '',
        [TypeLog] TINYINT NOT NULL DEFAULT(0),  -- 0: System, 1: User
        [IsProcessed] BIT NOT NULL DEFAULT(0),
        [ProcessContent] NVARCHAR(MAX) NULL
    )
END

🌐 Cấu hình Đa ngôn ngữ (i18n) với ResultLibOptions

ResultLib cung cấp cơ chế delegate toàn cục để ứng dụng tùy biến thông báo lỗi hệ thống và thông báo thành công mặc định (tích hợp mượt mà với IStringLocalizer hoặc đa ngôn ngữ theo từng request):

// Trong Program.cs / Startup
using ResultLib.Core;

// Đổi thông báo lỗi hệ thống chung (khi có Exception / lỗi không show chi tiết cho user)
ResultLibOptions.GetGeneralErrorMessage = () => localizer["GeneralErrorMessage"].Value;

// Đổi thông báo thành công mặc định cho ErrorOr<Success>.ToResult()
ResultLibOptions.GetDefaultSuccessMessage = () => localizer["SuccessMessage"].Value;

🛡️ Hằng số Validation & FluentValidation Extensions

1. ValidationConstants

Cung cấp Regex và message mặc định chuẩn hóa cho dự án:

using ResultLib.Utils;

// Regex mật khẩu mạnh (tối thiểu 8 ký tự, 1 thường, 1 số, 1 ký tự đặc biệt)
var passwordRegex = ValidationConstants.Password.StrongRegex;

// Regex số điện thoại Việt Nam (0xxxxxxxxx hoặc +84xxxxxxxxx)
var phoneRegex = ValidationConstants.PhoneNumber.VietnamRegex;

// Độ dài tối đa email
var maxEmail = ValidationConstants.Email.MaxLength; // 100

2. Tiện ích mở rộng FluentValidation

Dùng trực tiếp trong các class AbstractValidator<T>:

using FluentValidation;
using ResultLib.Extensions;

public class RegisterValidator : AbstractValidator<RegisterRequest>
{
    public RegisterValidator()
    {
        // Kiểm tra mật khẩu mạnh với message chuẩn mặc định
        RuleFor(x => x.Password).MatchesStrongPassword();

        // Hoặc truyền message đa ngôn ngữ tùy biến
        RuleFor(x => x.Password).MatchesStrongPassword(localizer["PasswordWeak"].Value);

        // Kiểm tra số điện thoại Việt Nam
        RuleFor(x => x.PhoneNumber).MatchesVietnamPhoneNumber();
    }
}

Developed with ❤️ for robust .NET applications.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.5.0 93 9/13/2026
2.4.3 105 8/29/2026
2.4.2 105 8/23/2026
2.4.1 106 8/10/2026
2.4.0 119 8/6/2026
2.3.4 112 8/5/2026
2.3.3 100 8/5/2026
2.3.2 92 8/5/2026
2.3.0 103 8/5/2026
2.1.1 129 7/18/2026
2.1.0 109 7/18/2026
2.0.1 109 7/18/2026
2.0.0 121 6/27/2026