شاخصهای مهم بررسی کارآیی کوئریها
در ابتدای بررسی هر کوئری، باید 4 شاخص بسیار مهم، مدنظر باشند:
- مدت زمان اجرای کوئری: هرچند بررسی مدت زمان اجرای کوئری، شاخص مهمیاست، اما الزاما حاوی اطلاعات مفیدی در مورد آن کوئری نیست. برای مثال اگر یک کوئری زیاد طول میکشد، حتما به معنای وجود مشکلی با آن نیست؛ ممکن است اطلاعات زیادی را واکشی میکند یا ممکن است توسط عاملی سد شدهاست. در این موارد هرچند مشکلاتی وجود دارند، اما مستقیما مرتبط با آن کوئری نیستند.
- میزان مصرف CPU: میزان کاری که باید توسط CPU انجام شود تا کوئری به نتیجه برسد.
- I/O: در SQL Server میتوان هم physical I/O و هم logical I/O را بررسی کرد. برای مثال اگر اطلاعات مورد درخواست توسط کوئری هم اکنون در حافظه موجود باشند، نیازی به physical I/O پرهزینه نخواهد بود و در مقابل آن logical I/O کم هزینهتر است.
- میزان مصرف حافظه
در کل هر کدام از این شاخصها اگر دارای مقدار بالایی باشند، بیانگر وجود مشکلی است.
مروری بر ابزارهای مختلف اندازهگیری شاخصهای کارآیی
Management studio
درون Management studio میتوان اطلاعات مرتبط با یک کوئری را به صورت زنده مشاهده کرد. البته این اطلاعات صرفا مرتبط با یک کوئری و یا تعدادی مشخص هستند؛ چون باید کوئری را به صورت دستی درون این برنامه اجرا کرد و سپس اطلاعات اجرای کوئریها را دریافت نمود. اطلاعات آماری که توسط آن نیز ارائه میشود محدودیتهایی دارد. برای مثال مدت زمان اجرای کوئری و یا تعداد رکوردهای تحت تاثیر قرار گرفته شده را میتوان مشاهده کرد. اما به اندازهی اطلاعات ارائه شدهی در یک execution plan کامل نیست. به علاوه بازگشت اطلاعات حاصل از اجرای کوئریها درون این برنامه، سربار خودش را داشته و سبب کند شدن برنامه میشود. در آخر اطلاعات ارائه شدهی توسط آنرا نیز باید از قسمتهای مختلفی جمع آوری و به صورت دستی ذخیره کرد.
Extended Events
توسط Extended Events نیز میتوان همانند Management studio، اطلاعات آماری یک تک کوئری و یا یک batch را جمع آوری کرد؛ اما پس از ایجاد و تنظیم آن، به صورت خودکار اجرا میشود. در حین تعریف یک سشن Extended Events میتوان شاخصهای خاصی را انتخاب کرد و یا شرطهای دقیقی را اعمال کرد. خروجی آن نیز به صورت خودکار در یک فایل ذخیره میشود.
Dynamic management objects
با استفاده از DMO's از نتایج آماری مرتبط با تک کوئریها، به نتایج تجمعی حاصل از اجرای آنها میرسیم. این نتایج نیز در plan cache ذخیره میشوند. به این معنا که اگر کش، تخلیه (با اجرای دستور DBCC FREEPROCCACHE) و یا سرور ریاستارت شود، این اطلاعات از دست خواهند رفت. هدف آن بیشتر رفع اشکال کوئریهایی است که هم اکنون در حال اجرا هستند. اگر نیاز به اطلاعات دورهای را داشته باشید، نیاز خواهید داشت تا با تهیهی snapshotهایی از بانک اطلاعاتی، این تاریخچه را تکمیل کنید. به همین جهت Query Store ارائه شدهاست تا نیازی به اینکار نباشد.
Query Store
Query Store کار ذخیره سازی متن plan و آمار تجمعی مرتبط با آنرا به صورت خودکار انجام میدهد و آنرا درون بانک اطلاعاتی کاربر ذخیره میکند. به همین جهت با خالی شدن کش، برخلاف DMO's، اطلاعات آن حذف نمیشود.
مثالی از روشهای مختلف جمع آوری اطلاعات آماری حاصل از اجرای کوئریها در SQL Server
در ادامه قصد داریم با مثالی، خلاصهای را از سه قسمتی که تاکنون بررسی کردیم، ارائه دهیم. برای این منظور ابتدا رویهی ذخیره شدهی زیر را ایجاد میکنیم:
USE [WideWorldImporters]; GO DROP PROCEDURE IF EXISTS [Application].[usp_GetPersonInfo]; GO CREATE PROCEDURE [Application].[usp_GetPersonInfo] (@PersonID INT) AS SELECT [p].[FullName], [p].[EmailAddress], [c].[FormalName] FROM [Application].[People] [p] LEFT OUTER JOIN [Application].[Countries] [c] ON [p].[PersonID] = [c].[LastEditedBy] WHERE [p].[PersonID] = @PersonID; GO
سپس یک سشن Extended event را با نام QueryPerf ایجاد میکنیم:
IF EXISTS ( SELECT * FROM sys.server_event_sessions WHERE [name] = 'QueryPerf') BEGIN DROP EVENT SESSION [QueryPerf] ON SERVER; END GO CREATE EVENT SESSION [QueryPerf] ON SERVER ADD EVENT sqlserver.sp_statement_completed( WHERE ([duration]>(1000))), ADD EVENT sqlserver.sql_statement_completed( WHERE ([duration]>(1000))), ADD EVENT sqlserver.query_post_execution_showplan ADD TARGET package0.event_file( SET filename=N'C:\Temp\QueryPerf\test.xel',max_file_size=(256)) WITH ( MAX_MEMORY=16384 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB, MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=ON,STARTUP_STATE=OFF); GO
در ادامه Query Store را نیز بر روی بانک اطلاعاتی WideWorldImporters فعال کرده و همچنین اگر اطلاعاتی از پیش در آن وجود دارند، پاک میشود.
USE [master]; GO ALTER DATABASE [WideWorldImporters] SET QUERY_STORE = ON; GO ALTER DATABASE [WideWorldImporters] SET QUERY_STORE ( OPERATION_MODE = READ_WRITE, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30), DATA_FLUSH_INTERVAL_SECONDS = 60, INTERVAL_LENGTH_MINUTES = 5, MAX_STORAGE_SIZE_MB = 100, QUERY_CAPTURE_MODE = ALL, SIZE_BASED_CLEANUP_MODE = AUTO, MAX_PLANS_PER_QUERY = 200); GO ALTER DATABASE [WideWorldImporters] SET QUERY_STORE CLEAR; GO
سپس هر آنچه را که در plan cache نیز وجود دارد، حذف میکنیم:
DBCC FREEPROCCACHE; GO
اکنون سشن QueryPerf را که پیشتر ایجاد کردیم، آغاز میکنیم:
ALTER EVENT SESSION [QueryPerf] ON SERVER STATE = START; GO
در ادامه چون میخواهیم نتایج آماری را در management studio نیز مشاهده کنیم، ابتدا جمع آوری شاخصهای آماری را در یک پنجرهی جدید new query، فعال میکنیم:
SET STATISTICS IO ON; GO SET STATISTICS TIME ON; GO SET STATISTICS XML ON; GO
همچنین در منوی Query، گزینهی Include client statistics را نیز انتخاب میکنیم تا مشخص شود که آیا عملیات insert/update/delete انجام شدهاست. چه تعداد ردیف تحت تاثیر اجرای این کوئری قرار گرفتهاند. چه تعداد تراکنش انجام شدهاست. همچنین اطلاعات آماری شبکه و زمان نیز ارائه شوند.
پس از این تنظیمات، اکنون نوبت به اجرای کوئریهای زیر رسیدهاست که یکی پارامتری است و دیگری AdHoc:
USE [WideWorldImporters]; GO EXECUTE [Application].[usp_GetPersonInfo] 1234; GO SELECT [s].[StateProvinceName], [s].[SalesTerritory], [s].[LatestRecordedPopulation], [s].[StateProvinceCode] FROM [Application].[Countries] [c] JOIN [Application].[StateProvinces] [s] ON [s].[CountryID] = [c].[CountryID] WHERE [c].[CountryName] = 'United States'; GO
سپس سشن QueryPerf را متوقف و حذف میکنیم:
ALTER EVENT SESSION [QueryPerf] ON SERVER STATE = STOP; GO DROP EVENT SESSION [QueryPerf] ON SERVER; GO
اگر بخواهیم از عملیات صورت گرفته توسط DMO's کوئری بگیریم:
SELECT [qs].[last_execution_time], [qs].[execution_count], [qs].[total_elapsed_time], [qs].[total_elapsed_time]/[qs].[execution_count] [AvgDuration], [qs].[total_logical_reads], [qs].[total_logical_reads]/[qs].[execution_count] [AvgLogicalReads], [t].[text], [p].[query_plan] FROM sys.dm_exec_query_stats [qs] CROSS APPLY sys.dm_exec_sql_text([qs].sql_handle) [t] CROSS APPLY sys.dm_exec_query_plan([qs].[plan_handle]) [p] WHERE [t].[text] LIKE '%Countries%'; GO
همانطور که مشاهده میکنید، شاخصهای چهارگانهای که در ابتدای بحث معرفی شدند، در مورد کوئری پارامتری نوشته شده، وضعیت بسیار بهتری نسبت به کوئری AdHoc دوم دارند.
از Query Store هم میتوان به صورت زیر کوئری گرفت (علاوه بر قسمت رابط کاربری Query Store که ذیل اشیاء مرتبط با بانک اطلاعاتی WideWorldImporters در management studio قابل مشاهدهاست):
USE [WideWorldImporters]; GO SELECT [qsq].[query_id], [qst].[query_sql_text], CASE WHEN [qsq].[object_id] = 0 THEN N'Ad-hoc' ELSE OBJECT_NAME([qsq].[object_id]) END AS [ObjectName], [qsp].[plan_id], [rs].[count_executions], [rs].[avg_logical_io_reads], [rs].[avg_duration], TRY_CONVERT(XML, [qsp].[query_plan]), [rs].[last_execution_time], (DATEADD(MINUTE, -(DATEDIFF(MINUTE, GETDATE(), GETUTCDATE())), [rs].[last_execution_time])) AS [LocalLastExecutionTime] FROM [sys].[query_store_query] [qsq] JOIN [sys].[query_store_query_text] [qst] ON [qsq].[query_text_id] = [qst].[query_text_id] JOIN [sys].[query_store_plan] [qsp] ON [qsq].[query_id] = [qsp].[query_id] JOIN [sys].[query_store_runtime_stats] [rs] ON [qsp].[plan_id] = [rs].[plan_id] WHERE [qst].[query_sql_text] LIKE '%Countries%'; GO
قبل از پاسخگویی به سؤال بالا، به یک سری مقدمات نیاز است:
وقتی یک کوئری به اس کیو ال ارسال میشود، چه اتفاقی رخ میدهد؟
وقتی یک کوئری ارسال میشود، تعدادی از پروسسها بر روی کوئری شروع به فعالیتهایی مانند مهیا نمودن دادههای بازگشتی، یا ذخیره سازی و ... میکنند.
پروسسها به دو دسته زیر تقسیم میشوند:
- پروسسهایی که در relational engine رخ میدهند
- پروسسهایی که در storage engine رخ میدهند
در
relational engine، هر کوئری pars شده و سپس بوسیله query optimizer
پردازش و پلن اجرایی (execution plan) آن که بفرمت باینری است، ایجاد میشود و
به storage engine ارسال میگردد. در storage engine پروسسهایی مانند قفل
گذاری، نگهداری ایندکسها و تراکنشها رخ میدهد. هنگامیکه اس کیو ال
سرور کوئری را دریافت مینمایند، آن را بلافاصله به relational engine ارسال
میکند. سپس نحو (syntax) آن بررسی میشود؛ این عمل query parsing نامیده
میشود. خروجی عملیات پارسر، یک ساختار درختی (query tree) است. این ساختار
درختی مشخص کننده مراحل لازم جهت اجرای کوئری ارائه شده میباشد.
اگر یک کوئری شامل DML نباشد (مانند ساخت جدول)، علمیات بهبود برروی آن صورت
نخواهد گرفت. ولی در صورتیکه کوئری ارسالی، DML باشد، درخت اشاره شده در
بالا به algebrizer فرستاده میشود که وظیفه آن تفسیر و بررسی کلیه نام
اشیاء، جداول و ستونهای اشاره شده در متن کوئری است. فرآیند algebrizer
بسیار مهم و حیاتی است؛ بدلیل اینکه در کوئری ممکن است اشاره کنندههایی به
اشیایی باشند که در بانک اطلاعاتی موجود نیست. خروجی algebrizer یک query
processor tree باینری است که به بهبود دهنده کوئری ارسال میگردد.
معرفی Query Optimizer (بهبود دهنده پرس و جو)
بهبود
دهنده، بهترین مسیر اجرای کوئری را مشخص میکند. این بهبود دهنده است که مشخص
میکند که اطلاعات بوسیله ایندکس دریافت شوند، یا اینکه از چه اتصالی استفاده
شود و الی آخر. این تصمیمات براساس محاسبات هزینههای (میزان پردازش لازم
cpu و I/O) پلن اجرایی صورت خواهد پذیرفت. بهمین دلیل به پلن cost-based نیز شناخته میشود.
هنگامیکه کوئری سادهای مانند دریافت اطلاعات از یک جدول، که بر روی آن
ایندکس گذاری انجام نشدهاست، ارسال شود، بهبود دهنده بجای مشخص نمودن یک
پلن مناسب بهینه، از یک پلن ساده (trivial) استفاده میکند. ولی برعکس در
صورتیکه کوئری trivial نباشد (یعنی مثلا کوئری به گونهای باشد که از
ایندکسها به شکل صحیحی استفاده شده باشند)، بهبود دهنده یک پلن مناسب را
براساس اطلاعات آماری مهیا شده در اس کیو ال سرور، تولید و انتخاب مینماید.
اطلاعات
آماری از ستونها و ایندکسها جمع آوری میشود. این اطلاعات شامل نحوه
توزیع داده، یکتایی و انتخاب شوندگی است. این اطلاعات توسط یک histogram
ارائه میشود. اگر اطلاعات آماری برای یک ستون و یا ایندکس وجود داشته
باشد، بهبود دهنده از آنها برای محاسبات خود استفاده خواهد کرد. اطلاعات
آماری بصورت خودکار برای تمام ایندکسها و یا هر ستونی که بشود بر روی آنها
where یا join نوشت، فراهم خواهد شد.
بهبود دهنده با مقایسه پلنها براساس بررسی تفاوتهای انواع joinها، چیدمان
مجدد ترتیب join و بررسی ایندکسهای مختلف و سایر فعالیتهای دیگر، پلن
مناسب را انتخاب و از آن استفاده میکند. در طی هر کدام از فعالیتهای
اشاره شده، زمان اجرای آنها نیز تخمین زده (estimated cost) خواهد شد و در
پایان، زمان کل تخمینی بدست خواهد آمد و بهبود دهنده از این زمان برای انتخاب
پلن مناسب بهره خواهد برد. باید توجه داشت که این زمان تقریبی است. زمانیکه بهبود دهنده پلن اجرایی انتخاب میکند، یک actual plan را ایجاد و در حافظه ذخیره
میشود؛ بنام plan cache. البته درصورتیکه پلن مشابه و بهینهتری وجود
نداشته باشد.
استفاده مجدد از پلن ها
تولید پلن هزینه بر است. بههمین دلیل اس کیوال سرور اقدام به ذخیره سازی و نگهداری آنها میکند تا بتواند از آنها مجددا استفاده نماید؛ البته تا جایی که مقدور باشد. هنگامیکه آنها تولید میشوند، در قسمتی از حافظه بنام plan cache ذخیره میشوند. به این عمل procedure cache نیز گفته میشود.
هنگامیکه کوئری به سرور ارسال میشود، بوسیله بهبود دهنده، یک estimated plan ایجاد
خواهد شد و قبل از اینکه به storage engine ارسال شود، بهبود دهنده estimated
plan را با actual execution planهای موجود در plan cache مقایسه میکند.
در صورتیکه یک actual plan را مطابق با estimated plan پیدا نماید، از آن مجدد
استفاده خواهد کرد. این استفاده مجدد به عدم تحمیل سربار اضافهای به سرور جهت
کوئریهای بزرگ و پیچیده که در زمان واحد، هزاران بار اجرا خواهند شد، منجر میشود.
هر پلن فقط یکبار در حافظه ذخیره خواهد شد. ولی در مواقعی با تشخیص
بهبود دهنده و هزینه پلن، یک کوئری میتواند پلن دیگری نیز داشته باشد.
بنابراین پلن دوم نیز با مجموعه عملیاتی متفاوت، جهت اجرای موازی (parallel
execution) برای یک کوئری ایجاد و در حافظه ذخیره میشود.
پلنهای اجرایی برای همیشه در حافظه باقی نخواهند ماند. پلنهای اجرایی دارای
طول عمری طبق فرمول حاصل ضرب هزینه، در تعداد دفعات میباشند. مثلاً پلنی با
هزینه 10 و تعداد دفعات اجرای 5، طول عمر 50 را خواهد داشت. پروسس lazywriter
که یک پروسس داخلی است وظیفه آزاد سازی تمام انواع کشها، از جمله پلن کش را دارد. این پروسس در بازههای مشخص، تمام اشیاء درون حافظه را بررسی کرده
و یک واحد از طول عمر آنها میکاهد.
در موارد زیر، یک پلن از حافظه پاک خواهد شد:
1. به حافظه بیشتری نیاز باشد
2. طول عمر پلن صفر شده باشد
جمله آخر، معمولا باعث ایجاد مشکل میشوند.
اگر optimizer تکست کوئری مشابهی را مشاهده نماید، ولی با پارامترهای متفاوت، به کش پلن مراجعه کرده و اگر در آن جا قرار داشت، از آن مجددا استفاده مینماید. این استفاده مجدد خوب است؛ اما درصورتیکه پارامتر ارسالی نال باشد چه اتفاقی رخ میدهد؟ جدول سفارشات محصول بسیار حجیم است و متاسفانه از پلنی که برای بازگشت 40 رکورد قبلا ایجاد شده، برای بازگشت این حجم بالای از رکوردها استفاده میشود که این کشنده است.
هیچ تضمینی وجود ندارد که از وقوع این اتفاق جلوگیری نمایید؛ اما میتوانید در هنگام توسعه، پروسیجر را شناسایی و نسبت به رفع آنها اقدام نمایید. ابتدا کش پلن را خالی نمایید و سپس پروسیجر را با مقادیر متفاوت، اجرا نمایید. در صورتیکه پلنهای متفاوتی مشاهده نمودید، این یک علامت هشدار است و میبایست نسبت به رفع آنها اقدام فوری نمایید.
- WebNote.ir | عادت های بد برنامه نویسی (کارمندی) - قسمت دوم | www.webnote.ir
- Load and Web Performance Testing using Visual Studio Ultimate 2010 [Part 1] | geekswithblogs.net
- Load and Web Performance Testing using Visual Studio Ultimate 2010-Part 2 | geekswithblogs.net
- Load and Web Performance Testing using Visual Studio Ultimate 2010-Part 3 | geekswithblogs.net
- SuperSocket, an extensible socket application framework | supersocket.codeplex.com
- XPath Visualizer | xpathvisualizer.codeplex.com
- Parallel Visualization Pack در نگارش بعدی ویژوال استودیو | www.infoq.com
- جاوا 8 با پشتیبانی از Lambda | jdk8.java.net
- معماری ARM و دات نت 4.5 | weblog.ikvm.net
در اس کیوال سرور 2000 اگر از ad hoc کوئری استفاده شود احتمال recompile شدن بسیار زیاد است اما این مورد از اس کیوال سرور 2005 به بعد به شدت بهبود یافته. برای مطالعه بیشتر
Execution Plan Caching and Reuse
http://technet.microsoft.com/en-us/library/ms181055%28SQL.90%29.aspx
نکته مهمی که در ad hoc کوئریها ممکن است سبب recompile شدن plan اجرایی آن شود بحث تغییر نوع یا اندازهی پارامترهای مورد استفاده است. اگر این موارد به صورت صریح ذکر شوند و از پارامترهایADO.Net استفاده شوند، تشخیص نوع و اندازه پارامترها برای اس کیوال سرور بسیار ساده شده و حتما از کش بجای recompile استفاده خواهد کرد. برای مطالعه بیشتر:
Parameters and Execution Plan Reuse
http://technet.microsoft.com/en-us/library/ms175580%28SQL.90%29.aspx
دقیقا همین نکته را اگر از Nhibernate استفاده میکنید نیز باید رعایت کنید:
NHibernate queries & sql server execution plans
http://testdrivendevelopment.wordpress.com/2009/03/10/nhibernate-queries-sql-server-execution-plans/
خلاصه این مقاله به این صورت است که اس کیوال سرور را وادار نکنیم که از recompile به جهت مشخص نبودن طول یا اندازه پارامترها، هر بار استفاده نماید.
وبلاگها و سایتهای ایرانی
امنیت
Visual Studio
ASP. Net
طراحی وب
PHP
- Aptana PHP 1.0 منتشر شد (اگر قبلا این IDE بسیار قابل توجه را دریافت کرده بودید فقط کافی است به منوی aptana و گزینه my aptana مراجعه کرده و از قسمت plugins ، این پلاگین 18 مگابایتی را دریافت کنید.)
اسکیوال سرور
سی شارپ
عمومی دات نت
ویندوز
متفرقه
- مزایای مهاجرت از ویژوال سورس سیف مایکرسافت به SVN
- افزونهای برای فایرفاکس جهت GUI prototyping (خیلی کار جالبی کرده ولی چرا به صورت یک افزونه؟)
NuGet 4.0 RTM منتشر شد
سیلورلایت 5 و تاریخ شمسی
Silverlight does not enable you to directly enumerate over a data service query. This is because enumeration automatically send a synchronous request to the data service. Because Silverlight only supports asynchronous operations, you must instead call BeginExecute and EndExecute method to obtain a query result that supports enumeration.
I’m happy to announce the release of Oracle Entity Framework Core (EF Core) 3.19.0 beta on NuGet Gallery. This beta supports the new changes in Entity Framework Core 3.1. Since it’s a beta, be sure to check off the “Include Prerelease” box when searching for the assembly on NuGet Gallery.
امنیت در LINQ to SQL
جواب کوتاه: بسیار زیاد!
توضیحات:
string query = @"SELECT * FROM USER_PROFILE
WHERE LOGIN_ID = '"+loginId+@"' AND PASSWORD = '"+password+@"'";
protected void btnSearch_Click(object sender, EventArgs e)
{
String cmd = @"SELECT [CustomerID], [CompanyName], [ContactName]
FROM [Customers] WHERE CompanyName ='" + txtCompanyName.Text
+ @"'";
SqlDataSource1.SelectCommand = cmd;
GridView1.Visible = true;
}
راه حلی که برای مقابله با آن در دات نت ارائه شده نوشتن کوئریهای پارامتری است و در این حالت کار encoding اطلاعات ورودی به صورت خودکار توسط فریم ورک مورد استفاده انجام خواهد شد؛ همچنین برای مثال اس کیوال سرور، execution plan این نوع کوئریهای پارامتری را همانند رویههای ذخیره شده، کش کرده و در دفعات آتی فراخوانی آنها به شدت سریعتر عمل خواهد کرد. برای مثال:
SqlCommand cmd = new SqlCommand("SELECT UserID FROM Users WHERE UserName=@UserName AND Password=@Password");
cmd.Parameters.Add(new SqlParameter("@UserName", System.Data.SqlDbType.NVarChar, 255, UserName));
cmd.Parameters.Add(new SqlParameter("@Password", System.Data.SqlDbType.NVarChar, 255, Password));
dr = cmd.ExecuteReader();
if (dr.Read()) userId = dr.GetInt32(dr.GetOrdinal("UserID"));
اما در مورد LINQ to SQL چطور؟
این سیستم به صورت پیش فرض طوری طراحی شده است که تمام کوئریهای SQL نهایی حاصل از کوئریهای LINQ نوشته شده توسط آن، پارامتری هستند. به عبارت دیگر این سیستم به صورت پیش فرض برای افرادی که دارای حداقل اطلاعات امنیتی هستند به شدت امنیت بالایی را به همراه خواهد آورد.
برای مثال کوئری LINQ زیر را در نظر بگیرید:
var products = from p in db.products
where p.description.StartsWith(_txtSearch.Text)
select new
{
p.description,
p.price,
p.stock
};
exec sp_executesql N'SELECT [t0].[description], [t0].[price], [t0].[stock]
FROM [dbo].[products] AS [t0]
WHERE [t0].[description] LIKE @p0',N'@p0 varchar(5)',@p0='sony%'
db.Log = Console.Out;
همانطور که ملاحظه میکنید، کوئری نهایی تولید شده پارامتری است و در صورت ورود اطلاعات خطرناک در پارامتر p0 ، هیچ اتفاق خاصی نخواهد افتاد و صرفا رکوردی بازگشت داده نمیشود.
و یا همان مثال کلاسیک اعتبار سنجی کاربر را در نظر بگیرید:
public bool Validate(string loginId, string password)
{
DataClassesDataContext db = new DataClassesDataContext();
var validUsers = from user in db.USER_PROFILEs
where user.LOGIN_ID == loginId
&& user.PASSWORD == password
select user;
if (validUsers.Count() > 0) return true;
else return false;
}
SELECT [t0].[LOGIN_ID], [t0].[PASSWORD]
FROM [dbo].[USER_PROFILE] AS [t0]
WHERE ([t0].[LOGIN_ID] = @p0) AND ([t0].[PASSWORD] = @p1)
تذکر مهم هنگام استفاده از سیستم LINQ to SQL :
اگر با استفاده از LINQ to SQL مجددا به روش قدیمی اجرای مستقیم کوئریهای SQL خود همانند مثال زیر روی بیاورید (این امکان نیز وجود دارد)، نتیجه این نوع کوئریهای حاصل از جمع زدن رشتهها، پارامتری "نبوده" و مستعد به تزریق اس کیوال هستند:
string sql = "select * from Trade where DealMember='" + this.txtParams.Text + "'";
var trades = driveHax.ExecuteQuery<Trade>(sql);
اما روش صحیحی نیز در مورد بکارگیری متد ExecuteQuery وجود دارد. استفاده از این متد به شکل زیر مشکل را حل خواهد کرد:
IEnumerable<Customer> results = db.ExecuteQuery<Customer>(
"SELECT contactname FROM customers WHERE city = {0}", "Tehran");