Skip to content

tahamucasiroglu/Return

Repository files navigation

TahaMucasiroglu.Return

NuGet Version License


Table of Contents




TR

Proje Tanitimi

TahaMucasiroglu.Return v2.0.0, C# dünyasinda metot ve servis cagrilariinizin donus degerlerini standartlastirmak icin tasarlanmis, kapsamli ve genisletilebilir bir kutuphanedir.

IReturn ve IReturn<T> arayuzleri ile hata, uyari, basari ve kritik durumlarini tek bir nesnede tutmanizi saglar.

Ana Hedefler

  • Kod tekrarini azaltmak: Her metodun farkli donus tipi yerine, IReturn/IReturn<T> ile tek tip sonuc nesnesi dondurmek
  • Merkezi hata yonetimi: Hata mesaji, exception, error code, severity ve veri (Data) tek bir objede toplanir
  • Otomatik loglama: Serilog veya MEL ile hic kod yazmadan tum islem sonuclarini loglamak
  • Try/Catch Wrapper:Elle try/catch yazmadan guvenli metot cagrilari yapmak
  • Fluent Builder API: Zincirleme metotlarla karmasik sonuc nesneleri olusturmak
  • Custom Error Codes: Lokalizasyon destekli ozel hata kodlari tanimlamak
  • SOLID & Clean Code: Immutable record yapilariyla thread-safe ve bakimi kolay altyapi

Ozellikler

Ozellik Aciklama
IReturn / IReturn<T> Standart donus arayuzleri
SuccessReturn / ErrorReturn / WarningReturn / CriticalReturn Hazir record tipleri
Result Factory Ok(), Fail(), Warn(), Critical() metodlari
Try/Catch Wrapper Result.Try() ile guvenli metot cagrilari
Fluent Builder Result.Create().WithSuccess().WithData(obj).Build()
Custom Error Codes ReturnErrorCode enum + localization
Severity Success, Warning, Error, Critical
Context TraceId, Timestamp, Metadata
Logging Serilog / MEL otomatik loglama
Mapping Return.Map ile dahili mapping destegi
ASP.NET Core Middleware + ToResponse() + IActionResult
Pure Minimal, zero-overhead surum
Structs GC-free struct alternatifleri

Paketler

Paket Surum Aciklama
TahaMucasiroglu.Return 2.0.0 Ana kutuphane
TahaMucasiroglu.Return.Pure 2.0.0 Minimal surum (logging, context yok)
TahaMucasiroglu.Return.Map 2.0.0 Expression tree tabanli mapping
TahaMucasiroglu.Return.Logging 2.0.0 Serilog / MEL otomatik loglama
TahaMucasiroglu.Return.AspNetCore 2.0.0 ASP.NET Core middleware + IActionResult

Kurulum

# Ana kutuphane
dotnet add package TahaMucasiroglu.Return

# Pure (minimal)
dotnet add package TahaMucasiroglu.Return.Pure

# Mapping destegi
dotnet add package TahaMucasiroglu.Return.Map

# Logging destegi
dotnet add package TahaMucasiroglu.Return.Logging

# ASP.NET Core entegrasyonu
dotnet add package TahaMucasiroglu.Return.AspNetCore

Hizli Baslangic

using Return.Abstract;
using Return.Factory;

// Basarili sonuc (data ile)
IReturn<string> success = Result.Ok("Merhaba Dunya");
Console.WriteLine(success.Status);   // true
Console.WriteLine(success.Data);     // "Merhaba Dunya"

// Basarisiz sonuc
IReturn error = Result.Fail("Bir hata olustu");
Console.WriteLine(error.Status);     // false
Console.WriteLine(error.Message);    // "Bir hata olustu"

// Data ile basarisiz sonuc
IReturn<int> notFound = Result.Fail<int>("Kullanici bulunamadi");
Console.WriteLine(notFound.Status);  // false
Console.WriteLine(notFound.Data);    // 0 (default)

Temel Tipler

IReturn (Veri Tasimayan)

public interface IReturn
{
    bool Status { get; }
    Severity Severity { get; }
    string? Message { get; }
    Exception? Exception { get; }
    int? ErrorCode { get; }
    ReturnContext? Context { get; init; }
}

IReturn (Veri Tasiyan)

public interface IReturn<T> : IReturn
{
    T? Data { get; init; }
    bool IsDataNull { get; }
    bool IsDataNotNull { get; }
    bool CheckStatusAndData { get; } // Status == true && Data != null
}

Concrete Tipler

using Return.Concrete;

// Basarili
new SuccessReturn();
new SuccessReturn("Islem basarili");
new SuccessReturn<string>("Merhaba");

// Hata
new ErrorReturn("Kayit bulunamadi");
new ErrorReturn<string>(default, "Kullanici yok");

