دوره آموزشی Entity FrameWork Core - قسمت 30

دوره آموزشی Entity FrameWork Core - قسمت 29

فیلترهای Global query :

فقط یک گزاره پرس و جو(query predicate) است که برای پرس و جوهای موجودیت هایی که این ویژگی برای آنها فعال شده است، به انتهای عبارت Where اضافه می شود.

فیلترهای Global query، معمولاً در OnModelCreating برای entity typeها اعمال می شوند. EF Core چنین فیلترهایی را به طور خودکار برای هر LINQ queries مربوط به آنentity type اعمال می کند.

  • ـSoft delete : یک Entity Type یک پراپرتی IsDeleted را تعریف می کند.
  • ـMulti-tenancy : یک Entity Type یک پراپرتی TenantId را تعریف می کند.


خوب ابتدا و قبل از آنکه وارد بحث اصلی خود شویم بیایید مفتهیم بالا را باهم مرور کنیم :

  • مورد اول Soft delete : عملیاتی که در آن از یک flag معمولا با عنوان IsDeleted برای علامت گذاری داده ها به عنوان "پاک شده" استفاده می شود، بدون اینکه خود داده ها از پایگاه داده پاک شوند. ساده است و حتما با آن آشنا هستید.
  • و اما مورد دوم Multi-tenancy: فرض کنید می خواهیم یک نرم افزار بزرگ مانند سیستم blog دهی طراحی کنیم در این چنین سیستم های معمولا دو روش برای طراحی و پیاده سازی پایگاه داده وجود دارد:
    اول : همه Blogها در یک دیتابیس بزرگ تجمیع شده و رکوردها توسط یک BlogId متمایز می شوند.
    دوم : هر Blog دیتابیس خود را دارد.
    اگر در این سیستم زیر URL های مختلف به ازای هر Blog ارائه شود، استفاده از یک دیتابیس بزرگ چندان معقول نیست( روبرو بودن با انبوهی از رکوردهای متعلق به دیگران) پس بیایید به سراغ روش دوم برویم که همان Multi Tenancy یا در اینجا به عبارتی دیگر DataBase Level Multi Tenancy نامیده می شود.هر Blog با استفاده از URL خودش با App ارتباط برقرار کرده اما از دیتابیس مربوط به خود استفاده می کند.


مثال زیر نحوه استفاده از فیلترهای Global query را برای پیاده سازی رفتارهای پرس و جوی Soft delete و Multi-tenancy در یک blogging model ساده نشان می دهد.

ابتدا موجودیت ها را تعریف می کنیم:

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

به اعلام فیلد tenantId_ در موجودیت Blog توجه کنید. این فیلد برای مرتبط کردن هر نمونه Blog با یک tenant خاص استفاده خواهد شد.همچنین یک پراپرتی IsDeleted در موجودیت Post تعریف شده است. این ویژگی برای پیگیری اینکه آیا یک نمونه پست "soft delete" شده است یا خیر استفاده می شود. به این معنی که نمونه بدون حذف فیزیکی داده‌ها، حذف شده علامت‌گذاری می‌شود.

سپس، فیلترهای پرس و جو را در OnModelCreating با استفاده از HasQueryFilter API پیکربندی کنید.

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

از این به بعد فیلترها به طور خودکار برای هر درخواست LINQ برای آن entity type اعمال می شود.

در حال حاضر نمی توان چندین فیلتر پرس و جو را روی یک موجودیت تعریف کرد - فقط آخرین فیلتر اعمال خواهد شد. با این حال، می توانید با استفاده از عملگر AND منطقی (&& در سی شارپ) یک فیلتر واحد با شرایط چندگانه تعریف کنید.


استفاده از navigations :

می توانید از navigation در تعریف فیلترهای global query استفاده کنید. استفاده از navigation در فیلتر پرس و جو باعث می شود که فیلترهای پرس و جو به صورت بازگشتی اعمال شوند. هنگامی که EF Core navigation های مورد استفاده در فیلترهای پرس و جو را گسترش می‌دهد، فیلترهای پرس و جوی تعریف شده بر روی موجودیت‌های ارجاع‌شده را نیز اعمال می‌کند.

توجه داشته باشید استفاده از required navigation برای دسترسی به موجودیتیکه دارای فیلترهای global query تعریف شده است ممکن است منجر به نتایج غیرمنتظره(unexpected results) شود.

این پیکربندی در OnModelCreating به صورت زیر خواهد بود:

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(&quotfish&quot));

در مرحله بعد:

var filteredBlogs = db.Blogs.ToList();

این پرس و جو SQL زیر را تولید می کند که فیلترهای پرس و جو تعریف شده برای هر دو موجودیت بلاگ و پست را اعمال می کند:

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
در حال حاضر EF Core چرخه‌ها را در تعاریف فیلتر global query تشخیص نمی‌دهد، بنابراین هنگام تعریف آنها باید مراقب باشید. چرخه ها می توانند به حلقه های بی نهایت در طول ترجمه پرس و جو منجر شوند.


غیر فعال کردن فیلترهای Global query :

فیلترها ممکن است با استفاده از عملگر IgnoreQueryFilters برای queryهای جداگانه LINQ غیرفعال شوند.

blogs = db.Blogs
    .Include(b => b.Posts)
    .IgnoreQueryFilters()
    .ToList();

محدودیت ها :

فیلترهای global query را فقط می توان برای root Entity Type یک inheritance hierarchy تعریف کرد.

بیشتر بخوانید : دوره آموزشی Entity FrameWork Core - قسمت 31

بیشتر بخوانید : دوره آموزشی Entity FrameWork Core

بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Core

https://zarinp.al/farshidazizi