UZMAN
Primitive Collections — EF Core 8+
List<string>, int[], List<DayOfWeek> gibi basit tip koleksiyonları artık ayrı tablo gerektirmeden JSON sütununa otomatik saklanır. Tags, roller, izin listeleri gibi basit diziler için ayrı ilişki tablosu açmak yerine bu özelliği kullan.
Veritabanı sağlayıcısı
Bu sayfadaki eşleşen örnekleri seçilen sağlayıcıya göre gösterir.
Kullanım
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string[] Tags { get; set; } // JSON array olarak saklanır
public List<int> LikedByUserIds { get; set; }
}
// Yapılandırma (genellikle convention yeterli)
builder.PrimitiveCollection(p => p.Tags)
.HasMaxLength(1000);
// Sorgulama (LINQ → SQL çevrimi)
var tagged = context.Posts
.Where(p => p.Tags.Contains("efcore"))
.ToList();
Tabloda nasıl görünür:
| Id | Title | Tags | LikedByUserIds |
|---|---|---|---|
| 1 | EF Core 8 Yenilikleri | ["efcore","dotnet","csharp"] |
[1,5,12,28] |
| 2 | React ile SPA | ["react","frontend"] |
[3,7] |
Contains sorgusu SQL'e nasıl çevrilir:
-- .Where(p => p.Tags.Contains("efcore"))
SELECT [p].[Id], [p].[Title], [p].[Tags], [p].[LikedByUserIds]
FROM [Posts] AS [p]
WHERE N'efcore' IN (
SELECT [t].[value] FROM OPENJSON([p].[Tags]) WITH ([value] NVARCHAR(MAX) '$') AS [t]
);
-- .Where(p => p.Tags.Contains("efcore"))
SELECT p.id, p.title, p.tags, p.liked_by_user_ids
FROM posts AS p
WHERE p.tags @> '["efcore"]'; -- jsonb contains operatörü (GIN index destekli)
-- Veya array tipinde saklanıyorsa:
-- WHERE 'efcore' = ANY(p.tags); -- native array operatörü
Desteklenen LINQ Operasyonları
// Contains — dizide eleman var mı
context.Posts.Where(p => p.Tags.Contains("efcore"));
// Count — dizi uzunluğu
context.Posts.Where(p => p.LikedByUserIds.Count > 5);
// Any — koşullu kontrol
context.Posts.Where(p => p.Tags.Any(t => t.StartsWith("dot")));
// Indexer — belirli pozisyon (EF Core 8+)
context.Posts.OrderBy(p => p.Tags[0]); // İlk tag'e göre sırala
EF Core 8 Öncesi — Manual JSON Conversion
// EF Core 7 ve öncesinde bu tip koleksiyonlar için manual converter gerekiyordu:
builder.Property(p => p.Tags)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
v => JsonSerializer.Deserialize<string[]>(v, (JsonSerializerOptions?)null)!)
.HasColumnType("nvarchar(max)");
// EF Core 8+ → bunlara gerek yok, PrimitiveCollection otomatik halleder
Primitive Collection vs JSON Column vs Ayrı Tablo
| Yöntem | Ne Zaman | LINQ Desteği | Performans |
|---|---|---|---|
PrimitiveCollection (EF8+) |
Basit liste (string, int) | Contains, Count, Any | OPENJSON overhead |
JSON Column (ToJson()) |
Kompleks nested nesne | Full LINQ | Parse overhead |
| Ayrı tablo (1-N ilişki) | Büyük veri, index gerekli | Full | En hızlı (index) |
PostgreSQL Native Array Desteği
PostgreSQL Büyük Avantaj — Native Array Desteği:
- SQL Server'da
PrimitiveCollection→NVARCHAR(MAX)+OPENJSONile sorgulanır (yavaş)- PostgreSQL'de native array tipi vardır:
text[],int[],uuid[]— JSON'a gerek yok!- Native array üzerine GIN index konulabilir →
@>(contains) sorguları index kullanır- EF Core Npgsql,
List<string>/string[]→ otomatiktext[]olarak map eder
// PostgreSQL — Native Array (JSON değil!)
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string[] Tags { get; set; } // PG: text[] (native array)
public List<int> LikedByUserIds { get; set; } // PG: integer[]
}
// Configuration (Npgsql otomatik yapar ama açık belirtilebilir):
builder.Property(p => p.Tags)
.HasColumnType("text[]");
builder.Property(p => p.LikedByUserIds)
.HasColumnType("integer[]");
// GIN index ile hızlı array arama:
builder.HasIndex(p => p.Tags).HasMethod("gin");
-- PostgreSQL'de oluşan tablo:
CREATE TABLE posts (
id INT GENERATED ALWAYS AS IDENTITY,
title TEXT NOT NULL,
tags TEXT[] NULL, -- Native array! JSON değil!
liked_by_user_ids INTEGER[] NULL,
CONSTRAINT pk_posts PRIMARY KEY (id)
);
-- GIN index:
CREATE INDEX ix_posts_tags ON posts USING gin (tags);
-- EF sorgusu: .Where(p => p.Tags.Contains("efcore"))
-- PostgreSQL çıktısı (OPENJSON yerine native operatör!):
SELECT p.id, p.title, p.tags, p.liked_by_user_ids
FROM posts AS p
WHERE p.tags @> ARRAY['efcore']::text[]; -- @> = contains, GIN index kullanır!
-- Diğer array operatörleri:
WHERE tags && ARRAY['efcore','dotnet'] -- overlap (herhangi biri var mı)
WHERE 'efcore' = ANY(tags) -- ANY ile eleman kontrolü
WHERE array_length(liked_by_user_ids, 1) > 5 -- Count karşılığı
Performans farkı: SQL Server'da
OPENJSONher sorgu için JSON parse eder. PostgreSQL'de native array operatörleri GIN index ile O(1) erişim sağlar — büyük veri setlerinde dramatik fark yaratır.