// Uyari
new WarningReturn("Dusuk stok uyariisi");

// Kritik
new CriticalReturn("Veritabani baglantisi koptu");

Result Factory

Result sinifi ile tipleri bilmeden hizlica sonuc olusturun:

using Return.Factory;

// Basarili
Result.Ok();
Result.Ok("Islem basarili");
Result.Ok(myData);
Result.Ok(myData, "Kayit olusturuldu");

// Hata
Result.Fail("Hata mesaji");
Result.Fail(exception);
Result.Fail("Hata", exception);

// Hata (generic)
Result.Fail<User>("Kullanici bulunamadi");

// Uyari
Result.Warn("Stok azaliyor");
Result.Warn(data, "Veri eski olabilir");

// Kritik
Result.Critical("Servis disarida");
Result.Critical("Baglanti hatasi", ex);

Severity

public enum Severity
{
    Success,  // Basarili
    Warning,  // Uyari
    Error,    // Hata
    Critical  // Kritik
}

Her IReturn nesnesi bir Severity degeri tasir:

var result = Result.Warn("Disk dolmak uzere");
Console.WriteLine(result.Severity); // Warning

Try/Catch Wrapper

Elle try/catch yazmadan guvenli metot cagrilari:

using Return.Factory;

// Sync
IReturn result = Result.Try(() => RiskyOperation());
IReturn<User> result = Result.Try(() => GetUser(id));

// Async
IReturn result = await Result.TryAsync(() => RiskyAsyncOperation());
IReturn<User> result = await Result.TryAsync(() => GetUserAsync(id));

Ornek Kullanim (Servis Katmani):

public IReturn<Order> CreateOrder(CreateOrderRequest request)
{
    return Result.Try(() =>
    {
        var order = new Order(request.ProductId, request.Quantity);
        _repo.Add(order);
        return Result.Ok(order, "Siparis olusturuldu");
    });
}

Fluent Builder API

Zincirleme metotlarla karmasik sonuc nesneleri olusturun:

using Return.Factory;

var result = Result.Create()
    .WithSuccess()
    .WithData(user)
    .WithMessage("Kullanici getirildi")
    .WithTraceId()
    .WithTimestamp()
    .Build();
// => IReturn<User>

var error = Result.Create()
    .WithError()
    .WithMessage("Yetkisiz erisim")
    .WithErrorCode(ReturnErrorCode.Unauthorized)
    .WithContext("ip", "192.168.1.1")
    .Build();
// => IReturn

var warning = Result.Create()
    .WithWarning()
    .WithData(stocks)
    .WithMessage("Stok kritik seviyede")
    .Build();
// => IReturn<List<Stock>>

Custom Error Codes & Localization

Hata kodlarini standartlastirin ve dil destegi ekleyin.

Dil Ayari (Program.cs)

ReturnLanguage enum ile tum hata mesajlarinin dilini belirleyin.

using Return.Localization;

// Turkce
ReturnLocalization.Language = ReturnLanguage.Turkish;

// Ingilizce
ReturnLocalization.Language = ReturnLanguage.English;

// Sistem kulturunu kullan (varsayilan)
ReturnLocalization.Language = ReturnLanguage.Auto;

Program.cs ornegi:

using Return.Localization;

var builder = WebApplication.CreateBuilder(args);

// Tum hata mesajlari Turkce olsun
ReturnLocalization.Language = ReturnLanguage.Turkish;

builder.Services.AddControllers();
// ...

var app = builder.Build();
app.MapControllers();
app.Run();

Ozel Hata Kodlari

using Return.Localization;

// Toplu kayit (EN/TR)
ReturnLocalization.Register(new Dictionary<int, (string en, string tr)>
{
    [6001] = ("Payment failed", "Odeme basarisiz"),
    [6002] = ("Insufficient balance", "Yetersiz bakiye"),
    [6003] = ("Order expired", "Siparis suresi doldu")
});

// Kullanim - dil ayarina gore otomatik cozumlenir
var result = Result.Fail(6001);
// ReturnLanguage.Turkish  => "Odeme basarisiz"
// ReturnLanguage.English => "Payment failed"

ReturnLanguage Enum

Deger Aciklama
ReturnLanguage.Auto Sistem kulturunu kullanir (varsayilan)
ReturnLanguage.English Tum mesajlar Ingilizce
ReturnLanguage.Turkish Tum mesajlar Turkce

Ileride yeni diller eklendiginde enum'a German, French gibi degerler eklenir.

Hazir Error Code'lar

Kod Araligi Kategori Ornekler
1001-1999 Not Found NotFound, AlreadyExists, DataIsNull
2001-2999 Validation ValidationError, InvalidFormat, RequiredFieldMissing
3001-3999 Auth Unauthorized, Forbidden, TokenExpired
4001-4999 System DatabaseError, ConnectionFailed, Timeout
5001-5999 Business BusinessRuleViolation, OperationCancelled, Conflict
9001 General Unknown

