بهینه سازی کوئری‌های LINQ در C#.NET برای MS SQL Server
نوشته شده توسط : عباسی


LINQ تحت عنوان یک گویش کارکشته برای رئیس داده ها به .NET اضافه شد. طراحی اپلیکیشن برای مثال LINQ to SQL با استعمال ازEntity Framework به شما این قابلیت و امکان را میدهد با DBMS به سهولت کلام فرمایید. البته اکثر زمان ها هنگام استعمال از آن، توسعه و گسترش‌دهندگان فراموش می‌نمایند که پژوهش نمایند چه نوع query SQl ای به وسیله provider ای که امکان کوئری زدن دارااست، ساخت میگردد (در نمونه ما Entity Framework). درین نوشته ی علمی ما رسیدگی خوا هیم کرد که به چه شکل خواهیم توانست کارایی کوئری‌های LINQ را باصرفه کنیم.


پیاده سازی

بیایید با به کارگیری از یک نمونه دونکته اساسی را تحقیق کنیم.

آغاز بایستی مقر داده Test را در SQL Server تولید کنیم. در‌این دیتابیس با اجرای کوئری تحت دو جدول ساخت و ساز خوا‌هیم شد.

USE [TEST]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Ref](
[ID] [int] NOT NULL,
[ID2] [int] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[InsertUTCDate] [datetime] NOT NULL,
CONSTRAINT [PK_Ref] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Ref] ADD CONSTRAINT [DF_Ref_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO

USE [TEST]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Customer](
[ID] [int] NOT NULL,
[Name] [nvarchar](255) NOT NULL,
[Ref_ID] [int] NOT NULL,
[InsertUTCDate] [datetime] NOT NULL,
[Ref_ID2] [int] NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Customer] ADD CONSTRAINT [DF_Customer_Ref_ID] DEFAULT ((0)) FOR [Ref_ID]
GO

ALTER TABLE [dbo].[Customer] ADD CONSTRAINT [DF_Customer_InsertUTCDate] DEFAULT (getutcdate()) FOR [InsertUTCDate]
GO
هم اکنون جدول Ref را به یاری script ذیل پرمی کنیم. طاقت فرمایید... ما تنها یک script را ایفا کردیم ولی آن را ذخیره نکردیم. در اینگونه مواقعی، SQL Complete به یاری Devart بسیار ریلکس خواهد بود، که با SSMS و Visual Studio ترکیب میشود و دارنده خصوصیت Execution History میباشد.

 

این تلاش تاریخچه انجام گردیده کوئری ها در SSMS را اکران می‌دهد.

 

دقت فرمایید پنجره از موادسازنده پایین ساخته شده میباشد :

1. جعبه کاوش برای غربال کردن حاصل

2. جعبه دامنه تاریخ برای غربال کردن حاصل

3. نتیجه ها ارائه گردیده در یک جدول. می‌توانید اطلاعات را مطابق ردیف های این جدول تر و تمیز نمائید (با به کارگیری از کلید SHIFT می‌توانید دسته ای از ردیف ها را برای نظم دهی گزینش فرمائید)

4. کد ستون تعیین گردیده

جدول سود دربردارنده تاریخچه اسکریپت های ایفا گردیده در SSMS میباشد و جداول ذیل را مشتمل بر می‌گردد:

Status .1: علامت میدهد که اسکریپت با توفیق ایفا گردیده یا این که نه

Query Text .2: کد اسکریپت

Size (Bytes) .3: اندازه متن در واحد بایت

Executed On .4: تاریخ و ساعت اسکریپت ایفا گردیده

Duration .5: زمان فرصت اجرای اسکریپت

File .6: اسم یک فولدر یا این که زبانه در SSMS و هم پا با اسم مثال SQL Server که اسکریپت روی آن اعمال گردیده است

Server .7: اسم مثال SQL Server که اسکریپت روی آن جاری ساختن گردیده است

User .8: ورود به سیستم ذیل اسکریپت ایفا گردیده

Database .9: بستر مقر داده ای که اسکریپت در آن انجام گردیده است

ما میتوانیم کوئری ما یحتاج را در‌این جدول پیدا کنیم،

USE [TEST]
GO

DECLARE @ind INT=1;

WHILE(@ind<1200000)
BEGIN
INSERT INTO [dbo].[Ref]
([ID]
,[ID2]
,[Name])
SELECT
@ind
,@ind
,CAST(@ind AS NVARCHAR(255));

SET @ind=@ind+1;
END
GO
به روشی شبیه، میتوانیم جدول مشتری را با استعمال از اسکریپت پایین لبریز کنیم

