Ang LINQ misulod sa .NET isip usa ka gamhanan nga bag-ong data manipulation language. Ang LINQ ngadto sa SQL isip kabahin niini nagtugot kanimo sa pagpakigsulti nga sayon sa usa ka DBMS gamit, pananglitan, Entity Framework. Bisan pa, sa kanunay nga paggamit niini, ang mga nag-develop nakalimot sa pagtan-aw kung unsang klase sa SQL nga pangutana ang gipangutana nga tighatag, sa imong kaso nga Entity Framework, nga mamugna.
Atong tan-awon ang duha ka pangunang punto gamit ang usa ka pananglitan.
Aron mahimo kini, paghimo og Test database sa SQL Server, ug paghimo og duha ka lamesa niini gamit ang mosunod nga pangutana:
Paghimo og mga lamesa
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
Karon atong pun-on ang Ref table pinaagi sa pagpadagan sa mosunod nga script:
Pagpuno sa Ref table
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
Susama natong pun-on ang lamesa sa Customer gamit ang mosunod nga script:
Pag-populate sa lamesa sa Customer
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
Sa ingon, nakadawat kami og duha ka mga lamesa, ang usa niini adunay labaw pa sa 1 milyon nga mga laray sa datos, ug ang lain adunay labaw pa sa 10 milyon nga mga laray sa datos.
Karon sa Visual Studio kinahanglan nimo nga maghimo usa ka pagsulay nga Visual C# Console App (.NET Framework) nga proyekto:
Sunod, kinahanglan ka magdugang usa ka librarya alang sa Entity Framework aron makig-uban sa database.
Aron idugang kini, pag-right-click sa proyekto ug pilia ang Manage NuGet Packages gikan sa menu sa konteksto:
Dayon, sa bintana sa pagdumala sa package sa NuGet nga makita, isulod ang pulong nga "Entity Framework" sa search window ug pilia ang Entity Framework package ug i-install kini:
Sunod, sa App.config file, pagkahuman sa pagsira sa configSections nga elemento, kinahanglan nimong idugang ang mosunud nga bloke:
<connectionStrings>
<add name="DBConnection" connectionString="data source=ИМЯ_ЭКЗЕМПЛЯРА_MSSQL;Initial Catalog=TEST;Integrated Security=True;" providerName="System.Data.SqlClient" />
</connectionStrings>
Sa connectionString kinahanglan nimo nga mosulod sa koneksyon string.
Karon maghimo kita og 3 ka interface sa lain nga mga file:
- Pag-implementar sa interface sa IBaseEntityID
namespace TestLINQ { public interface IBaseEntityID { int ID { get; set; } } }
- Pagpatuman sa interface sa IBaseEntityName
namespace TestLINQ { public interface IBaseEntityName { string Name { get; set; } } }
- Pagpatuman sa interface sa IBaseNameInsertUTCDate
namespace TestLINQ { public interface IBaseNameInsertUTCDate { DateTime InsertUTCDate { get; set; } } }
Ug sa usa ka bulag nga file maghimo kami usa ka base nga klase nga BaseEntity alang sa among duha nga mga entidad, nga maglakip sa sagad nga mga natad:
Pagpatuman sa base nga klase nga BaseEntity
namespace TestLINQ
{
public class BaseEntity : IBaseEntityID, IBaseEntityName, IBaseNameInsertUTCDate
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime InsertUTCDate { get; set; }
}
}
Sunod, maghimo kami sa among duha ka entidad sa managlahing mga file:
- Pagpatuman sa klase sa Ref
using System.ComponentModel.DataAnnotations.Schema; namespace TestLINQ { [Table("Ref")] public class Ref : BaseEntity { public int ID2 { get; set; } } }
- Pagpatuman sa klase sa 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; } } }
Karon maghimo kita usa ka konteksto sa UserContext sa usa ka lahi nga file:
Pagpatuman sa klase sa UserContex
using System.Data.Entity;
namespace TestLINQ
{
public class UserContext : DbContext
{
public UserContext()
: base("DbConnection")
{
Database.SetInitializer<UserContext>(null);
}
public DbSet<Customer> Customer { get; set; }
public DbSet<Ref> Ref { get; set; }
}
}
Nakadawat kami usa ka andam nga solusyon alang sa pagpahigayon sa mga pagsulay sa pag-optimize sa LINQ hangtod sa SQL pinaagi sa EF alang sa MS SQL Server:
Karon isulod ang mosunod nga code sa Program.cs file:
Program.cs file
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<string>();
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();
}
}
}
}
Sunod, ilunsad nato ang atong proyekto.
Sa pagtapos sa trabaho, ang mosunod nga ipakita sa console:
Nahimo nga SQL Query
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])
Sa ato pa, sa kinatibuk-an, ang LINQ nga pangutana nakamugna og SQL nga pangutana sa MS SQL Server DBMS nga maayo.
Karon atong usbon ang AND nga kondisyon sa OR sa LINQ nga pangutana:
Pangutana sa LINQ
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 };
Ug atong ilunsad ang atong aplikasyon pag-usab.
Ang pagpatay ma-crash sa usa ka sayup tungod sa oras sa pagpatuman sa mando nga sobra sa 30 segundos:
Kung imong tan-awon ang pangutana nga gihimo sa LINQ:
, unya imong masiguro nga ang pagpili mahitabo pinaagi sa Cartesian nga produkto sa duha ka set (mga lamesa):
Nahimo nga SQL Query
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]
Atong isulat pag-usab ang LINQ nga pangutana sama sa mosunod:
Na-optimize nga LINQ nga pangutana
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
select new { Data1 = e1.Name, Data2 = e2.Name });
Dayon atong makuha ang mosunod nga SQL query:
Pangutana sa SQL
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]
Alangan, sa mga pangutana sa LINQ mahimo ra adunay usa ka kondisyon sa pag-apil, busa dinhi posible nga maghimo usa ka katumbas nga pangutana gamit ang duha nga mga pangutana alang sa matag kondisyon ug dayon isagol kini pinaagi sa Union aron makuha ang mga duplicate taliwala sa mga linya.
Oo, ang mga pangutana sa kasagaran dili katumbas, nga gikonsiderar nga ang kompleto nga doble nga mga laray mahimong ibalik. Bisan pa, sa tinuud nga kinabuhi, dili kinahanglan ang kompleto nga mga duplicate nga linya ug ang mga tawo naningkamot sa pagtangtang niini.
Karon atong itandi ang mga plano sa pagpatuman niining duha ka pangutana:
- alang sa CROSS JOIN ang kasagaran nga oras sa pagpatuman mao ang 195 segundos:
- alang sa INNER JOIN-UNION ang kasagaran nga oras sa pagpatuman dili mubu sa 24 segundos:
Sama sa imong makita gikan sa mga resulta, alang sa duha ka mga lamesa nga adunay minilyon nga mga rekord, ang na-optimize nga LINQ nga pangutana sa daghang mga higayon nga mas paspas kaysa sa wala ma-optimize.
Alang sa kapilian nga adunay AND sa mga kondisyon, usa ka pangutana sa LINQ sa porma:
Pangutana sa LINQ
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 };
Ang husto nga pangutana sa SQL hapit kanunay mabuhat, nga modagan sa kasagaran sa mga 1 segundo:
Usab alang sa LINQ sa Objects manipulations imbes sa usa ka pangutana sama sa:
LINQ nga pangutana (1st nga kapilian)
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 };
mahimo nimong gamiton ang pangutana sama sa:
LINQ nga pangutana (2st nga kapilian)
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 };
diin:
Paghubit sa duha ka array
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" } };
, ug ang tipo sa Para gihubit ingon sa mosunod:
Kahulugan sa Para Type
class Para
{
public int Key1, Key2;
public string Data;
}
Busa, among gisusi ang pipila ka mga aspeto sa pag-optimize sa LINQ nga mga pangutana sa MS SQL Server.
Ikasubo, bisan ang mga eksperyensiyado ug nanguna nga mga developer sa NET nakalimot nga kinahanglan nila nga masabtan kung unsa ang gibuhat sa mga panudlo nga ilang gigamit sa luyo sa mga talan-awon. Kung dili, mahimo silang mga configurator ug mahimo’g magtanom usa ka bomba sa oras sa umaabot kung gi-scale ang solusyon sa software ug adunay gamay nga pagbag-o sa mga kondisyon sa gawas sa palibot.
Usa ka mubo nga pagrepaso ang gihimo usab
Ang mga tinubdan alang sa pagsulay - ang proyekto mismo, ang paghimo sa mga lamesa sa TEST database, ingon man ang pagpuno niini nga mga lamesa sa datos nahimutang
Usab sa kini nga repository, sa folder nga Mga Plano, adunay mga plano alang sa pagpatuman sa mga pangutana nga adunay mga kondisyon sa O.
Source: www.habr.com