Extension Metotlari

using Return.Extensions;

var result = Result.Ok(user);

// Kontrol
result.IsSuccess();  // true
result.IsError();    // false

// Veri alma
var data = result.GetDataOrDefault(new User());

// Pattern matching
result.Match(
    onSuccess: r => Console.WriteLine("Basarili"),
    onError: r => Console.WriteLine("Hata")
);

// Callback
result.OnSuccess(r => ProcessUser(r.Data));
result.OnError(r => LogError(r.Message));

// Transformasyon
var name = result.Map(u => u.Name);        // IReturn<string>
var orders = result.Bind(u => GetOrders(u.Id)); // IReturn<List<Order>>

// Side effect
result.Tap(r => Console.WriteLine(r.Data.Name));

// Kosul
result.Ensure(r => r.Data.IsActive, "Kullanici pasif");

Async Extension'lar

using Return.Extensions;

var tasks = new[]
{
    GetUserAsync(1),
    GetUserAsync(2),
    GetUserAsync(3)
};

// Tumunu bekle
var all = await tasks.WhenAllReturn();

// Ilk biteni bekle
var first = await tasks.WhenAnyReturn();

// Birlestir
var combined = await tasks.CombineAllAsync();

ASP.NET Core Entegrasyonu

Middleware

// Program.cs
using Return.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// Exception handler - tum exception'lari IReturn formatinda doner
builder.Services.AddControllers();

var app = builder.Build();
app.UseReturnExceptionHandler(); // Global exception handling
app.UseReturnResponse();         // IReturn response serialization
app.MapControllers();
app.Run();

ToResponse() - JSON Response

using Return.Extensions;

[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var result = _service.GetById(id);
    return result.Status ? Ok(result.ToResponse()) : NotFound(result.ToResponse());
}

ToResponse() ciktisi:

{
  "status": true,
  "message": "Blog created",
  "errorCode": null,
  "severity": "Success",
  "data": { "id": 1, "title": "Merhaba", "author": "Taha" }
}

IActionResult Wrapper

using Return.AspNetCore.Results;

[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var result = _service.GetById(id);
    return new ReturnResult<User>(result);
    // Success -> 200, Error -> 500, Critical -> 503
}

Logging (Otomatik Loglama)

Return.Logging ile tum IReturn islemleri otomatik loglanir. Service katmaninda hic log kodu yazmaniza gerek yok.

Serilog ile Program.cs

Ornek: Test/AutoMapper_Serilog_Return/Api/Program.cs

using Api.Services;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Return.Logging;
using Serilog;
using Api.Mapping;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddAutoMapper(typeof(BlogProfile));
builder.Services.AddScoped<IBlogService, BlogService>();

// Return.Logging - tum IReturn islemlerini otomatik logla
builder.Services.AddReturnLogging();

var app = builder.Build();
app.MapControllers();
app.Run("http://localhost:5001");

Service Katmani (Log Kodu YOK!)

Ornek: Test/AutoMapper_Serilog_Return/Api/Services/BlogService.cs

using Return.Abstract;
using Return.Factory;

public class BlogService : IBlogService
{
    private readonly IBlogRepository _repo;

    public IReturn<List<BlogDto>> GetAll()
    {
        var blogs = _repo.GetAll();
        var dtos = _mapper.Map<List<BlogDto>>(blogs);
        return Result.Ok(dtos);
        // Return.Logging otomatik logluyor!
    }

    public IReturn<BlogDto> GetById(int id)
    {
        var blog = _repo.GetById(id);
        if (blog is null) return Result.Fail<BlogDto>($"Blog {id} not found");
        return Result.Ok(_mapper.Map<BlogDto>(blog));
    }
}

Mapping Destegi

Return.Map, expression tree tabanli yuksek performansli mapping saglar.

Program.cs

Ornek: Test/Default_None_Return/Api/Program.cs

using Return.Map.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddScoped<IBlogRepository, BlogRepository>();

// Return.Map - dahili mapping destegi
builder.Services.AddReturnMapper();

builder.Services.AddScoped<IBlogService, BlogService>();

var app = builder.Build();
app.MapControllers();
app.Run();

Service ile Kullanim

Ornek: Test/Default_None_Return/Api/Services/BlogService.cs

public IReturn<List<BlogDto>> GetAll()
{
    var blogs = _repo.GetAll();
    var dtos = blogs.Select(b => new BlogDto
    {
        Id = b.Id, Title = b.Title,
        Content = b.Content, Author = b.Author,
        CreatedAt = b.CreatedAt
    }).ToList();
    return Result.Ok(dtos);
}