USE [TEST]
GO

DECLARE @ind INT=1;
DECLARE @ind_ref INT=1;

WHILE(@ind<=12000000)
BEGIN
IF(@ind%3=0) SET @ind_ref=1;
ELSE IF (@ind%5=0) SET @ind_ref=2;
ELSE IF (@ind%7=0) SET @ind_ref=3;
ELSE IF (@ind%11=0) SET @ind_ref=4;
ELSE IF (@ind%13=0) SET @ind_ref=5;
ELSE IF (@ind%17=0) SET @ind_ref=6;
ELSE IF (@ind%19=0) SET @ind_ref=7;
ELSE IF (@ind%23=0) SET @ind_ref=8;
ELSE IF (@ind%29=0) SET @ind_ref=9;
ELSE IF (@ind%31=0) SET @ind_ref=10;
ELSE IF (@ind%37=0) SET @ind_ref=11;
ELSE SET @ind_ref=@ind%1190000;
INSERT INTO [dbo].[Customer]
([ID]
,[Name]
,[Ref_ID]
,[Ref_ID2])
SELECT
@ind,
CAST(@ind AS NVARCHAR(255)),
@ind_ref,
@ind_ref;

SET @ind=@ind+1;
END
GO
ابزار SQL Complete در مراقبت پوسته منظم کد اسکریپت ها به شما میتواند یاری دهد.

براین اساس ، ما دو جدول ساخت کردیم - یک کدام از آنان بیشتراز 1 میلیون ستون داراست و دیگری بیشتراز 10 میلیون ستون داراست.

اینک بایستی یک پروژه آزمایشی در Visual C# Console App ساخت و ساز کنیم:

در گام بعدی ، می بایست یک کتابخانه به Entity Framework اضافه کنیم تا بتوانیم با مقر داده در خصوص باشیم.

برای اضافه کردن این کتابخانه، روی پروژه کلیک راست نمائید و‘Manage NuGet Packages …’ را در منو تعیین نمائید،

 

در پنجره گشوده گردیده، \"Entity Framework\" را در فریم کاوش وارد فرمائید، بسته Entity Framework را گزینش نمائید و آن را نصب نمایید:

 

در گام بعد از آن، در فولدر App.config، کدهای تحت را آن‌گاه ازعنصر configSections اضافه می‌کنیم:

 


مطمئن گردید حرفه اتصال در connection string وارداتی باشد.

درحال حاضر سه Interface در فولدر های غیروابسته تولید می‌کنیم:

IBaseEntityID

namespace TestLINQ
{
public interface IBaseEntityID
{
int ID { get; set; }
}
}
IBaseEntityName

namespace TestLINQ
{
public interface IBaseEntityName
{
string Name { get; set; }
}
}
IBaseNameInsertUTCDate

namespace TestLINQ
{
public interface IBaseNameInsertUTCDate
{
DateTime InsertUTCDate { get; set; }
}
}
در یک پوشه جدا، یک کلاس BaseEntity برای دو موجودیت خویش ساخت و ساز می‌کنیم که دربرگیرنده فیلدهای مشترک آنان باشد.

namespace TestLINQ
{
public class BaseEntity : IBaseEntityID, IBaseEntityName, IBaseNameInsertUTCDate
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime InsertUTCDate { get; set; }
}
}
در گام آن گاه دو موجودیت (Entitie) می‌سازیم هر مورد در پوشه جدا:

Ref

using System.ComponentModel.DataAnnotations.Schema;

