چگونه توسط EF Core، چندین کوئری را یکجا به بانک اطلاعاتی ارسال کنیم؟
روشی را که در این مطلب مشاهده کردید، در موارد مشابه دیگری هم قابل استفادهاست. برای مثال فرض کنید اطلاعات یک مشتری را قرار است به صورت زیر ذخیره کنیم:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public CustomerType Type { get; set; }
}
public enum CustomerType
{
Individual,
Institution,
}
حالت عادی کوئری گرفتن از اطلاعات جدول آن که به همراه صفحه بندی، نمایش تعداد رکوردها و یک کوئری دلخواه دیگر باشد، به صورت زیر است:
void ManyQueriesManyCalls()
{
using var scope = serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<CustomerContext>();
var baseQuery = context.Customers.Select(customer => new
{
customer.Name,
customer.Type,
customer.Id,
});
var total = baseQuery.Count();
var types = baseQuery.GroupBy(x => x.Type)
.Select(x => x.Key).ToList();
var pageSize = 10;
var pageIndex = 0;
var results = baseQuery
.OrderBy(x => x.Id)
.Skip(pageSize * pageIndex)
.Take(pageSize)
.ToList();
Console.WriteLine($"Total:{total}, First Type: {types.First()}, First Item: {results.First().Name}");
}
که سبب میشود سه کوئری و سه بار رفت و برگشت را به بانک اطلاعاتی داشته باشیم:
SELECT COUNT(*)
FROM [Customers] AS [c]
SELECT [c].[Type]
FROM [Customers] AS [c]
GROUP BY [c].[Type]
SELECT [c].[Name], [c].[Type], [c].[Id]
FROM [Customers] AS [c]
ORDER BY [c].[Id]
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY
اگر بخواهیم این سه کوئری را یکبار به سمت بانک اطلاعاتی ارسال کنیم، میتوان از همان ترفند گروه بندی مطرح شدهی در این مثال برای ترکیب کوئریها استفاده کرد:
void ManyQueriesOnCall()
{
using var scope = serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<CustomerContext>();
var baseQuery = context.Customers.Select(customer => new
{
customer.Name,
customer.Type,
customer.Id,
});
var pageSize = 10;
var pageIndex = 0;
var allTogether = baseQuery
.GroupBy(x => 1)
.Select(bq => new
{
Total = baseQuery.Count(),
Types = baseQuery.GroupBy(x => x.Type)
.Select(x => x.Key)
.ToList(),
Results = baseQuery
.OrderBy(x => x.Id)
.Skip(pageSize * pageIndex)
.Take(pageSize)
.ToList(),
})
.FirstOrDefault();
Console.WriteLine($"Total:{allTogether.Total}, First Type: {allTogether.Types.First()}, First Item: {allTogether.Results.First().Name}");
}
که اینبار فقط یک کوئری
outer apply دار را تولید میکند و فقط یکبار، رفت و برگشت به بانک اطلاعاتی را شاهد خواهیم بود:
SELECT [t0].[Key], [t1].[Type], [t2].[Name], [t2].[Type], [t2].[Id]
FROM (
SELECT TOP(1) [t].[Key]
FROM (
SELECT 1 AS [Key]
FROM [Customers] AS [c]
) AS [t]
GROUP BY [t].[Key]
) AS [t0]
OUTER APPLY (
SELECT [c0].[Type]
FROM [Customers] AS [c0]
GROUP BY [c0].[Type]
) AS [t1]
OUTER APPLY (
SELECT [c1].[Name], [c1].[Type], [c1].[Id]
FROM [Customers] AS [c1]
ORDER BY [c1].[Id]
OFFSET @__p_1 ROWS FETCH NEXT @__pageSize_2 ROWS ONLY
) AS [t2]
ORDER BY [t0].[Key], [t1].[Type], [t2].[Id]
کدهای این مثال را از اینجا میتوانید دریافت کنید:
EF7ManyQueriesOneCall.zip