Genel Sorgu Filtreleri

Genel sorgu filtreleri, meta veri modelinde (genellikle içinde) Varlık Türlerine uygulanan LINQ sorgu OnModelCreating hazırlıklarıdır. Sorgu, genellikle LINQ sorgu işlecine geçirilen bir boole Where ifadesidir. EF Core bu tür filtreleri söz konusu Varlık Türleri ile ilgili tüm LINQ sorgularına otomatik olarak uygular. EF Core, dahil etmek veya gezinti özelliğini kullanmak yoluyla dolaylı olarak başvurulan Varlık Türlerine de uygulanır. Bu özelliğin bazı yaygın uygulamaları:

  • Yumuşak silme - Varlık Türü bir özelliği tanımlar.
  • Çok kiralı - Varlık Türü bir özelliği tanımlar.

Örnek

Aşağıdaki örnekte, basit bir basit modelde çok katmanlı ve soft-delete sorgu davranışlarını uygulamak için Genel Sorgu Filtreleri'nin nasıl kullanımına sahip olduğu gösterir.

İpucu

Bu makalenin örneğini daha sonra GitHub.

İ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 alanın _tenantId bildirimini Blog not eder. Bu alan, her Blog örneğini belirli bir kiracıyla ilişkilendirmek için kullanılır. Ayrıca, varlık türü IsDeleted üzerinde bir özellik olarak Post tanımlanır. Bu özellik, bir post örneğinin "yazılımdan silinmiş" olup olmadığını izlemek için kullanılır. Başka bir ifadeyle örnek, temel alınan verileri fiziksel olarak kaldırmadan silinmiş olarak işaretlenir.

Ardından API'yi kullanarak içinde OnModelCreating sorgu filtrelerini HasQueryFilter yapılandırabilirsiniz.

modelBuilder.Entity<Blog>().HasQueryFilter(b => EF.Property<string>(b, "_tenantId") == _tenantId);
modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);

Çağrılara geçirilen önkate ifadeler artık bu türler için tüm HasQueryFilter LINQ sorgularına otomatik olarak uygulanır.

İpucu

Geçerli kiracıyı ayarlamak için kullanılan DbContext _tenantId örnek düzeyi alanı kullanımına dikkatin. Model düzeyinde filtreler, doğru bağlam örneğinden (sorguyu yürüten örnek) değeri kullanır.

Not

Şu anda aynı varlığa birden çok sorgu filtresi tanımlamak mümkün değildir; yalnızca son filtre uygulanır. Ancak, mantıksal işleci (C# içinde) kullanarak birden çok koşulları AND olan tek bir filtreAND

Gezintilerin kullanımı

Gezintileri genel sorgu filtrelerini tanımlamada da kullanabilirsiniz. Sorgu filtresinde gezintilerin kullanımı, sorgu filtrelerinin tekrar tekrar uygulanmasına neden olur. Sorgu EF Core gezintileri genişletilirse, başvurulan varlıklarda tanımlanan sorgu filtreleri de uygulanır.

Bunu göstermek için içinde sorgu OnModelCreating filtrelerini aşağıdaki şekilde yapılandırabilirsiniz:

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 varlıkları Blog sorgula:

var filteredBlogs = db.Blogs.ToList();

Bu sorgu, hem SQL için tanımlanan sorgu filtrelerini uygulanan aşağıdaki Blog sorguyu Post 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

Not

Şu EF Core genel sorgu filtresi tanımlarında döngüleri algılamaz, bu nedenle bunları tanımlarken dikkatli olmalısınız. Yanlış belirtilirse, döngüler sorgu çevirisi sırasında sonsuz döngülere neden olabilir.

Gerekli gezintiyi kullanarak sorgu filtresiyle varlığa erişme

Dikkat

Genel sorgu filtresi tanımlanmış olan varlığa erişmek için gerekli gezintiyi kullanmak beklenmeyen sonuçlara neden olabilir.

Gerekli gezinti, ilgili varlığın her zaman mevcut olmasını bekler. Gerekirse ilgili varlık sorgu filtresine göre filtrelenmişse üst varlık da sonuç olarak yer alamayacaktır. Bu nedenle sonuçta beklenenden daha az öğe elde olabilir.

Sorunu göstermek için yukarıda belirtilen ve BlogPost varlıklarını ve aşağıdaki yöntemi OnModelCreating kullanabiliriz:

modelBuilder.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).IsRequired();
modelBuilder.Entity<Blog>().HasQueryFilter(b => b.Url.Contains("fish"));

Modelin çekirdeği aşağıdaki verilerle birlikte olabilir:

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" }
        }
    });

İki sorgu yürütücürken sorun gözlemlenebilir:

var allPosts = db.Posts.ToList();
var allPostsWithBlogsIncluded = db.Posts.Include(p => p.Blog).ToList();

Yukarıdaki kurulumda ilk sorgu 6 sn'nin hepsini Post döndürür, ancak ikinci sorgu yalnızca 3 döndürür. Bu eşleşmezlik, ikinci Include sorguda yöntemin ilgili varlıkları yüklemesi nedeniyle Blog gerçekleşir. ve arasında gezinme Blog gerekli olduğu için EF Core oluşturmak için PostINNER JOIN aşağıdakini kullanır:

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]

Filtrelerin INNER JOIN kullanımı, Post ilgilileri genel Blog sorgu filtresi tarafından kaldırılmış olan tüm filtrelerin kullanımıdır.

Gerekli yerine isteğe bağlı gezinti kullanılarak bu sorun giderildi. Bu şekilde ilk sorgu öncekiyle aynı kalır, ancak ikinci sorgu şimdi LEFT JOIN 6 sonuç oluşturur ve geri döner.

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 varlıklarda tutarlı filtreler BlogPost belirtmektir. Bu şekilde eşleşen filtreler hem hem de 'ye BlogPost uygulanır. Postbeklenmeyen durumla karşılaşılabilir s kaldırılıyor ve her iki sorgu da 3 sonuç iade ediyor.

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, IgnoreQueryFilters işleci kullanılarak 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 bir devralma hiyerarşisinin kök Varlık Türü için tanımlanabilir.