namespace TestLINQ
{
[Table(\"Ref\")]
public class Ref : BaseEntity
{
public int ID2 { get; set; }
}
}
Customer

using System.ComponentModel.DataAnnotations.Schema;

namespace TestLINQ
{
[Table(\"Customer\")]
public class Customer: BaseEntity
{
public int Ref_ID { get; set; }
public int Ref_ID2 { get; set; }
}
}
در پایان یک UserContext در یک فولدر مستقل ساخت و ساز می‌کنیم،

using System.Data.Entity;

namespace TestLINQ
{
public class UserContext : DbContext
{
public UserContext()
: base(\"DbConnection\")
{
Database.SetInitializer(null);
}

public DbSet Customer { get; set; }
public DbSet Ref { get; set; }
}
}
بنابراین، ما یک راه‌حل برای اجرا آزمایش های سئو به یاری LINQ to SQL از روش Entity Framework برای MS SQL Server اخذ میکنیم:

 

اینک، کد ذیل را در Program.cs وارد می‌کنیم:

using System;
using System.Collections.Generic;
using System.Linq;

namespace TestLINQ
{
class Program
{
static void Main(string[] args)
{
using (UserContext db = new UserContext())
{
var dblog = new List();
db.Database.Log = dblog.Add;

var query = from e1 in db.Customer
from e2 in db.Ref
where (e1.Ref_ID == e2.ID)
&& (e1.Ref_ID2 == e2.ID2)
select new { Data1 = e1.Name, Data2 = e2.Name };

var result = query.Take(1000).ToList();

Console.WriteLine(dblog[1]);

Console.ReadKey();
}
}
}
}
زمانی پروژه را انجام کنیم، این خروجی میباشد که تحت عنوان سود در کنسول خوا هیم روءیت کرد،

SELECT TOP (1000)
[Extent1].[Ref_ID] AS [Ref_ID],
[Extent1].[Name] AS [Name],
[Extent2].[Name] AS [Name1]
FROM [dbo].[Customer] AS [Extent1]
INNER JOIN [dbo].[Ref] AS [Extent2] ON ([Extent1].[Ref_ID] = [Extent2].[ID]) AND ([Extent1].[Ref_ID2] = [Extent2].[ID2])
همان گونه که مشاهده می‌کنید، یک LINQ query به صورت کارآمد SQL query را در

MS SQL Server DBMS تولید نموده است.

حال، شرط AND را به OR در LINQ query تغییر تحول می‌دهیم

var query = from e1 in db.Customer
from e2 in db.Ref
where (e1.Ref_ID == e2.ID)
|| (e1.Ref_ID2 == e2.ID2)
select new { Data1 = e1.Name, Data2 = e2.Name };
مجدد اپلیکیشن را اعمال می‌کنیم

یک اشتباه فیس می دهد، از تفصیل غلط، متوجه می شویم که عملیات بعد از 30 ثانیه به نقطه نهایی رسیده میباشد،

 

این کوئریی میباشد که LINQ ساخت‌و‌ساز نموده است:

 

می‌بینیم که تعیین از یک حاصلضرب دکارتی از دو دسته (جدول) ایفا میگردد

SELECT TOP (1000)
[Extent1].[Ref_ID] AS [Ref_ID],
[Extent1].[Name] AS [Name],
[Extent2].[Name] AS [Name1]
FROM [dbo].[Customer] AS [Extent1]
CROSS JOIN [dbo].[Ref] AS [Extent2]
WHERE [Extent1].[Ref_ID] = [Extent2].[ID] OR [Extent1].[Ref_ID2] = [Extent2].[ID2]
LINQ query را مجدد به‌این شکل می نویسیم

var query = (from e1 in db.Customer
join e2 in db.Ref
on e1.Ref_ID equals e2.ID
select new { Data1 = e1.Name, Data2 = e2.Name }).Union(from e1 in db.Customer
join e2 in db.Ref
on e1.Ref_ID2 equals e2.ID2
se
این SQL query می باشد که تحت عنوان فیض اخذ میکنیم

SELECT
[Limit1].[C1] AS [C1],
[Limit1].[C2] AS [C2],
[Limit1].[C3] AS [C3]
FROM ( SELECT DISTINCT TOP (1000)
[UnionAll1].[C1] AS [C1],
[UnionAll1].[Name] AS [C2],
[UnionAll1].[Name1] AS [C3]
FROM (SELECT
1 AS [C1],
[Extent1].[Name] AS [Name],
[Extent2].[Name] AS [Name1]
FROM [dbo].[Customer] AS [Extent1]
INNER JOIN [dbo].[Ref] AS [Extent2] ON [Extent1].[Ref_ID] = [Extent2].[ID]
UNION ALL
SELECT
1 AS [C1],
[Extent3].[Name] AS [Name],
[Extent4].[Name] AS [Name1]
FROM [dbo].[Customer] AS [Extent3]
INNER JOIN [dbo].[Ref] AS [Extent4] ON [Extent3].[Ref_ID2] = [Extent4].[ID2]) AS [UnionAll1]
) AS [Limit1]
متأسفانه ، تنها یک شرط اتصال در یک LINQ query وجود داراست، بدین ترتیب می‌توانیم با ساخت یک کوئری برای هر دو شرط و آن گاه مخلوط آنان با به کارگیری از Union برای حذف خطوط تکراری ، به نتیجه ها دلخواه برسیم.

آری، با دقت به اینکه مجموع ستون های تکراری را می‌توانید برگردانید کوئری ها در اکثر مفاد نابرابر خواهند بود. با این درحال حاضر ، در معاش حقیقی به ستون های تکراری نیازی وجود ندارد و معمولاً مواقعی می باشند که میخواهید از آنان خلاص گردید.

اکنون بیایید نرم افزار های اجرایی این دو کوئری را با هم مقایسه کنیم:

معدل مجال ایفا برای ,CROSS JOIN 195 ثانیه میباشد

 

معدل فرصت انجام برای INNER JOIN-UNION کمتر از 24 ثانیه میباشد

 

همانگونه که از حاصل مشاهده می‌کنیم، LINQ query با صرفه گردیده یک سری موازی سریعتر از یک گزینه با صرفه نشده در‌این دو جدول با میلیون ها رکورد، فعالیت می نماید.

برای نسخه با شرط AND، یک LINQ query این چنین خواهد بود،

var query = from e1 in db.Customer
from e2 in db.Ref
where (e1.Ref_ID == e2.ID)
&& (e1.Ref_ID2 == e2.ID2)
select new { Data1 = e1.Name, Data2 = e2.Name };
به طور تقریبً در‌این حالت ، یک SQL query درست ساخت‌و‌ساز می‌گردد ، با فرصت اجرای به طور تقریبً 24 ثانیه:

 

همینطور، برای عملیات LINQ to Objects، به مکان کوئری که این چنین میباشد:

var query = from e1 in seq1
from e2 in seq2
where (e1.Key1==e2.Key1)
&& (e1.Key2==e2.Key2)
select new { Data1 = e1.Data, Data2 = e2.Data };
میتوانیم از کوئری شبیه ذیل استعمال کنیم:

var query = from e1 in seq1
join e2 in seq2
on new { e1.Key1, e1.Key2 } equals new { e2.Key1, e2.Key2 }
select new { Data1 = e1.Data, Data2 = e2.Data };
جایی که

Para[] seq1 = new[] { new Para { Key1 = 1, Key2 = 2, Data = \"777\" }, new Para { Key1 = 2, Key2 = 3, Data = \"888\" }, new Para { Key1 = 3, Key2 = 4, Data = \"999\" } };
Para[] seq2 = new[] { new Para { Key1 = 1, Key2 = 2, Data = \"777\" }, new Para { Key1 = 2, Key2 = 3, Data = \"888\" }, new Para { Key1 = 3, Key2 = 5, Data = \"999\" } };
نوع Para به طرز ذیل تعریف و تمجید میشود:

class Para
{
public int Key1, Key2;
public string Data;
}
سود گیری

ما بعضی از شرایط های سئو LINQ query را برای MS SQL Server پژوهش کردیم. همینطور، SQL Complete در جستجوی تاریخچه کوئری و همینطور پوسته بندی اسکریپت هایی که درین نوشته‌ی‌علمی به کار گیری کردیم، به ما یاری متعددی کرد.

متأسفانه، حتی توسعه و گسترش دهندگان مجرب .NET نیز اکثر وقت ها فراموش می نمایند که ادراک راهبرد های مستعمل در دور‌نما ضروری میباشد. وگرنه، آن ها میتوانند تنظیمات گردیده و در آجل بمب ساعتی مجازی تهیه و تنظیم نمایند، چه وقتی که چاره مقیاس بندی گردیده باشد و چه وقتی که دست اندرکاران فرنگی پاره ای تغییر تحول نماید.

همینطور ، فایل Plans از این ریپازیتوری دربرگیرنده نرم‌افزار های اجرایی کوئری ها با شرط OR میباشد.

خلال این ، یک چاره بهتر به اسم dotConnect وجود داراست، که یک سطر از کامپوننت های data access از Devart برای DBMS های متنوع میباشد. از بین آن ها، مولفه های dotConnect از ابزارهای ORM مانند Entity Framework Core و LinqConnect هواخواهی می نمایند که به شما این قابلیت را میدهد با کلاس های LINQ to SQL فعالیت نمایید.





:: برچسب‌ها: طراحی اپلیکیشن ,
:: بازدید از این مطلب : 61
|
امتیاز مطلب : 0
|
تعداد امتیازدهندگان : 0
|
مجموع امتیاز : 0
تاریخ انتشار : پنج شنبه 29 ارديبهشت 1401 | نظرات ()
مطالب مرتبط با این پست
لیست
می توانید دیدگاه خود را بنویسید


نام
آدرس ایمیل
وب سایت/بلاگ
:) :( ;) :D
;)) :X :? :P
:* =(( :O };-
:B /:) =DD :S
-) :-(( :-| :-))
نظر خصوصی

 کد را وارد نمایید:

آپلود عکس دلخواه: