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

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


خوب Split queries یک ویژگی است که از Entity Framework Core 5 معرفی شد و به معنای امکان تقسیم یک پرس و جو به چندین کوئری SQL است.

برای درک تفاوت بین default query)single query) و Split query ، یک console application با استفاده از NET Core 6.0 ایجاد و بسته های زیر را نصب کنید:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools

در ادامه در کلاس DbContext، در متد OnConfiguring، گزینه Log into the console "تمام کوئری های تولید شده توسط EF Core" را فعال کنید:

public class EFContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            const string strConnection = &quotData source=(localdb)\\mssqllocaldb; Initial Catalog=DefaultQueryAndSplitQuery;Integrated Security=true;pooling=true;&quot
            optionsBuilder
                .UseSqlServer(strConnection)
                .EnableSensitiveDataLogging()
                .LogTo(Console.WriteLine, LogLevel.Information);
        }
    }

حالا اجازه دهید سناریویی را در نظر بگیریم که در آن دو موجودیت با رابطه یک به چند (1:N) داریم: "Student" و "Course"، که در آن یک دوره می تواند دانشجویان زیادی داشته باشد، و یک دانشجو فقط می تواند یک Course داشته باشد.

   public class Course
    {
        public int CourseId { get; set; }
        public string Title { get; set; }
        public List<Student> Students { get; set; }
    }

و :

 public class Student
    {
        public int StudentId { get; set; }
        public string Name { get; set; }
        public int CourseId { get; set; }
        public Course Course { get; set; }
    }

در ادامه مدل خود را با داده های آزمایشی پر می کنیم :

public class DbInitializer
    {
        private readonly ModelBuilder modelBuilder;
        public DbInitializer(ModelBuilder modelBuilder)
        {
            this.modelBuilder = modelBuilder;
        }
        public void Seed()
        {
            modelBuilder.Entity<Course>().HasData(
                   new Course { CourseId = 25, Title = &quotC#&quot },
                   new Course { CourseId = 50, Title = &quotJava&quot }
             );
            modelBuilder.Entity<Student>().HasData(
                    new Student { StudentId = 10, Name = &quotFarshid azizi&quot, CourseId = 25 },
                    new Student { StudentId = 20, Name = &quotreza ahmadi&quot, CourseId = 25 },
                    new Student { StudentId = 30, Name = &quotelnaz Goli&quot, CourseId = 50 }
          );
        }
    }

و در کلاس EFContext کد زیر را نیز اضافه کنید :

  protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            new DbInitializer(modelBuilder).Seed();
        }

دستورات زیر را اجرا کنید :

add-migration DataSeeding

update-database

بررسی default query)single query) :

در این مثال، ما در حال ایجاد یک پرس و جوی LINQ با استفاده از Include، بدون استفاده از Slipt query، برای بازگرداندن Courseها و Studentها هستیم:

 public class QueryWithoutSplitQuery
    {
        public void SingleQuery()
        {
            using var db = new EFContext();
            var courses = db.Courses
                .Include(p => p.Students)
                .ToList();
            foreach (var course in courses)
            {
                Console.WriteLine($&quotCourse: {course.Title}&quot);
                foreach (var student in course.Students)
                {
                    Console.WriteLine($&quot\tStudent: {student.Name}&quot);
                }
            }
        }
    }

به کلاس Program.cs بروید و یه نمونه از کلاس QueryWithoutSplitQuery ایجاد و در نهایت برنامه را اجرا کنید :

QueryWithoutSplitQuery ins1 = new QueryWithoutSplitQuery();
ins1.SingleQuery();

در این جا، EF Core یک پرس و جو ایجاد می کند که در آن اطلاعات برای دوره و دانش آموزان برگردانده می شود:

 SELECT [c].[CourseId], [c].[Title], [s].[StudentId], [s].[CourseId], [s].[Name]
      FROM [Courses] AS [c]
      LEFT JOIN [Students] AS [s] ON [c].[CourseId] = [s].[CourseId]
      ORDER BY [c].[CourseId]
اگر یک دوره دارای چندین دانش آموز مرتبط باشد، ردیف‌های مربوط به این Studentها اطلاعات Courseرا تکرار می‌کنند. این تکرار منجر به به اصطلاح مشکل "انفجار دکارتی/cartesian explosion" می شود. با بارگیری بیشتر روابط یک به چند، میزان داده های تکراری ممکن است افزایش یابد و بر عملکرد برنامه شما تأثیر منفی بگذارد.

بررسی SplitQuery :

از EF Core 5، ما این امکان را داریم که از متد AsSplitQuery برای تقسیم کوئری استفاده کنیم:

 public class QueryWithSplitQuery
    {
        public  void SplitQuery()
        {
            using var db = new EFContext();
            var courses = db.Courses
                .Include(p => p.Students)
                .AsSplitQuery()
                .ToList();
            foreach (var course in courses)
            {
                Console.WriteLine($&quotCourse: {course.Title}&quot);
                foreach (var student in course.Students)
                {
                    Console.WriteLine($&quot\tStudent: {student.Name}&quot);
                }
            }
        }
    }

