ErginWebDev.StronglyTypedIds
1.0.5
dotnet add package ErginWebDev.StronglyTypedIds --version 1.0.5
NuGet\Install-Package ErginWebDev.StronglyTypedIds -Version 1.0.5
<PackageReference Include="ErginWebDev.StronglyTypedIds" Version="1.0.5" />
<PackageVersion Include="ErginWebDev.StronglyTypedIds" Version="1.0.5" />
<PackageReference Include="ErginWebDev.StronglyTypedIds" />
paket add ErginWebDev.StronglyTypedIds --version 1.0.5
#r "nuget: ErginWebDev.StronglyTypedIds, 1.0.5"
#:package ErginWebDev.StronglyTypedIds@1.0.5
#addin nuget:?package=ErginWebDev.StronglyTypedIds&version=1.0.5
#tool nuget:?package=ErginWebDev.StronglyTypedIds&version=1.0.5
ErginWebDev.StronglyTypedIds
A modern, DDD-compliant library for creating strongly typed IDs in .NET applications with automatic integration for Entity Framework Core, System.Text.Json, and OpenAPI (Swagger/Scalar). Supports both .NET 8.0 and .NET 9.0.
Table of Contents
- Why Strongly Typed IDs?
- Features
- Installation
- Quick Start
- Supported Value Types
- Detailed Usage
- Advanced Scenarios
- Performance Considerations
- Migration Guide
- Contributing
- License
Why Strongly Typed IDs?
Instead of using primitive types like Guid or int directly, strongly typed IDs provide:
✅ Type Safety: Prevents mixing different entity IDs at compile-time
✅ Code Clarity: FindCustomer(CustomerId id) is clearer than FindCustomer(Guid id)
✅ Refactoring Safety: Compiler errors when changing ID types
✅ DDD Compliance: Follows Domain-Driven Design value object pattern
✅ Zero Boilerplate: Automatic EF Core, JSON, and OpenAPI integration
Before (Primitive Obsession)
public class OrderService
{
// ❌ Easy to mix up customer and order IDs
public Order CreateOrder(Guid customerId, Guid productId) { }
// ❌ Accidentally swapping parameters compiles but fails at runtime
var order = CreateOrder(productId, customerId); // WRONG!
}
After (Strongly Typed IDs)
public class OrderService
{
// ✅ Type-safe parameters
public Order CreateOrder(CustomerId customerId, ProductId productId) { }
// ✅ Compile error prevents mistakes
var order = CreateOrder(productId, customerId); // Won't compile!
}
Features
- 🎯 Generic Base Type: Support for
Guid,int,long,string,decimal,double,DateTime,DateTimeOffset, andEnum - 🗃️ EF Core Convention: Automatic value converter registration
- 📦 JSON Serialization: Seamless System.Text.Json integration
- 📝 OpenAPI Support: Works with Swagger UI and Scalar API
- ⚡ High Performance: Reflection only at startup, zero runtime overhead
- 🔧 Zero Configuration: Convention-based auto-discovery
- 🎨 Clean Code: Minimal boilerplate with
record struct
Installation
dotnet add package ErginWebDev.StronglyTypedIds
Requirements:
- .NET 8.0 or later (.NET 8.0 and .NET 9.0 are both supported)
- Entity Framework Core 8.0+ (if using EF Core integration)
- Swashbuckle.AspNetCore 6.8+ (if using Swagger)
Quick Start
1. Define Your Strongly Typed IDs
using ErginWebDev.StronglyTypedIds.ValueObjects;
// Guid-based IDs
public readonly record struct CustomerId(Guid Value) : StronglyTypedId<Guid>(Value);
public readonly record struct OrderId(Guid Value) : StronglyTypedId<Guid>(Value);
// Other value types
public readonly record struct OrderNumber(int Value) : StronglyTypedId<int>(Value);
public readonly record struct ProductCode(string Value) : StronglyTypedId<string>(Value);
public readonly record struct Price(decimal Value) : StronglyTypedId<decimal>(Value);
2. Use in Your Domain Entities
public class Customer
{
public CustomerId Id { get; init; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
public class Order
{
public OrderId Id { get; init; }
public CustomerId CustomerId { get; init; }
public OrderNumber OrderNumber { get; init; }
public Price TotalPrice { get; init; }
public DateTime CreatedAt { get; init; }
}
3. Configure Services (Program.cs)
using ErginWebDev.StronglyTypedIds;
var builder = WebApplication.CreateBuilder(args);
// Add strongly typed IDs support
builder.Services.AddStronglyTypedIds(); // JSON + Swagger
// Add your DbContext
builder.Services.AddDbContext<AppDbContext>();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.Run();
4. Configure EF Core (DbContext)
public class AppDbContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Enable automatic strongly typed ID conversion
configurationBuilder.ConfigureStronglyTypedIds();
}
}
5. Use in Controllers
[ApiController]
[Route("api/[controller]")]
public class CustomersController : ControllerBase
{
private readonly AppDbContext _context;
public CustomersController(AppDbContext context) => _context = context;
[HttpGet("{id}")]
public async Task<ActionResult<Customer>> GetCustomer(CustomerId id)
{
// ID automatically binds from URL: "550e8400-e29b-41d4-a716-446655440000"
var customer = await _context.Customers.FindAsync(id);
if (customer == null)
return NotFound();
return customer; // Automatically serialized to JSON
}
[HttpPost]
public async Task<ActionResult<Customer>> CreateCustomer(CreateCustomerRequest request)
{
var customer = new Customer
{
Id = new CustomerId(Guid.CreateVersion7()), // Time-ordered GUID
Name = request.Name,
Email = request.Email
};
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetCustomer), new { id = customer.Id }, customer);
}
}
That's it! Your strongly typed IDs now work seamlessly across your entire application.
Supported Value Types
| Type | Storage | JSON Format | OpenAPI Type | Example |
|---|---|---|---|---|
Guid |
uniqueidentifier | string | string/uuid |
"550e8400-e29b-41d4-a716-446655440000" |
int |
int | number | integer/int32 |
12345 |
long |
bigint | number | integer/int64 |
9999999999 |
string |
nvarchar | string | string |
"PROD-001" |
decimal |
decimal | number | number/decimal |
99.95 |
double |
float | number | number/double |
123.456 |
DateTime |
datetime2 | string | string/date-time |
"2025-11-08T10:30:00" |
DateTimeOffset |
datetimeoffset | string | string/date-time |
"2025-11-08T10:30:00Z" |
Enum |
int | string | string + enum values |
"Active" |
Detailed Usage
Entity Framework Core Integration
The library uses EF Core's convention system to automatically detect and convert strongly typed IDs.
How It Works
public class AppDbContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
public DbSet<Product> Products => Set<Product>();
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// This single line enables automatic conversion for ALL strongly typed IDs
configurationBuilder.ConfigureStronglyTypedIds();
// The convention scans all entity properties and detects:
// - Value types (struct)
// - With a "Value" property
// - Of a supported type (Guid, int, long, string, etc.)
// Then automatically applies the appropriate converter
}
}
Database Storage
Strongly typed IDs are stored as their underlying value type:
-- Customer table
CREATE TABLE Customers (
Id uniqueidentifier PRIMARY KEY, -- CustomerId stored as Guid
Name nvarchar(max),
Email nvarchar(max)
);
-- Order table
CREATE TABLE Orders (
Id uniqueidentifier PRIMARY KEY, -- OrderId stored as Guid
CustomerId uniqueidentifier NOT NULL, -- CustomerId stored as Guid
OrderNumber int NOT NULL, -- OrderNumber stored as int
TotalPrice decimal(18,2) NOT NULL, -- Price stored as decimal
FOREIGN KEY (CustomerId) REFERENCES Customers(Id)
);
Querying
All standard EF Core query operations work seamlessly:
// Find by ID
var customer = await context.Customers.FindAsync(customerId);
// Where clause
var orders = await context.Orders
.Where(o => o.CustomerId == customerId)
.ToListAsync();
// Join operations
var customerOrders = await context.Customers
.Where(c => c.Id == customerId)
.Include(c => c.Orders)
.FirstOrDefaultAsync();
// Contains
var customerIds = new[] { customerId1, customerId2, customerId3 };
var customers = await context.Customers
.Where(c => customerIds.Contains(c.Id))
.ToListAsync();
// Aggregate functions
var totalRevenue = await context.Orders
.Where(o => o.CustomerId == customerId)
.SumAsync(o => o.TotalPrice.Value); // Access underlying value when needed
Relationships and Foreign Keys
public class Order
{
public OrderId Id { get; init; }
public CustomerId CustomerId { get; init; } // Foreign key
// Navigation property
public Customer Customer { get; set; } = null!;
}
public class Customer
{
public CustomerId Id { get; init; }
// Navigation property
public ICollection<Order> Orders { get; set; } = new List<Order>();
}
// EF Core automatically handles the relationship
var orderWithCustomer = await context.Orders
.Include(o => o.Customer)
.FirstAsync(o => o.Id == orderId);
JSON Serialization
The library provides automatic JSON conversion using System.Text.Json.
Request/Response Format
API Request:
POST /api/customers HTTP/1.1
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com"
}
API Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "John Doe",
"email": "john@example.com"
}
Notice how the CustomerId is automatically serialized as a simple string, not as an object!
Type-Specific Serialization
public class Product
{
public ProductId Id { get; init; } // Guid
public ProductCode Code { get; init; } // string
public ProductNumber Number { get; init; } // int
public Price Price { get; init; } // decimal
public Weight Weight { get; init; } // double
public CreatedAt CreatedAt { get; init; } // DateTime
public ProductStatus Status { get; init; } // Enum
}
// Serializes to:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"code": "PROD-001",
"number": 12345,
"price": 99.95,
"weight": 2.5,
"createdAt": "2025-11-08T10:30:00Z",
"status": "Active"
}
Manual Serialization/Deserialization
using System.Text.Json;
// Serialize
var customer = new Customer { Id = new CustomerId(Guid.CreateVersion7()), Name = "John" };
var json = JsonSerializer.Serialize(customer);
// Deserialize
var deserializedCustomer = JsonSerializer.Deserialize<Customer>(json);
// Works with collections
var customers = new List<Customer> { customer1, customer2 };
var customersJson = JsonSerializer.Serialize(customers);
OpenAPI/Swagger Documentation
The schema filter automatically transforms strongly typed IDs in your API documentation.
Swagger UI Integration
// Program.cs
builder.Services.AddStronglyTypedIds(); // Includes Swagger configuration
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My API",
Version = "v1"
});
// StronglyTypedIdSchemaFilter is already registered
});
app.UseSwagger();
app.UseSwaggerUI();
Generated OpenAPI Schema
Before (without this library):
CustomerId:
type: object
properties:
value:
type: string
format: uuid
After (with this library):
CustomerId:
type: string
format: uuid
Much cleaner and follows OpenAPI best practices!
Scalar API Support
Scalar is a modern alternative to Swagger UI. The library works seamlessly with Scalar.
Option 1: Scalar Only
// Program.cs
builder.Services.AddStronglyTypedIds(configureSwagger: false); // Skip Swagger
// Add Scalar
builder.Services.AddOpenApi(); // .NET 9 built-in OpenAPI
app.MapOpenApi();
var app = builder.Build();
app.MapScalarApiReference(); // Add Scalar UI
app.Run();
Option 2: Both Swagger and Scalar
builder.Services.AddStronglyTypedIds(); // Includes Swagger
app.UseSwagger();
app.UseSwaggerUI();
app.MapScalarApiReference(); // Both work together!
Option 3: JSON Only (No API Docs)
builder.Services.AddStronglyTypedIdsJsonOnly(); // No OpenAPI configuration
Advanced Scenarios
Enum-Based IDs
public enum OrderStatus
{
Pending,
Confirmed,
Shipped,
Delivered,
Cancelled
}
public readonly record struct OrderStatusId(OrderStatus Value)
: StronglyTypedId<OrderStatus>(Value);
public class Order
{
public OrderId Id { get; init; }
public OrderStatusId Status { get; init; }
}
// Usage
var order = new Order
{
Id = new OrderId(Guid.CreateVersion7()),
Status = new OrderStatusId(OrderStatus.Confirmed)
};
// JSON: { "id": "...", "status": "Confirmed" }
// Swagger: type: "string", enum: ["Pending", "Confirmed", "Shipped", "Delivered", "Cancelled"]
Guid Generation Strategies
// Option 1: Guid.CreateVersion7() - Time-ordered (recommended for databases)
// Available in .NET 9.0+
#if NET9_0_OR_GREATER
var customerId = new CustomerId(Guid.CreateVersion7());
#else
// For .NET 8.0, use Guid.NewGuid() - the library's NewId() method handles this automatically
var customerId = new CustomerId(Guid.NewGuid());
#endif
// Pros: Better database index performance (NET 9+), sortable by creation time
// Cons: Slightly predictable sequence
// Option 2: Guid.NewGuid() - Random (traditional, works on all versions)
var customerId = new CustomerId(Guid.NewGuid());
// Pros: Completely random, unpredictable, works on .NET 8.0+
// Cons: Poor database index performance with clustered indexes
// Note: The library's NewId() method automatically uses CreateVersion7() on .NET 9.0
// and falls back to NewGuid() on .NET 8.0
Nullable IDs
public class Order
{
public OrderId Id { get; init; }
public CustomerId CustomerId { get; init; }
public OrderId? ParentOrderId { get; init; } // Nullable for optional relationships
}
// Usage
var order = new Order
{
Id = new OrderId(Guid.CreateVersion7()),
CustomerId = customerId,
ParentOrderId = null // No parent order
};
Value Access
var customerId = new CustomerId(Guid.CreateVersion7());
// Access underlying value when needed
Guid underlyingGuid = customerId.Value;
// Use in non-EF Core scenarios (e.g., external APIs)
var externalApiRequest = new ExternalRequest
{
CustomerId = customerId.Value.ToString()
};
Performance Considerations
Reflection Usage
The library uses reflection only at application startup:
// Startup (one-time cost during app initialization)
builder.Services.AddStronglyTypedIds(); // Registers JSON converter factory
configurationBuilder.ConfigureStronglyTypedIds(); // Scans EF Core model
// Runtime (zero reflection - uses cached converters)
var json = JsonSerializer.Serialize(customer); // ✅ Fast
var customer = await context.Customers.FindAsync(id); // ✅ Fast
var response = await controller.GetCustomer(customerId); // ✅ Fast
Benchmarks
| Method | Mean | Allocated |
|------------------------ |----------:|----------:|
| Serialize_StronglyTyped | 1.234 μs | 1.2 KB |
| Serialize_Primitive | 1.198 μs | 1.2 KB | ← Negligible difference
| EF_Query_StronglyTyped | 45.23 μs | 2.5 KB |
| EF_Query_Primitive | 45.01 μs | 2.5 KB | ← Negligible difference
The performance overhead is negligible - you get type safety without sacrificing speed!
Migration Guide
Migrating from Primitive Types
Step 1: Create Strongly Typed IDs
// Before
public class Customer
{
public Guid Id { get; set; }
}
// After - Add new record struct
public readonly record struct CustomerId(Guid Value) : StronglyTypedId<Guid>(Value);
Step 2: Update Entity Classes
public class Customer
{
public CustomerId Id { get; init; } // Change Guid to CustomerId
public string Name { get; set; } = string.Empty;
}
Step 3: Configure EF Core
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.ConfigureStronglyTypedIds();
}
Step 4: No Database Migration Needed!
The database schema stays the same - IDs are still stored as Guid/int/etc.
-- Schema doesn't change
CREATE TABLE Customers (
Id uniqueidentifier PRIMARY KEY, -- Still a Guid
Name nvarchar(max)
);
Step 5: Update Service Layer
// Before
public Task<Customer> GetCustomerAsync(Guid id)
// After
public Task<Customer> GetCustomerAsync(CustomerId id)
Step 6: Configure API
builder.Services.AddStronglyTypedIds();
Done! Your API now uses strongly typed IDs with full JSON and Swagger support.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
<a name="turkish-documentation"></a>
🇹🇷 Türkçe Dokümantasyon
İçindekiler
- Neden Strongly Typed ID'ler?
- Özellikler
- Kurulum
- Hızlı Başlangıç
- Desteklenen Değer Tipleri
- Detaylı Kullanım
- Gelişmiş Senaryolar
- Performans Değerlendirmesi
- Migrasyon Kılavuzu
Neden Strongly Typed ID'ler?
Guid veya int gibi primitive tipleri doğrudan kullanmak yerine, strongly typed ID'ler şunları sağlar:
✅ Tip Güvenliği: Farklı entity ID'lerinin karıştırılmasını derleme zamanında önler
✅ Kod Netliği: FindCustomer(CustomerId id) daha açıklayıcıdır
✅ Refactoring Güvenliği: ID tiplerini değiştirirken derleyici hataları
✅ DDD Uyumluluğu: Domain-Driven Design value object pattern'ini takip eder
✅ Sıfır Boilerplate: Otomatik EF Core, JSON ve OpenAPI entegrasyonu
Önce (Primitive Obsession)
public class OrderService
{
// ❌ Müşteri ve sipariş ID'lerini karıştırmak kolay
public Order CreateOrder(Guid customerId, Guid productId) { }
// ❌ Parametreleri yanlışlıkla yer değiştirmek derlenir ama çalışma zamanında hata verir
var order = CreateOrder(productId, customerId); // YANLIŞ!
}
Sonra (Strongly Typed ID'ler)
public class OrderService
{
// ✅ Tip-güvenli parametreler
public Order CreateOrder(CustomerId customerId, ProductId productId) { }
// ✅ Derleme hatası hataları önler
var order = CreateOrder(productId, customerId); // Derlenmez!
}
<a name="özellikler-tr"></a>Özellikler
- 🎯 Generic Base Tip:
Guid,int,long,string,decimal,double,DateTime,DateTimeOffsetveEnumdesteği - 🗃️ EF Core Convention: Otomatik value converter kaydı
- 📦 JSON Serileştirme: Sorunsuz System.Text.Json entegrasyonu
- 📝 OpenAPI Desteği: Swagger UI ve Scalar API ile çalışır
- ⚡ Yüksek Performans: Reflection sadece başlangıçta, çalışma zamanında sıfır maliyet
- 🔧 Sıfır Konfigürasyon: Convention-based otomatik keşif
- 🎨 Temiz Kod:
record structile minimal boilerplate
<a name="kurulum-tr"></a>Kurulum
dotnet add package ErginWebDev.StronglyTypedIds
Gereksinimler:
- .NET 8.0 veya üzeri (.NET 8.0 ve .NET 9.0 her ikisi de desteklenir)
- Entity Framework Core 8.0+ (EF Core entegrasyonu kullanılıyorsa)
- Swashbuckle.AspNetCore 6.8+ (Swagger kullanılıyorsa)
<a name="hızlı-başlangıç-tr"></a>Hızlı Başlangıç
1. Strongly Typed ID'lerinizi Tanımlayın
using ErginWebDev.StronglyTypedIds.ValueObjects;
// Guid tabanlı ID'ler
public readonly record struct CustomerId(Guid Value) : StronglyTypedId<Guid>(Value);
public readonly record struct OrderId(Guid Value) : StronglyTypedId<Guid>(Value);
// Diğer değer tipleri
public readonly record struct OrderNumber(int Value) : StronglyTypedId<int>(Value);
public readonly record struct ProductCode(string Value) : StronglyTypedId<string>(Value);
public readonly record struct Price(decimal Value) : StronglyTypedId<decimal>(Value);
2. Domain Entity'lerinizde Kullanın
public class Customer
{
public CustomerId Id { get; init; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
public class Order
{
public OrderId Id { get; init; }
public CustomerId CustomerId { get; init; }
public OrderNumber OrderNumber { get; init; }
public Price TotalPrice { get; init; }
public DateTime CreatedAt { get; init; }
}
3. Servisleri Yapılandırın (Program.cs)
using ErginWebDev.StronglyTypedIds;
var builder = WebApplication.CreateBuilder(args);
// Strongly typed ID desteğini ekle
builder.Services.AddStronglyTypedIds(); // JSON + Swagger
// DbContext'inizi ekleyin
builder.Services.AddDbContext<AppDbContext>();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI();
app.Run();
4. EF Core'u Yapılandırın (DbContext)
public class AppDbContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Otomatik strongly typed ID dönüşümünü etkinleştir
configurationBuilder.ConfigureStronglyTypedIds();
}
}
5. Controller'larda Kullanın
[ApiController]
[Route("api/[controller]")]
public class CustomersController : ControllerBase
{
private readonly AppDbContext _context;
public CustomersController(AppDbContext context) => _context = context;
[HttpGet("{id}")]
public async Task<ActionResult<Customer>> GetCustomer(CustomerId id)
{
// ID otomatik olarak URL'den bind edilir: "550e8400-e29b-41d4-a716-446655440000"
var customer = await _context.Customers.FindAsync(id);
if (customer == null)
return NotFound();
return customer; // Otomatik olarak JSON'a dönüştürülür
}
[HttpPost]
public async Task<ActionResult<Customer>> CreateCustomer(CreateCustomerRequest request)
{
var customer = new Customer
{
Id = new CustomerId(Guid.CreateVersion7()), // Zaman sıralı GUID
Name = request.Name,
Email = request.Email
};
_context.Customers.Add(customer);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetCustomer), new { id = customer.Id }, customer);
}
}
Bu kadar! Strongly typed ID'leriniz artık tüm uygulamanızda sorunsuz çalışıyor.
<a name="desteklenen-değer-tipleri-tr"></a>Desteklenen Değer Tipleri
| Tip | Veritabanı | JSON Format | OpenAPI Tip | Örnek |
|---|---|---|---|---|
Guid |
uniqueidentifier | string | string/uuid |
"550e8400-e29b-41d4-a716-446655440000" |
int |
int | number | integer/int32 |
12345 |
long |
bigint | number | integer/int64 |
9999999999 |
string |
nvarchar | string | string |
"PROD-001" |
decimal |
decimal | number | number/decimal |
99.95 |
double |
float | number | number/double |
123.456 |
DateTime |
datetime2 | string | string/date-time |
"2025-11-08T10:30:00" |
DateTimeOffset |
datetimeoffset | string | string/date-time |
"2025-11-08T10:30:00Z" |
Enum |
int | string | string + enum değerleri |
"Active" |
<a name="detaylı-kullanım-tr"></a>Detaylı Kullanım
Entity Framework Core Entegrasyonu
Kütüphane, EF Core'un convention sistemini kullanarak strongly typed ID'leri otomatik olarak tespit eder ve dönüştürür.
Nasıl Çalışır
public class AppDbContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Bu tek satır TÜM strongly typed ID'ler için otomatik dönüşümü etkinleştirir
configurationBuilder.ConfigureStronglyTypedIds();
// Convention tüm entity property'lerini tarar ve şunları tespit eder:
// - Value type'lar (struct)
// - "Value" property'si olan
// - Desteklenen bir tipte (Guid, int, long, string, vb.)
// Ardından otomatik olarak uygun converter'ı uygular
}
}
Veritabanı Depolama
Strongly typed ID'ler altta yatan değer tipi olarak saklanır:
-- Customer tablosu
CREATE TABLE Customers (
Id uniqueidentifier PRIMARY KEY, -- CustomerId Guid olarak saklanır
Name nvarchar(max),
Email nvarchar(max)
);
-- Order tablosu
CREATE TABLE Orders (
Id uniqueidentifier PRIMARY KEY, -- OrderId Guid olarak saklanır
CustomerId uniqueidentifier NOT NULL, -- CustomerId Guid olarak saklanır
OrderNumber int NOT NULL, -- OrderNumber int olarak saklanır
TotalPrice decimal(18,2) NOT NULL, -- Price decimal olarak saklanır
FOREIGN KEY (CustomerId) REFERENCES Customers(Id)
);
JSON Serileştirme
Kütüphane, System.Text.Json kullanarak otomatik JSON dönüşümü sağlar.
Tip-Spesifik Serileştirme
public class Product
{
public ProductId Id { get; init; } // Guid
public ProductCode Code { get; init; } // string
public ProductNumber Number { get; init; } // int
public Price Price { get; init; } // decimal
public Weight Weight { get; init; } // double
public CreatedAt CreatedAt { get; init; } // DateTime
public ProductStatus Status { get; init; } // Enum
}
// Şuna dönüştürülür:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"code": "PROD-001",
"number": 12345,
"price": 99.95,
"weight": 2.5,
"createdAt": "2025-11-08T10:30:00Z",
"status": "Active"
}
Scalar API Desteği
Scalar, Swagger UI'ya modern bir alternatiftir. Kütüphane Scalar ile sorunsuz çalışır.
// Program.cs - Sadece Scalar
builder.Services.AddStronglyTypedIds(configureSwagger: false);
builder.Services.AddOpenApi(); // .NET 9 yerleşik OpenAPI
app.MapOpenApi();
app.MapScalarApiReference();
<a name="gelişmiş-senaryolar-tr"></a>Gelişmiş Senaryolar
Enum Tabanlı ID'ler
public enum OrderStatus
{
Beklemede,
Onaylandi,
Kargolandi,
TeslimEdildi,
IptalEdildi
}
public readonly record struct OrderStatusId(OrderStatus Value)
: StronglyTypedId<OrderStatus>(Value);
public class Order
{
public OrderId Id { get; init; }
public OrderStatusId Status { get; init; }
}
// Kullanım
var order = new Order
{
Id = new OrderId(Guid.CreateVersion7()),
Status = new OrderStatusId(OrderStatus.Onaylandi)
};
Guid Üretim Stratejileri
// Seçenek 1: Guid.CreateVersion7() - Zaman sıralı (veritabanları için önerilen)
// .NET 9.0+ için kullanılabilir
#if NET9_0_OR_GREATER
var customerId = new CustomerId(Guid.CreateVersion7());
#else
// .NET 8.0 için Guid.NewGuid() kullanın - kütüphanenin NewId() metodu bunu otomatik yapar
var customerId = new CustomerId(Guid.NewGuid());
#endif
// Artılar: Daha iyi veritabanı index performansı (NET 9+), oluşturulma zamanına göre sıralanabilir
// Eksiler: Hafif tahmin edilebilir sıra
// Seçenek 2: Guid.NewGuid() - Rastgele (geleneksel, tüm versiyonlarda çalışır)
var customerId = new CustomerId(Guid.NewGuid());
// Artılar: Tamamen rastgele, tahmin edilemez, .NET 8.0+ ile çalışır
// Eksiler: Clustered index'lerde zayıf veritabanı performansı
// Not: Kütüphanenin NewId() metodu .NET 9.0'da otomatik olarak CreateVersion7() kullanır
// ve .NET 8.0'da NewGuid()'e geri döner
<a name="performans-değerlendirmesi-tr"></a>Performans Değerlendirmesi
Reflection Kullanımı
Kütüphane reflection'ı sadece uygulama başlangıcında kullanır:
// Başlangıç (uygulama başlatılırken tek seferlik maliyet)
builder.Services.AddStronglyTypedIds(); // JSON converter factory kaydı
configurationBuilder.ConfigureStronglyTypedIds(); // EF Core model taraması
// Çalışma zamanı (sıfır reflection - önbelleğe alınmış converter'lar kullanılır)
var json = JsonSerializer.Serialize(customer); // ✅ Hızlı
var customer = await context.Customers.FindAsync(id); // ✅ Hızlı
var response = await controller.GetCustomer(customerId); // ✅ Hızlı
Performans maliyeti ihmal edilebilir - tip güvenliğini hızdan ödün vermeden elde edersiniz!
<a name="migrasyon-kılavuzu-tr"></a>Migrasyon Kılavuzu
Primitive Tiplerden Geçiş
Adım 1: Strongly Typed ID'ler Oluşturun
// Önce
public class Customer
{
public Guid Id { get; set; }
}
// Sonra - Yeni record struct ekleyin
public readonly record struct CustomerId(Guid Value) : StronglyTypedId<Guid>(Value);
Adım 2: Entity Sınıflarını Güncelleyin
public class Customer
{
public CustomerId Id { get; init; } // Guid'den CustomerId'ye değiştirin
public string Name { get; set; } = string.Empty;
}
Adım 3: EF Core'u Yapılandırın
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.ConfigureStronglyTypedIds();
}
Adım 4: Veritabanı Migration'a Gerek Yok!
Veritabanı şeması aynı kalır - ID'ler hala Guid/int/vb. olarak saklanır.
Adım 5: API'yi Yapılandırın
builder.Services.AddStronglyTypedIds();
Tamamlandı! API'niz artık tam JSON ve Swagger desteği ile strongly typed ID'ler kullanıyor.
Made with ❤️ by Ergin TIRAVOGLU
| 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 is compatible. 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
- No dependencies.
-
net9.0
- No dependencies.
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 |
|---|