Pure (Minimal Surum)

Return.Pure, logging, context, severity, error code gibi ozellikleri icermez. Manuel kontrol isteyen projeler icin idealdir.

Farklar

Ozellik Return (Full) Pure
Logging Otomatik Manuel
Context/TraceId Var Yok
Severity Var Yok
ErrorCode Var Yok
Result Factory Var Yok (dogrudan new)
Struct tipler Var Yok

Pure ile Service Katmani

Ornek: Test/AutoMapper_Serilog_Pure/Api/Services/BlogService.cs

using Return.Pure.Concrete;
using Return.Pure.Abstract;
using Microsoft.Extensions.Logging;

public class BlogService : IBlogService
{
    private readonly IBlogRepository _repo;
    private readonly ILogger<BlogService> _logger;

    public IReturn<BlogDto> GetById(int id)
    {
        try
        {
            var blog = _repo.GetById(id);
            if (blog is null)
                return new ErrorReturn<BlogDto>(default, $"Blog {id} not found");
            return new SuccessReturn<BlogDto>(_mapper.Map<BlogDto>(blog));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "GetById failed");
            return new ErrorReturn<BlogDto>(default, ex.Message);
        }
    }
}

Pure Controller (Manuel Response)

Ornek: Test/AutoMapper_Serilog_Pure/Api/Controllers/BlogController.cs

[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var r = _svc.GetById(id);
    return r.Status
        ? Ok(new { status = true, data = r.Data })
        : NotFound(new { status = false, message = r.Message });
}

API Response

Return (Full) - ToResponse()

Full Return ile controller'da ToResponse() cagirilir, data otomatik serialize olur:

// Return Full - ToResponse() ile otomatik JSON
[HttpGet]
public IActionResult GetAll() => Ok(_service.GetAll().ToResponse());

Cikti:

{
  "status": true,
  "message": null,
  "errorCode": null,
  "severity": "Success",
  "data": [
    { "id": 1, "title": "Blog 1", "content": "...", "author": "Taha" }
  ]
}

Pure - Manuel Response

Pure'da response'u kendiniz olusturursunuz:

// Pure - Manuel response olusturma
[HttpGet]
public IActionResult GetAll()
{
    var r = _svc.GetAll();
    return Ok(new { status = true, data = r.Data });
}

Program.cs Ornekleri

Tum kombinasyonlar icin Test/ klasorunde 18 canli proje bulunur. En sik kullanilan 3 kurulum:

1. Full Return + Serilog + AutoMapper

Kaynak: Test/AutoMapper_Serilog_Return/Api/Program.cs

using Api.Services;
using Api.Mapping;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Return.Logging;
using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddAutoMapper(typeof(BlogProfile));
builder.Services.AddScoped<IBlogService, BlogService>();
builder.Services.AddReturnLogging();

var app = builder.Build();
app.MapControllers();
app.Run();

2. Full Return + Return.Map (Logger Yok)

Kaynak: Test/Default_None_Return/Api/Program.cs

using Api.Services;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Return.Map.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddReturnMapper();
builder.Services.AddScoped<IBlogService, BlogService>();

var app = builder.Build();
app.MapControllers();
app.Run();

3. Pure + Serilog (Manuel Loglama)

Kaynak: Test/AutoMapper_Serilog_Pure/Api/Program.cs

using Api.Services;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Serilog;
using AutoMapper;
using Api.Mapping;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddAutoMapper(typeof(BlogProfile));
builder.Services.AddScoped<IBlogService, BlogService>();

var app = builder.Build();
app.MapControllers();
app.Run();

Test Projesi

Test/ klasorunde 18 farkli kombinasyonla Blog CRUD uygulamasi bulunur:

Kombinasyon Adedi Aciklama
AutoMapper x Serilog x Return/Pure 2 Reflection mapping + structed logging
AutoMapper x MEL x Return/Pure 2 Reflection mapping + Microsoft logging
AutoMapper x None x Return/Pure 2 Reflection mapping, log yok
Mapster x Serilog x Return/Pure 2 Code-gen mapping + structed logging
Mapster x MEL x Return/Pure 2 Code-gen mapping + Microsoft logging
Mapster x None x Return/Pure 2 Code-gen mapping, log yok
Default x Serilog x Return/Pure 2 Return.Map + structed logging
Default x MEL x Return/Pure 2 Return.Map + Microsoft logging
Default x None x Return/Pure 2 Return.Map, log yok

Her proje ayri port ve ayri veritabani kullanir. Tumu build ve CRUD testlerinden gecmistir.

Benchmark sonuclari icin: Test/Blog.Benchmark/RESULTS.md

Kapsamli Benchmark: Tum Return tipleri, mapper'lar, validation, logging, struct vs record, Pure vs Full karsilastirmalari icin → Benchmark.md