به کلاس Program.cs بروید و یه نمونه از کلاس QueryWithSplitQuery ایجاد و در نهایت برنامه را اجرا کنید :

QueryWithSplitQuery ins2 = new QueryWithSplitQuery();
ins2.SplitQuery();

هنگام استفاده از متد AsSplitQuery در این حالت EF Core دو کوئری ایجاد می کند:

اولی( فقط اطلاعات مربوط به دوره ها برگردانده می شود.) :

 SELECT [c].[CourseId], [c].[Title]
      FROM [Courses] AS [c]
      ORDER BY [c].[CourseId]

و دومی(فقط اطلاعات مربوط به دانش آموزان برگردانده می شود) :

 SELECT [s].[StudentId], [s].[CourseId], [s].[Name], [c].[CourseId]
      FROM [Courses] AS [c]
      INNER JOIN [Students] AS [s] ON [c].[CourseId] = [s].[CourseId]
      ORDER BY [c].[CourseId]

همانطور که می بینیم، با SplitQuery نام دوره ها فقط یک بار برگردانده شد. این به این معنی است که حتی اگر پرس‌وجوهای پیچیده‌تری با ستون‌های بیشتر و روابط بیشتر داشته باشیم، داده‌ها تکراری نمی‌شوند، که منجر به پرس و جو بسیار کارآمدتر می‌شود.

هنگام استفاده از Split Query با Skip/Take، توجه ویژه‌ای داشته باشید که query ordering خود را کاملاً منحصر به فرد کنید. عدم انجام این کار می تواند باعث برگرداندن داده های نادرست شود.به عنوان مثال، اگر نتایج فقط بر اساس تاریخ مرتب شوند، اما ممکن است چندین نتیجه با تاریخ یکسان وجود داشته باشد، هر یک از Split Queryها می‌توانند نتایج متفاوتی را از پایگاه داده دریافت کنند. ordering بر اساس تاریخ و شناسه (یا هر ویژگی منحصر به فرد دیگر یا ترکیبی از پراپرتی ها) باعث می شود ordering کاملاً منحصر به فرد باشد و از این مشکل جلوگیری شود.توجه داشته باشید که پایگاه داده های رابطه ای هیچ ترتیبی را به طور پیش فرض اعمال نمی کنند، حتی در کلید اصلی.
موجودیت‌های مرتبط یک به یک همیشه از طریق JOIN در همان query بارگیری می‌شوند، زیرا تأثیری بر عملکرد ندارد.
هر پرس و جو در حال حاضر مستلزم یک رفت و برگشت اضافی به پایگاه داده است و چندین رفت و برگشت شبکه به خودی خود می تواند عملکرد را کاهش دهد.


فعال کردن split queries به صورت globally یا پیش فرض :

همچنین می توانید پرس و جوهای تقسیم شده را به عنوان پیش فرض برای Context برنامه خود پیکربندی کنید:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(
            @&quotServer=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True&quot,
            o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
}

هنگامی که پرس‌وجوهای تقسیم‌شده به‌عنوان پیش‌فرض پیکربندی می‌شوند، همچنان می‌توان پرس‌و‌جوهای خاصی را برای اجرا به‌عنوان single query پیکربندی کرد:

using (var context = new EFContext())
{
var courses = db.Courses
        .Include(p => p.Students)
        .AsSingleQuery()
        .ToList();
}

مشاهده سورس پروژه در GitHub

نتیجه گیری :

در حالی که Split query از مشکلات عملکرد مرتبط با JOIN ها و انفجار دکارتی جلوگیری می کند، همچنین دارای معایبی است:

  • استفاده از single query یکپارچگی داده‌هایی را که بازگردانده می‌شوند تضمین می‌کند، چنین تضمین‌هایی برای Split query وجود ندارد اگر پایگاه داده هنگام اجرای پرس و جوهای شما به طور همزمان به روز شود، داده های حاصل ممکن است منجر به ناسازگاری در نتایج برگشتی شود.
  • هر پرس و جو در حال حاضر مستلزم یک رفت و برگشت شبکه اضافی به پایگاه داده شما است. رفت و برگشت های چندگانه شبکه می تواند عملکرد را کاهش دهد، به خصوص در مواردی که تاخیر در پایگاه داده زیاد است (به عنوان مثال، cloud services).
  • در حالی که برخی از پایگاه‌های داده اجازه مصرف همزمان نتایج چندین پرس‌وجو را می‌دهند (SQL Server با MARS، Sqlite)، اکثر آنها اجازه می‌دهند تنها یک پرس‌وجو در هر نقطه مشخص فعال باشد. بنابراین تمام نتایج پرس‌و‌جوهای قبلی باید قبل از اجرای پرس‌وجوهای بعدی در حافظه برنامه شما بافر شوند، که منجر به افزایش نیاز به حافظه می‌شود.

متأسفانه، یک استراتژی مشخص برای بارگیری موجودیت های مرتبط وجود ندارد که متناسب با همه سناریوها باشد. مزایا و معایب پرس و جوهای Single و Split را با دقت در نظر بگیرید تا موردی را که متناسب با نیاز شماست انتخاب کنید.

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

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

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

https://zarinp.al/farshidazizi