Aracılığıyla paylaş


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 BlogPost 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 Postsn'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 Bloggenel sorgu filtresi tarafından kaldırılmış olan tüm Postfiltrelerin 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 BlogPost varlıklarda tutarlı filtreler belirtmektir. Bu şekilde eşleşen filtreler hem hem Postde Blog öğesine uygulanır. Postbeklenmeyen 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.