Roadmap

  • Core IReturn / IReturn arayuzleri
  • Result Factory (Ok, Fail, Warn, Critical)
  • Severity (Success, Warning, Error, Critical)
  • Context (TraceId, Timestamp, Metadata)
  • Try/Catch Wrapper (sync + async)
  • Fluent Builder API
  • Custom Error Codes & Localization (EN/TR)
  • Logging (Serilog + MEL)
  • Return.Map (Expression tree mapping)
  • Return.Pure (Minimal surum)
  • Return.AspNetCore (Middleware + IActionResult)
  • ToResponse() JSON serialization
  • Struct tipler (GC-free)
  • Extension metotlari (Match, Bind, Map, Tap, Ensure)
  • Async extension'lar (WhenAll, WhenAny)
  • 18 kombinasyonlu canli test projeleri
  • BenchmarkDotNet performans testi
  • Swagger/OpenAPI entegrasyonu
  • CLI araclari (dotnet return init)
  • Daha fazla validation destegi



EN

Project Overview

TahaMucasiroglu.Return v2.0.0 is a comprehensive and extensible library designed to standardize return values of method and service calls in C#.

With IReturn and IReturn<T> interfaces, it encapsulates error, warning, success, and critical states in a single object.

Main Goals

  • Reduce code duplication: Return a single result object using IReturn/IReturn<T> instead of different return types
  • Centralized error handling: Error message, exception, error code, severity, and data (Data) in one object
  • Automatic logging: Log all operation results with Serilog or MEL without writing any logging code
  • Try/Catch Wrapper: Safe method calls without manual try/catch blocks
  • Fluent Builder API: Build complex result objects with chained method calls
  • Custom Error Codes: Define custom error codes with localization support
  • SOLID & Clean Code: Thread-safe and maintainable infrastructure with immutable record types

Features

Feature Description
IReturn / IReturn<T> Standard return interfaces
SuccessReturn / ErrorReturn / WarningReturn / CriticalReturn Predefined record types
Result Factory Ok(), Fail(), Warn(), Critical() methods
Try/Catch Wrapper Safe method calls with Result.Try()
Fluent Builder Result.Create().WithSuccess().WithData(obj).Build()
Custom Error Codes ReturnErrorCode enum + localization
Severity Success, Warning, Error, Critical
Context TraceId, Timestamp, Metadata
Logging Serilog / MEL automatic logging
Mapping Built-in mapping via Return.Map
ASP.NET Core Middleware + ToResponse() + IActionResult
Pure Minimal, zero-overhead version
Structs GC-free struct alternatives

Packages

Package Version Description
TahaMucasiroglu.Return 2.0.0 Core library
TahaMucasiroglu.Return.Pure 2.0.0 Minimal version (no logging, context)
TahaMucasiroglu.Return.Map 2.0.0 Expression tree-based mapping
TahaMucasiroglu.Return.Logging 2.0.0 Serilog / MEL automatic logging
TahaMucasiroglu.Return.AspNetCore 2.0.0 ASP.NET Core middleware + IActionResult

Installation

# Core library
dotnet add package TahaMucasiroglu.Return

# Pure (minimal)
dotnet add package TahaMucasiroglu.Return.Pure

# Mapping support
dotnet add package TahaMucasiroglu.Return.Map

# Logging support
dotnet add package TahaMucasiroglu.Return.Logging

# ASP.NET Core integration
dotnet add package TahaMucasiroglu.Return.AspNetCore

Quick Start

using Return.Abstract;
using Return.Factory;

// Success with data
IReturn<string> success = Result.Ok("Hello World");
Console.WriteLine(success.Status);   // true
Console.WriteLine(success.Data);     // "Hello World"

// Error
IReturn error = Result.Fail("An error occurred");
Console.WriteLine(error.Status);     // false
Console.WriteLine(error.Message);    // "An error occurred"

// Error with generic type
IReturn<int> notFound = Result.Fail<int>("User not found");
Console.WriteLine(notFound.Status);  // false
Console.WriteLine(notFound.Data);    // 0 (default)

Core Types

IReturn (Non-Generic)

public interface IReturn
{
    bool Status { get; }
    Severity Severity { get; }
    string? Message { get; }
    Exception? Exception { get; }
    int? ErrorCode { get; }
    ReturnContext? Context { get; init; }
}

IReturn (Generic with Data)

public interface IReturn<T> : IReturn
{
    T? Data { get; init; }
    bool IsDataNull { get; }
    bool IsDataNotNull { get; }
    bool CheckStatusAndData { get; } // Status == true && Data != null
}

Concrete Types

using Return.Concrete;

// Success
new SuccessReturn();
new SuccessReturn("Operation successful");
new SuccessReturn<string>("Hello");

