Genel Sorgu Filtreleri
Genel sorgu filtreleri, meta veri modelindeki Varlık Türlerine uygulanan LINQ sorgu önkoşullarıdır (genellikle içinde OnModelCreating
). Sorgu koşulu genellikle LINQ Where
sorgu işlecine geçirilen bir boole ifadesidir. EF Core, bu tür filtreleri söz konusu Varlık Türlerini içeren linq sorgularına otomatik olarak uygular. EF Core bunları, Include veya navigation özelliğinin kullanımı aracılığıyla dolaylı olarak başvuruda bulunarak Varlık Türlerine de uygular. Bu özellikle ilgili bazı yaygın uygulamalar şunlardır:
- Geçici silme - Varlık Türü bir
IsDeleted
özelliği tanımlar. - Çok kiracılı - Varlık Türü bir
TenantId
özelliği tanımlar.
Örnek
Aşağıdaki örnekte, basit bir bloglama modelinde çok kiracılı ve geçici silme sorgu davranışlarını uygulamak için Genel Sorgu Filtrelerinin nasıl kullanılacağı gösterilmektedir.
Bahşiş
Bu makalenin örneğini GitHub'da görüntüleyebilirsiniz.
Dekont
Çoklu kiracı, burada basit bir örnek olarak kullanılır. EF Core uygulamalarında çok kiracılılık için kapsamlı rehberlik içeren bir makale de vardır.
İlk olarak varlıkları tanımlayın:
public class Blog
{
#pragma warning disable IDE0051, CS0169 // Remove unused private members
private string _tenantId;
#pragma warning restore IDE0051, CS0169 // Remove unused private members
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public bool IsDeleted { get; set; }
public Blog Blog { get; set; }
}
Varlıkta bir _tenantId
alanın bildirimini Blog
not edin. Bu alan, her Blog örneğini belirli bir kiracıyla ilişkilendirmek için kullanılır. Ayrıca varlık türündeki Post
bir IsDeleted
özellik de tanımlanır. Bu özellik, post örneğinin "geçici olarak silinip silinmediğini" izlemek için kullanılır. Diğer bir ifadeyle örnek, temel alınan veriler fiziksel olarak kaldırılmadan silinmiş olarak işaretlenir.
Ardından API'yi kullanarak içindeki OnModelCreating
sorgu filtrelerini HasQueryFilter
yapılandırın.
modelBuilder.Entity<Blog>().HasQueryFilter(b => EF.Property<string>(b, "_tenantId") == _tenantId);
modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);
Çağrılara HasQueryFilter
geçirilen koşul ifadeleri artık bu türler için tüm LINQ sorgularına otomatik olarak uygulanır.
Bahşiş
Geçerli kiracıyı ayarlamak için kullanılan DbContext örnek düzeyi alanının _tenantId
kullanımına dikkat edin. Model düzeyi filtreleri doğru bağlam örneğindeki değeri (sorguyu yürüten örnek) kullanır.
Dekont
Şu anda aynı varlıkta birden çok sorgu filtresi tanımlamak mümkün değildir; yalnızca sonuncusu uygulanır. Ancak, mantıksal AND
işlecini (&&
C# dilinde) kullanarak birden çok koşula sahip tek bir filtre tanımlayabilirsiniz.
Gezintilerin kullanımı
Genel sorgu filtrelerini tanımlamak için gezintileri de kullanabilirsiniz. Sorgu filtresinde gezintilerin kullanılması, sorgu filtrelerinin özyinelemeli olarak uygulanmasına neden olur. EF Core, sorgu filtrelerinde kullanılan gezintileri genişlettiğinde, başvuruda bulunılan varlıklarda tanımlanan sorgu filtrelerini de uygular.
Bu sorgu filtrelerini OnModelCreating
aşağıdaki şekilde yapılandırmak için:
modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog);
modelBuilder.Entity<Blog>().HasQueryFilter(b => b.Posts.Count > 0);
modelBuilder.Entity<Post>().HasQueryFilter(p => p.Title.Contains("fish"));
Ardından, tüm Blog
varlıklar için sorgu:
var filteredBlogs = db.Blogs.ToList();
Bu sorgu, hem hem de Blog
Post
varlıklar için tanımlanan sorgu filtrelerini uygulayan aşağıdaki SQL'i oluşturur:
SELECT [b].[BlogId], [b].[Name], [b].[Url]
FROM [Blogs] AS [b]
WHERE (
SELECT COUNT(*)
FROM [Posts] AS [p]
WHERE ([p].[Title] LIKE N'%fish%') AND ([b].[BlogId] = [p].[BlogId])) > 0
Dekont
ŞU anda EF Core genel sorgu filtresi tanımlarındaki döngüleri algılamaz, bu nedenle bunları tanımlarken dikkatli olmanız gerekir. Yanlış belirtilirse, döngüler sorgu çevirisi sırasında sonsuz döngülere yol açabilir.
Gerekli gezintiyi kullanarak sorgu filtresiyle varlığa erişme
Dikkat
Genel sorgu filtresi tanımlanmış olan varlığa erişmek için gerekli gezintinin kullanılması beklenmeyen sonuçlara neden olabilir.
Gerekli gezinti, ilgili varlığın her zaman mevcut olmasını bekler. Gerekli ilgili varlık sorgu filtresine göre filtrelenirse, üst varlık da sonuç olarak kullanılamaz. Bu nedenle beklenenden daha az öğe elde edebilirsiniz.
Sorunu göstermek için yukarıda belirtilen ve Post
varlıklarını ve aşağıdaki OnModelCreating
yöntemi kullanabilirizBlog
:
modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).IsRequired();
modelBuilder.Entity<Blog>().HasQueryFilter(b => b.Url.Contains("fish"));
Model aşağıdaki verilerle dağıtılabilir:
db.Blogs.Add(
new Blog
{
Url = "http://sample.com/blogs/fish",
Posts = new List<Post>
{
new Post { Title = "Fish care 101" },
new Post { Title = "Caring for tropical fish" },
new Post { Title = "Types of ornamental fish" }
}
});
db.Blogs.Add(
new Blog
{
Url = "http://sample.com/blogs/cats",
Posts = new List<Post>
{
new Post { Title = "Cat care 101" },
new Post { Title = "Caring for tropical cats" },
new Post { Title = "Types of ornamental cats" }
}
});
Sorun iki sorgu yürütülürken gözlemlenebilir:
var allPosts = db.Posts.ToList();
var allPostsWithBlogsIncluded = db.Posts.Include(p => p.Blog).ToList();
Yukarıdaki kurulumla, ilk sorgu 6 Post
sn'nin tümünü döndürür, ancak ikinci sorgu yalnızca 3 döndürür. bu uyuşmazlık, ikinci sorgudaki yöntem ilgili Blog
varlıkları yüklediğinden Include
ortaya çıkar. ile Post
arasında Blog
gezinti gerektiğinden, EF Core sorguyu oluştururken şunu kullanırINNER JOIN
:
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[IsDeleted], [p].[Title], [t].[BlogId], [t].[Name], [t].[Url]
FROM [Posts] AS [p]
INNER JOIN (
SELECT [b].[BlogId], [b].[Name], [b].[Url]
FROM [Blogs] AS [b]
WHERE [b].[Url] LIKE N'%fish%'
) AS [t] ON [p].[BlogId] = [t].[BlogId]
INNER JOIN
İlgilileri Blog
genel sorgu filtresi tarafından kaldırılmış olan tüm Post
filtrelerin kullanılması.
Gerekli yerine isteğe bağlı gezinti kullanılarak ele alınabilir.
Bu şekilde ilk sorgu öncekiyle aynı kalır, ancak ikinci sorgu artık 6 sonuç oluşturur LEFT JOIN
ve döndürür.
modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).IsRequired(false);
modelBuilder.Entity<Blog>().HasQueryFilter(b => b.Url.Contains("fish"));
Alternatif yaklaşım, hem hem de Blog
Post
varlıklarda tutarlı filtreler belirtmektir.
Bu şekilde eşleşen filtreler hem hem Post
de Blog
öğesine uygulanır. Post
beklenmeyen bir duruma dönüşebilecek s'ler kaldırılır ve her iki sorgu da 3 sonuç döndürür.
modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).IsRequired();
modelBuilder.Entity<Blog>().HasQueryFilter(b => b.Url.Contains("fish"));
modelBuilder.Entity<Post>().HasQueryFilter(p => p.Blog.Url.Contains("fish"));
Filtreleri Devre Dışı Bırakma
Filtreler, işleci kullanılarak IgnoreQueryFilters tek tek LINQ sorguları için devre dışı bırakılabilir.
blogs = db.Blogs
.Include(b => b.Posts)
.IgnoreQueryFilters()
.ToList();
Sınırlamalar
Genel sorgu filtreleri aşağıdaki sınırlamalara sahiptir:
- Filtreler yalnızca devralma hiyerarşisinin kök Varlık Türü için tanımlanabilir.