// Error
new ErrorReturn("Record not found");
new ErrorReturn<string>(default, "User not found");

// Warning
new WarningReturn("Low stock warning");

// Critical
new CriticalReturn("Database connection lost");

Result Factory {#result-factory-en}

Create results without knowing concrete types:

using Return.Factory;

// Success
Result.Ok();
Result.Ok("Operation successful");
Result.Ok(myData);
Result.Ok(myData, "Record created");

// Error
Result.Fail("Error message");
Result.Fail(exception);
Result.Fail("Error", exception);

// Error (generic)
Result.Fail<User>("User not found");

// Warning
Result.Warn("Stock running low");
Result.Warn(data, "Data may be stale");

// Critical
Result.Critical("Service unavailable");
Result.Critical("Connection failed", ex);

Severity Levels

public enum Severity
{
    Success,  // Operation succeeded
    Warning,  // Warning condition
    Error,    // Error occurred
    Critical  // Critical failure
}

Every IReturn object carries a Severity value:

var result = Result.Warn("Disk space running low");
Console.WriteLine(result.Severity); // Warning

Try/Catch Wrapper {#trycatch-wrapper-en}

Safe method calls without manual try/catch blocks:

using Return.Factory;

// Sync
IReturn result = Result.Try(() => RiskyOperation());
IReturn<User> result = Result.Try(() => GetUser(id));

// Async
IReturn result = await Result.TryAsync(() => RiskyAsyncOperation());
IReturn<User> result = await Result.TryAsync(() => GetUserAsync(id));

Service Layer Example:

public IReturn<Order> CreateOrder(CreateOrderRequest request)
{
    return Result.Try(() =>
    {
        var order = new Order(request.ProductId, request.Quantity);
        _repo.Add(order);
        return Result.Ok(order, "Order created");
    });
}

Fluent Builder API {#fluent-builder-api-en}

Build complex result objects with chained method calls:

using Return.Factory;

var result = Result.Create()
    .WithSuccess()
    .WithData(user)
    .WithMessage("User retrieved")
    .WithTraceId()
    .WithTimestamp()
    .Build();
// => IReturn<User>

var error = Result.Create()
    .WithError()
    .WithMessage("Unauthorized access")
    .WithErrorCode(ReturnErrorCode.Unauthorized)
    .WithContext("ip", "192.168.1.1")
    .Build();
// => IReturn

var warning = Result.Create()
    .WithWarning()
    .WithData(stocks)
    .WithMessage("Stock at critical level")
    .Build();
// => IReturn<List<Stock>>

Custom Error Codes & Localization {#custom-error-codes--localization-en}

Standardize error codes with multi-language support.

Language Setting (Program.cs)

Set the language for all error messages using the ReturnLanguage enum.

using Return.Localization;

// Turkish
ReturnLocalization.Language = ReturnLanguage.Turkish;

// English
ReturnLocalization.Language = ReturnLanguage.English;

// Use system culture (default)
ReturnLocalization.Language = ReturnLanguage.Auto;

Program.cs example:

using Return.Localization;

var builder = WebApplication.CreateBuilder(args);

// All error messages in Turkish
ReturnLocalization.Language = ReturnLanguage.Turkish;

builder.Services.AddControllers();
// ...

var app = builder.Build();
app.MapControllers();
app.Run();

Custom Error Codes

using Return.Localization;

// Bulk registration (EN/TR)
ReturnLocalization.Register(new Dictionary<int, (string en, string tr)>
{
    [6001] = ("Payment failed", "Odeme basarisiz"),
    [6002] = ("Insufficient balance", "Yetersiz bakiye"),
    [6003] = ("Order expired", "Siparis suresi doldu")
});

// Usage - resolves automatically based on language setting
var result = Result.Fail(6001);
// ReturnLanguage.Turkish  => "Odeme basarisiz"
// ReturnLanguage.English => "Payment failed"

ReturnLanguage Enum

Value Description
ReturnLanguage.Auto Uses system culture (default)
ReturnLanguage.English All messages in English
ReturnLanguage.Turkish All messages in Turkish

When new languages are added in the future, values like German, French will be added to the enum.

Built-in Error Codes

Code Range Category Examples
1001-1999 Not Found NotFound, AlreadyExists, DataIsNull
2001-2999 Validation ValidationError, InvalidFormat, RequiredFieldMissing
3001-3999 Auth Unauthorized, Forbidden, TokenExpired
4001-4999 System DatabaseError, ConnectionFailed, Timeout
5001-5999 Business BusinessRuleViolation, OperationCancelled, Conflict
9001 General Unknown

Extension Methods

using Return.Extensions;

var result = Result.Ok(user);

// Checks
result.IsSuccess();  // true
result.IsError();    // false

// Get data
var data = result.GetDataOrDefault(new User());

// Pattern matching
result.Match(
    onSuccess: r => Console.WriteLine("Success"),
    onError: r => Console.WriteLine("Error")
);

// Callbacks
result.OnSuccess(r => ProcessUser(r.Data));
result.OnError(r => LogError(r.Message));

// Transformations
var name = result.Map(u => u.Name);              // IReturn<string>
var orders = result.Bind(u => GetOrders(u.Id));   // IReturn<List<Order>>

// Side effects
result.Tap(r => Console.WriteLine(r.Data.Name));

// Conditions
result.Ensure(r => r.Data.IsActive, "User is inactive");

Async Extensions

using Return.Extensions;

var tasks = new[]
{
    GetUserAsync(1),
    GetUserAsync(2),
    GetUserAsync(3)
};

// Wait for all
var all = await tasks.WhenAllReturn();

// Wait for first
var first = await tasks.WhenAnyReturn();

// Combine
var combined = await tasks.CombineAllAsync();

ASP.NET Core Integration

Middleware

// Program.cs
using Return.AspNetCore;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

var app = builder.Build();
app.UseReturnExceptionHandler(); // Global exception handling -> IReturn JSON
app.UseReturnResponse();         // IReturn response serialization
app.MapControllers();
app.Run();

ToResponse() - JSON Response

using Return.Extensions;

[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var result = _service.GetById(id);
    return result.Status ? Ok(result.ToResponse()) : NotFound(result.ToResponse());
}

ToResponse() output:

{
  "status": true,
  "message": "Blog created",
  "errorCode": null,
  "severity": "Success",
  "data": { "id": 1, "title": "Hello", "author": "Taha" }
}

IActionResult Wrapper

using Return.AspNetCore.Results;

[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var result = _service.GetById(id);
    return new ReturnResult<User>(result);
    // Success -> 200, Error -> 500, Critical -> 503
}

Logging (Auto-Logging)

With Return.Logging, all IReturn operations are logged automatically. No logging code needed in your service layer.

Program.cs with Serilog

Reference: Test/AutoMapper_Serilog_Return/Api/Program.cs

using Api.Services;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Return.Logging;
using Serilog;
using Api.Mapping;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddAutoMapper(typeof(BlogProfile));
builder.Services.AddScoped<IBlogService, BlogService>();

// Return.Logging - automatically log all IReturn operations
builder.Services.AddReturnLogging();

var app = builder.Build();
app.MapControllers();
app.Run("http://localhost:5001");

Service Layer (NO Logging Code!)

Reference: Test/AutoMapper_Serilog_Return/Api/Services/BlogService.cs

using Return.Abstract;
using Return.Factory;

public class BlogService : IBlogService
{
    private readonly IBlogRepository _repo;

    public IReturn<List<BlogDto>> GetAll()
    {
        var blogs = _repo.GetAll();
        var dtos = _mapper.Map<List<BlogDto>>(blogs);
        return Result.Ok(dtos);
        // Return.Logging handles the logging automatically!
    }

    public IReturn<BlogDto> GetById(int id)
    {
        var blog = _repo.GetById(id);
        if (blog is null) return Result.Fail<BlogDto>($"Blog {id} not found");
        return Result.Ok(_mapper.Map<BlogDto>(blog));
    }
}

Mapping Support

Return.Map provides expression tree-based high-performance mapping.

Program.cs

Reference: Test/Default_None_Return/Api/Program.cs

using Return.Map.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddScoped<IBlogRepository, BlogRepository>();

// Return.Map - built-in mapping support
builder.Services.AddReturnMapper();

builder.Services.AddScoped<IBlogService, BlogService>();

var app = builder.Build();
app.MapControllers();
app.Run();

Pure (Minimal Version)

Return.Pure excludes logging, context, severity, error codes. Ideal for projects that want manual control.

Differences

Feature Return (Full) Pure
Logging Automatic Manual
Context/TraceId Yes No
Severity Yes No
ErrorCode Yes No
Result Factory Yes No (use new directly)
Struct types Yes No

Pure Service Layer

Reference: Test/AutoMapper_Serilog_Pure/Api/Services/BlogService.cs

using Return.Pure.Concrete;
using Return.Pure.Abstract;
using Microsoft.Extensions.Logging;

public class BlogService : IBlogService
{
    private readonly IBlogRepository _repo;
    private readonly ILogger<BlogService> _logger;

    public IReturn<BlogDto> GetById(int id)
    {
        try
        {
            var blog = _repo.GetById(id);
            if (blog is null)
                return new ErrorReturn<BlogDto>(default, $"Blog {id} not found");
            return new SuccessReturn<BlogDto>(_mapper.Map<BlogDto>(blog));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "GetById failed");
            return new ErrorReturn<BlogDto>(default, ex.Message);
        }
    }
}

Pure Controller (Manual Response)

Reference: Test/AutoMapper_Serilog_Pure/Api/Controllers/BlogController.cs

[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var r = _svc.GetById(id);
    return r.Status
        ? Ok(new { status = true, data = r.Data })
        : NotFound(new { status = false, message = r.Message });
}

API Response (ToResponse) {#api-response-totresponse}

Return (Full) - ToResponse()

With Full Return, call ToResponse() in your controller. Data is automatically serialized:

// Full Return - automatic JSON via ToResponse()
[HttpGet]
public IActionResult GetAll() => Ok(_service.GetAll().ToResponse());

Output:

{
  "status": true,
  "message": null,
  "errorCode": null,
  "severity": "Success",
  "data": [
    { "id": 1, "title": "Blog 1", "content": "...", "author": "Taha" }
  ]
}

Pure - Manual Response

With Pure, you build the response yourself:

// Pure - manual response construction
[HttpGet]
public IActionResult GetAll()
{
    var r = _svc.GetAll();
    return Ok(new { status = true, data = r.Data });
}

Program.cs Examples

The Test/ directory contains 18 live projects with all combinations. Here are the 3 most common setups:

1. Full Return + Serilog + AutoMapper

Source: Test/AutoMapper_Serilog_Return/Api/Program.cs

using Api.Services;
using Api.Mapping;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Return.Logging;
using Serilog;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddAutoMapper(typeof(BlogProfile));
builder.Services.AddScoped<IBlogService, BlogService>();
builder.Services.AddReturnLogging();

var app = builder.Build();
app.MapControllers();
app.Run();

2. Full Return + Return.Map (No Logger)

Source: Test/Default_None_Return/Api/Program.cs

using Api.Services;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Return.Map.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddReturnMapper();
builder.Services.AddScoped<IBlogService, BlogService>();

var app = builder.Build();
app.MapControllers();
app.Run();

3. Pure + Serilog (Manual Logging)

Source: Test/AutoMapper_Serilog_Pure/Api/Program.cs

using Api.Services;
using Blog.Domain.Interfaces;
using Blog.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Serilog;
using AutoMapper;
using Api.Mapping;

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

builder.Services.AddControllers();
builder.Services.AddDbContext<BlogDbContext>(opt =>
    opt.UseSqlServer(@"Server=(localdb)\MSSQLLocalDB;Database=BlogDb;Trusted_Connection=True;"));
builder.Services.AddScoped<IBlogRepository, BlogRepository>();
builder.Services.AddAutoMapper(typeof(BlogProfile));
builder.Services.AddScoped<IBlogService, BlogService>();

var app = builder.Build();
app.MapControllers();
app.Run();

Test Project

The Test/ directory contains 18 Blog CRUD applications with different combinations:

Combination Count Description
AutoMapper x Serilog x Return/Pure 2 Reflection mapping + structured logging
AutoMapper x MEL x Return/Pure 2 Reflection mapping + Microsoft logging
AutoMapper x None x Return/Pure 2 Reflection mapping, no logging
Mapster x Serilog x Return/Pure 2 Code-gen mapping + structured logging
Mapster x MEL x Return/Pure 2 Code-gen mapping + Microsoft logging
Mapster x None x Return/Pure 2 Code-gen mapping, no logging
Default x Serilog x Return/Pure 2 Return.Map + structured logging
Default x MEL x Return/Pure 2 Return.Map + Microsoft logging
Default x None x Return/Pure 2 Return.Map, no logging

Each project uses a separate port and database. All have passed build and CRUD live tests (18/18).

For benchmark results: Test/Blog.Benchmark/RESULTS.md

Comprehensive Benchmark: All Return types, mappers, validation, logging, struct vs record, Pure vs Full comparisons → Benchmark.md


Roadmap {#roadmap-en}

  • Core IReturn / IReturn interfaces
  • Result Factory (Ok, Fail, Warn, Critical)
  • Severity (Success, Warning, Error, Critical)
  • Context (TraceId, Timestamp, Metadata)
  • Try/Catch Wrapper (sync + async)
  • Fluent Builder API
  • Custom Error Codes & Localization (EN/TR)
  • Logging (Serilog + MEL)
  • Return.Map (Expression tree mapping)
  • Return.Pure (Minimal version)
  • Return.AspNetCore (Middleware + IActionResult)
  • ToResponse() JSON serialization
  • Struct types (GC-free)
  • Extension methods (Match, Bind, Map, Tap, Ensure)
  • Async extensions (WhenAll, WhenAny)
  • 18-combination live test projects
  • BenchmarkDotNet performance tests
  • Swagger/OpenAPI integration
  • CLI tools (dotnet return init)
  • Extended validation support

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors