اشتراک‌ها
نکاتی راجع به Event Sourcing که به شما نمی‌گویند

این روزها Event Sourcing و CQRS محبوبیت زیادی بین توسعه دهندگان پیدا کرده است و من پس از چندین سال استفاده از این الگو قصد دارم نکات و هشدارهایی را اینجا بازگو کنم

نکاتی راجع به Event Sourcing که به شما نمی‌گویند
اشتراک‌ها
نگاهی به تغییرات NET Framework 4.6.

نگارش 4.6 در این تاریخ در مرحله‌ی پیش نمایش قرار دارد و شامل افزوده شدن یک سری بسته‌های سورس باز به آن، تغییرات encoding و event tracing است.

نگاهی به تغییرات NET Framework 4.6.
نظرات مطالب
پیاده سازی CQRS توسط MediatR - قسمت سوم
فرض کنید در ابتدای یک Handler، یک Handler دیگر را فراخوانی می‌کنیم (شبیه Handler فوق که در آن عمل Save و Event ای Raise شده است) اگر این Handler با خطا مواجه شود چطور می‌توان سیستم را به حالت اولیه برگرداند؟
مطالب
روش اجرای پروژه‌های ASP.NET Core توسط Rider و IIS Express
آخرین نگارش Rider، پشتیبانی از اجرای برنامه‌های ASP.NET Core را توسط IIS Express هم اضافه کرده‌است. در این حالت اگر به صورت پیش‌فرض و بدون تنظیم خاصی، سعی در اجرای برنامه‌ی وب خود توسط IIS Express کنید، با خطای زیر مواجه خواهید شد:
HTTP Error 500.21 - Internal Server Error
Handler "aspNetCore" has a bad module "AspNetCoreModule" in its module list
در ادامه قصد داریم روش رفع این مشکل را بررسی کنیم.


پیشنیازهای کار با IIS Express توسط Rider

- نصب IIS Express به صورت جداگانه
- نصب بسته‌ی هاستینگ ASP.NET Core برای IIS
این مورد برای اضافه شدن AspNetCoreModuleV2 به IIS Express خام فوق، الزامی است.

یک نکته: نگارش بسته‌ی هاستینگ، باید با SDK و یا runtime نصب شده، مطابقت داشته باشد (بنابراین نصب SDK و یا Runtime نیز ضروری است).


معرفی بسته‌ی هاستینگ ASP.NET Core به IIS Express

پس از نصب این بسته‌ها، فایل واقع در مسیر زیر را برای یافتن واژه‌ی AspNetCoreModule جستجو کنید (یک چنین فایلی در مسیر 64 بیتی C:\Program Files\IIS Express\config\templates\PersonalWebServer نیز وجود دارد):
%PROGRAMFILES(x86)%\IIS Express\config\templates\PersonalWebServer\applicationhost.config
همچنین مسیر زیر نیز باید موجود باشد:
%PROGRAMFILES(x86)%\IIS Express\Asp.Net Core Module\V2
اما اگر واژه‌ی AspNetCoreModule، در این فایل ذکر نشده بود و یا مسیر پوشه‌ی Asp.Net Core Module فوق وجود نداشت، یعنی بسته‌ی هاستینگ نصب شده، به IIS Express معرفی نشده‌است. برای رفع این مشکلات:
- ابتدا پوشه‌ی C:\Program Files (x86)\IIS\Asp.Net Core Module را به درون پوشه‌ی C:\Program Files (x86)\IIS Express کپی کنید.
- سپس پوشه‌ی C:\Program Files\IIS\Asp.Net Core Module را به درون پوشه‌ی C:\Program Files\IIS Express کپی کنید.

- در آخر نیاز است دو فایل config\templates\PersonalWebServer\applicationhost.config را در پوشه‌های x86 و x64 مربوط به IIS Express به صورت زیر ویرایش کنیم:
- پیش از بسته شدن تگ globalModules در قسمت <system.webServer><globalModules>، دو سطر زیر را اضافه کنید:
در فایل C:\Program Files\IIS Express\config\templates\PersonalWebServer\applicationhost.config
<add name="AspNetCoreModule" image="C:\Program Files\IIS Express\aspnetcore.dll" />
<add name="AspNetCoreModuleV2" image="C:\Program Files\IIS Express\Asp.Net Core Module\V2\aspnetcorev2.dll" />
و همچنین در فایل C:\Program Files (x86)\IIS Express\config\templates\PersonalWebServer\applicationhost.config
<add name="AspNetCoreModule" image="C:\Program Files (x86)\IIS Express\aspnetcore.dll" />
<add name="AspNetCoreModuleV2" image="C:\Program Files (x86)\IIS Express\Asp.Net Core Module\V2\aspnetcorev2.dll" />

- ذیل تگ <sectionGroup name="system.webServer">، سطر زیر را اضافه کنید:
<section name="aspNetCore" overrideModeDefault="Allow" />

- ذیل تگ <system.webServer><modules>، دو سطر زیر را اضافه کنید:
<add name="AspNetCoreModule" lockItem="true" />
<add name="AspNetCoreModuleV2" lockItem="true" />

البته برای ذخیره سازی فایل‌های موجود در Program Files، باید آن‌ها را با دسترسی ادمین باز کنید. برای مثال اگر از ++nodepad استفاده کنید، به صورت خودکار این مساله را تشخیص داده و دسترسی صحیح را درخواست می‌کند.


تنظیم Rider برای یافتن مسیر صحیح AspNetCoreModuleV2 نصب شده

در برنامه‌ی Rider، از منوی File، قسمت settings آن، گزینه‌ی Build, Execution, Deployment | IIS Express را انتخاب و سپس مسیرهای x86 و x64 را به صورت زیر تنظیم کنید:


البته دراپ داون‌های این صفحه، به صورت خودکار این مسیرها را پر می‌کنند. فقط کافی است، مسیر صحیح را از طریق آن‌ها انتخاب کنید.

- اکنون به ریشه‌ی پروژه‌ی خود مراجعه کرده و فایل idea\config\applicationhost.config. را در صورت وجود حذف کنید (البته بهتر است کل پوشه‌ی idea. و همچنین vs. را (در صورت وجود) حذف کنید؛ هر دو را با هم. مهم!). برنامه‌ی Rider، این فایل تنظیمات موقتی IIS Express را بر اساس دو فایل config\templates\PersonalWebServer\applicationhost.config ای که اصلاح کردیم، به صورت خودکار تولید می‌کند و حاوی تمام تغییرات فوق خواهد بود.

- فایل web.config واقع در ریشه‌ی پروژه وب نیز بهتر است یک چنین محتوایی را داشته باشد:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%"                
   stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
  </system.webServer>
</configuration>
در اینجا ذکر تنظیم "hostingModel="InProcess سبب بروز خطا می‌شود و به نظر Rider هنوز از آن پشتیبانی نمی‌کند.


تنظیمات IIS Express در Rider

تنظیمات پورت IIS Express، در فایل Properties\launchSettings.json پروژه‌های وب، قابل مشاهده و تغییر است. اگر نیاز به کار با HTTPS باشد، برنامه‌ی Rider، پیام کوچکی را که در آن لینک setup certificate قرار دارد، نمایش می‌دهد و با کلیک بر روی آن، یک مجوز موقتی self-signed certificate تولید و نصب خواهد شد.


و یا در Rider، از منوی بالای صفحه که تنظیمات Build را نمایش می‌دهد، می‌توان IIS Express را به عنوان اجرا کننده‌ی پروژه، انتخاب کرد. پس از انتخاب آن، یکبار دیگر از همان dropdown می‌توان گزینه‌ی edit configuration را انتخاب کرد تا تنظیمات مخصوص IIS Express، ظاهر شود.

بازخوردهای پروژه‌ها
خطا هنگام اجرا در IIS/7.5 کلیه آدرس ها نیاز به دریافت پارامتر ارسالی دارند
با سلام و تشکر از برنامه خوبتون
بنده برنامه رو بروی هاست آپلود کردم و مشکل از آنجا شروع شد که نه خطایی دریافت می‌شه و نه برنامه اجرا می‌شود و بطور اتفاقی متوجه شدم که تنها صفحه ای که لود می‌شود User/logon?returnurl=/admin
که بعد از آن با قرار دادن پارامتر در انتهای هر لینک یا آدرس برنامه به درستی کار کرد (http://blob.com/?blob)و مشخص شد مشکل از routeconfig برنامه می‌باشد که متاسفانه به دلیل حطا ندادن برنامه و اجرا شدن آن در محیط لوکال بدون ایراد راه حل مناسبی پیدانشد
            routes.MapRouteLowercase("Default", "{controller}/{action}/{id}", new
            {
                area = "",
                controller = "Home",
                action = "Index",
                id = UrlParameter.Optional,
            }, new[] { "Iris.Web.Controllers" }
                );
routing سایت را مجبور به دریافت پارامتر نموده./
مطالب
سفارشی کردن قابلیت طراحی ریپورت توسط END USER - بخش اول
در قسمتی از پروژه تجاری، برای طراحی ریپورت توسط کاربر نیاز به موضوع مطرح شده این پست داشتم و به نتایج مطلوبی در این زمینه توسط کامپوننت Stimulsoft Report.Net دست یافتم.
پروژه بنده به صورتی هست که در آن قرار است یک سری اطلاعات توسط DataTable به Report ارسال و ریپورت هم Field‌ها را که از قبل طراحی شده، روی فرم داشته باشد و کاربر فقط این امکان را داشته باشد که مکان فیلد‌ها ( یعنی مختصات ) روی صفحه A4 را با توجه به سلیقه خودش تنظیم و ذخیره نماید.

اهداف :
1.غیر فعال کردن بعضی از امکانات صفحه Design
2.اعمال یکسری تنظیمات از طریق کدنویسی (Code Behind) بر روی ریپورت
مانند : اینکه ShowGrid فعال باشه یا نه -- یا Toolbox.visible=false باشه
3.فارسی سازی محیط طراحی برای کاربر
4.ذخیره و ...

اسکرین شات : ( حالت پیشفرض بدون اعمال تغییرات Runtime )


رفرنس‌های مورد نیاز:
using Stimulsoft.Base.Services;
using Stimulsoft.Report;
using Stimulsoft.Report.Components;
using Stimulsoft.Report.Design;
using Stimulsoft.Report.Design.Toolbars;
using Stimulsoft.Report.Render;
using Stimulsoft.Report.Units;

//ایجاد یک شی از ریپورت            
StiReport report = new StiReport();
//ریست کردن تنظیمات به حالت پیشفرض
 report.Reset();
//ریست کردن تنظیمات سرویس‌های StiConfig.Reset();
//ریست کردن تنظیمات چاپ
 StiSettings.Clear();

            

            //تنظیم عنوان ریپورت به صورت دلخواه
            StiOptions.Designer.DesignerTitle = title + " طراحی فرم ";
            StiOptions.Designer.DesignerTitleText = title + " طراحی فرم ";
            //غیرفعال شدن نمایش تب کدنویسی
            StiOptions.Designer.CodeTabVisible = false;
            //فعال کردن امکان RightToLeft
            StiOptions.Designer.UseRightToLeftGlobalizationEditor = true;
            //غیرفعال شدن قابلیت تغییر نام ریپورت توسط کاربر
            StiOptions.Designer.CanDesignerChangeReportFileName = false;
            //غیر فعال کردن تشخیص اتوماتیک زبان پیش فرض UI طراحی
            StiOptions.Designer.UseSimpleGlobalizationEditor = false;

            //تنظیم تم ریپورت از حالت استاندارد به ریبون
            StiOptions.Windows.GlobalGuiStyle = StiGlobalGuiStyle.Office2010Blue;
            //فعال سازی تم ریبون
            StiOptions.Designer.IsRibbonGuiEnabled = true;
برای دسترسی به بخش Dictionary که به اطلاعات DataSoruce‌ها دسترسی می‌دهد البته برای قابلیت Design :
یک شی از StiOptions ایجاد کنید. سپس Designer و فعال و غیرفعال کردن نمایش هر بخش را و حتی تغییر آیتم‌های موجود در منوی راست کلیک هر شی، قابل تغییر خواهند بود.

پنل Dictionary از سه شاخه اصلی تشکیل می‌شود : که با دستورات زیر می‌توان نمایش این بخش‌ها را در صورت خالی بودن از داده غیر فعال نمود
BusinessObjectsCategory - DataSourcesCategory -VariablesCategory
StiOptions.Designer.Panels.Dictionary.ShowEmptyBusinessObjectsCategory = false;
StiOptions.Designer.Panels.Dictionary.ShowEmptyDataSourcesCategory = false;
StiOptions.Designer.Panels.Dictionary.ShowEmptyVariablesCategory = false;
در صفحه طراحی، 3 پنل وجود دارد :
1- Dictionary
2- Properties
3- Report Tree

که Properties نسبت به هر شیء ایی که از صفحه ریپورت انتخاب می‌کنید، تنظیمات مربوط به آن را برای ویرایش در اختیار کاربر قرار می‌دهد.
پنل Report Tree نیز از داده‌های موجود از دیتاسورس بخش Dictionary که به صورت شیء در صفحه ریپورت قرار داده شده‌اند نمایش درخت واره‌ای را در اختیار کاربر قرار می‌دهد و می‌تواند از اشیاء این درخت واره در ریپورت به صورت متعدد استفاده نماید.

می‌توان هر کدام از این پنل‌ها را ( که به صورت سرویس در Stimulsoft تعریف شده) از دید کاربر مخفی نمود یا به صورت محدود یکسری از قابلیت‌ها را در اختیار کاربر قرار داد:
//غیر فعال کردن سرویس‌های پنل
            Stimulsoft.Report.Design.Panels.StiPropertiesPanelService propPanel = Stimulsoft.Report.Design.Panels.StiPropertiesPanelService.GetService();
            propPanel.ServiceEnabled = false;

            Stimulsoft.Report.Design.Panels.StiDictionaryPanelService dictPanel = Stimulsoft.Report.Design.Panels.StiDictionaryPanelService.GetService();
            dictPanel.ServiceEnabled = true;
            
            Stimulsoft.Report.Design.Panels.StiReportTreePanelService treePanel = Stimulsoft.Report.Design.Panels.StiReportTreePanelService.GetService();
            treePanel.ServiceEnabled = false;

            Stimulsoft.Report.Design.Toolbars.StiToolsToolbarService cpanel = Stimulsoft.Report.Design.Toolbars.StiToolsToolbarService.GetService();
            cpanel.ServiceEnabled = false;
            StiOptions.Dictionary.BusinessObjects.AddBusinessObjectAssemblyToReferencedAssembliesAutomatically = false;
            StiOptions.Dictionary.BusinessObjects.AllowProcessNullItemsInEnumerables = false;
            StiOptions.Dictionary.BusinessObjects.AllowUseDataColumn = false;
            StiOptions.Dictionary.BusinessObjects.AllowUseFields = false;
            StiOptions.Dictionary.BusinessObjects.AllowUseProperties = false;
            StiOptions.Dictionary.BusinessObjects.CheckTableDuplication = false;
           
            

            StiOptions.Dictionary.ShowOnlyAliasForDataSource = true;
            StiOptions.Dictionary.ShowOnlyAliasForDataColumn = true;
            StiOptions.Dictionary.ShowOnlyAliasForTotal = true;
            dictPanel.ShowNewButton = false;
            dictPanel.ShowActionsButton = false;
            dictPanel.ShowBusinessObjectNewMenuItem = false;
            dictPanel.ShowCalcColumnNewMenuItem = false;
            dictPanel.ShowCategoryNewMenuItem = false;
            dictPanel.ShowCollapseAllMenuItem = true;
            dictPanel.ShowColumnNewMenuItem = false;
            dictPanel.ShowConnectionNewMenuItem = false;
            dictPanel.ShowContextMenu = false;
            dictPanel.ShowCreateFieldOnDoubleClick = false;
            dictPanel.ShowCreateLabel = false;
            dictPanel.ShowDataParameterNewMenuItem = false;
            dictPanel.ShowDataSourceNewMenuItem = false;
            dictPanel.ShowDataSourcesNewMenuItem = false;
            dictPanel.ShowDeleteButton = false;
            dictPanel.ShowDeleteForBusinessObject = false;
            dictPanel.ShowDeleteForDataColumn = false;
            dictPanel.ShowDeleteForDataConnection = false;
            dictPanel.ShowDeleteForDataParameter = false;
            dictPanel.ShowDeleteForDataRelation = false;
            dictPanel.ShowDeleteForDataSource = false;
            dictPanel.ShowDeleteForVariable = false;
            dictPanel.ShowDeleteMenuItem = false;
            dictPanel.ShowDictMergeMenuItem = false;
            dictPanel.ShowDictNewMenuItem = false;
            dictPanel.ShowDictOpenMenuItem = false;
            dictPanel.ShowDictSaveMenuItem = false;
            dictPanel.ShowDictXmlExportMenuItem = false;
            dictPanel.ShowDictXmlImportMenuItem = false;
            dictPanel.ShowDictXmlMergeMenuItem = false;
            dictPanel.ShowDownButton = false;
            dictPanel.ShowEditButton = false;
            dictPanel.ShowEditForBusinessObject = false;
            dictPanel.ShowEditForDataColumn = false;
            dictPanel.ShowEditForDataConnection = false;
            dictPanel.ShowEditForDataParameter = false;
            dictPanel.ShowEditForDataRelation = false;
            dictPanel.ShowEditForDataSource = false;
            dictPanel.ShowEditForVariable = false;
            dictPanel.ShowEditMenuItem = false;
            
            dictPanel.ShowExpandAllMenuItem = true;
            dictPanel.ShowMarkUsedMenuItem = false;
            dictPanel.ShowNewButton = false;
            dictPanel.ShowPropertiesForBusinessObject = false;
            dictPanel.ShowPropertiesForDataColumn = false;
            dictPanel.ShowPropertiesForDataConnection = false;
            dictPanel.ShowPropertiesForDataParameter = false;
            dictPanel.ShowPropertiesForDataRelation = false;
            dictPanel.ShowPropertiesForDataSource = false;
            dictPanel.ShowPropertiesForVariable = false;
            dictPanel.ShowPropertiesMenuItem = false;
            dictPanel.ShowRelationNewMenuItem = false;
            dictPanel.ShowRelationsImportMenuItem = false;
            dictPanel.ShowRemoveUnusedMenuItem = false;
            dictPanel.ShowSortItemsButton = false;
            dictPanel.ShowSynchronizeMenuItem = false;
            dictPanel.ShowUpButton = false;
            dictPanel.ShowUseAliases = true;
            dictPanel.ShowVariableNewMenuItem = false;
            dictPanel.ShowViewDataMenuItem = false;
یکی از قابلیت‌های خوب و کاربردی این کامپوننت پشتیبانی کامل حتی منوها از زبان فارسی می‌باشد که استفاده از این کامپوننت را در نرم افزار‌های تجاری در کشور قابل انعطاف پذیر‌تر می‌کند. برای فارسی سازی به یک فایل XML که در مسیر نصب کامپوننت قرار دارد، نیاز است.
نام فایل fa.xml می‌باشد. آن‌را در مسیر نرم افزار قرار دهید و سپس کد زیر را اضافه نمایید:
//تنظیم زبان به فارسی
StiConfig.LoadLocalization("fa.xml");
مطالب دوره‌ها
بررسی کارآیی و ایندکس گذاری بر روی اسناد XML در SQL Server - قسمت دوم
تا اینجا ملاحظه کردید که XQuery ایندکس نشده چگونه بر روی Query Plan تاثیر دارد. در ادامه، مباحث ایندکس گذاری بر روی اسناد XML ایی را مرور خواهیم کرد.


ایندکس‌های XML ایی

ایندکس‌های XML ایی، ایندکس‌های خاصی هستند که بر روی ستون‌هایی از نوع XML تعریف می‌شوند. هدف از تعریف آن‌ها، بهینه سازی اعمال مبتنی بر XQuery، بر روی داده‌های این نوع ستون‌ها است. چهار نوع XML Index قابل تعریف هستند؛ اما primary xml index باید ابتدا ایجاد شود. در این حالت جدولی که دارای ستون XML ایی است نیز باید دارای یک clustered index باشد. هدف از primary XML indexها، ارائه‌ی تخمین‌های بهتری است به بهینه ساز کوئری‌ها در SQL Server.


جزئیات primary XML indexها

زمانیکه یک primary xml index را ایجاد می‌کنیم، node table یاد شده در قسمت قبل را، بر روی سخت دیسک ذخیره خواهیم کرد (بجای هربار محاسبه در زمان اجرا). متادیتای این اطلاعات ذخیره شده را در جداول سیستمی sys.indexes و sys.columns می‌توان مشاهده کرد. باید دقت داشت که تهیه‌ی این ایندکس‌ها، فضای قابل توجهی را از سخت دیسک به خود اختصاص خواهند داد؛ چیزی حدود 2 تا 5 برابر حجم اطلاعات اولیه. بدیهی است تهیه‌ی این ایندکس‌ها که نتیجه‌ی تجزیه‌ی اطلاعات XML ایی است، بر روی سرعت insert تاثیر خواهند گذاشت. Node table دارای ستون‌هایی مانند نام تگ، آدرس تگ، نوع داده آن، مسیر و امثال آن است.
زمانیکه یک Primary XML Index تعریف می‌شود، اگر به Query Plan حاصل دقت کنید، دیگر خبری از XML Readerها مانند قبل نخواهد بود. در اینجا Clustered index seek قابل مشاهده‌است.


ایجاد primary XML indexها

همان مثال قسمت قبل را که دو جدول از آن به نام‌های xmlInvoice و xmlInvoice2 ایجاد کردیم، درنظر بگیرید. اینبار یک xmlInvoice3 را با همان ساختار و همان 6 رکوردی که معرفی شدند، ایجاد می‌کنیم. بنابراین برای آزمایش جاری، در مثال قبل، هرجایی xmlInvoice مشاهده می‌کنید، آن‌را به xmlInvoice3 تغییر داده و مجددا جدول مربوطه و داده‌های آن‌را ایجاد کنید.
اکنون برای ایجاد primary XML index بر روی ستون invoice آن می‌توان نوشت:
 CREATE PRIMARY XML INDEX invoice_idx ON xmlInvoice3(invoice)
 SELECT * FROM sys.internal_tables
کوئری دومی که بر روی sys.internal_tables انجام شده، محل ذخیره سازی این ایندکس را نمایش می‌دهد که دارای نامی مانند xml_index_nodes_325576198_256000 خواهد بود. دو عدد پس از آن table object id و column object id هستند.
در ادامه علاقمند هستیم که بدانیم داخل آن چه چیزی ذخیره شده‌است:
 SELECT * FROM sys.xml_index_nodes_325576198_256000
اگر این کوئری را اجرا کنید احتمالا به خطای Invalid object name برخواهید خورد. علت اینجا است که برای مشاهده‌ی اطلاعات جداول داخلی مانند این، نیاز است حین اتصال به SQL Server، در قسمت server name نوشت admin:(local) و حالت authentication نیز باید بر روی Windows authentication باشد. به آن اصطلاحا Dedicated administrator connection نیز می‌گویند. برای این منظور حتما نیاز است از طریق منوی File -> New -> Database Engine Query شروع کنید در غیراینصورت پیام Dedicated administrator connections are not supported را دریافت خواهید کرد.
اگر به این جدول دقت کنید، 6 ردیف اطلاعات XML ایی، به حدود 100 ردیف اطلاعات ایندکس شده، تبدیل گردیده‌است. با استفاده از دستور ذیل می‌توان حجم ایندکس تهیه شده را نیز مشاهده کرد:
 sp_spaceused 'xmlInvoice3'
در صورت نیاز برای حذف ایندکس ایجاد شده می‌توان به نحو ذیل عمل کرد:
 --DROP INDEX invoice_idx ON xmlInvoice3


تاثیر primary XML indexها بر روی سرعت اجرای کوئری‌ها

همان 10 کوئری قسمت قبل را درنظر بگیرید. اینبار برای مقایسه می‌توان به نحو ذیل عمل کرد:
 SELECT * FROM xmlInvoice
WHERE invoice.exist('/Invoice[@InvoiceId = "1003"]') = 1

SELECT * FROM xmlInvoice3
WHERE invoice.exist('/Invoice[@InvoiceId = "1003"]') = 1
دو کوئری یکی هستند اما اولی بر روی xmlInvoice اجرا می‌شود و دومی بر روی xmlInvoice3. هر دو کوئری را انتخاب کرده و با استفاده از منوی Query، گزینه‌ی Include actual execution plan را نیز انتخاب کنید (یا فشردن دکمه‌های Ctrl+M) تا پس از اجرای کوئری، بتوان Query Plan نهایی را نیز مشاهده نمود.


چند نکته در این تصویر حائز اهمیت است:
- Query plan کوئری انجام شده بر روی جدول دارای primary XML index، مانند قسمت قبل، حاوی XML Readerها نیست.
- هزینه‌ی انجام کوئری بر روی جدول دارای XML ایندکس نسبت به حالت بدون ایندکس، تقریبا نزدیک به صفر است. (بهبود کارآیی فوق العاده)
اگر کوئری‌های دیگر را نیز با هم مقایسه کنید، تقریبا به نتیجه‌ی کمتر از یک سوم تا یک چهارم حالت بدون ایندکس خواهید رسید.
همچنین اگر برای حالت دارای Schema collection نیز ایندکس ایجاد کنید، اینبار کوئری پلن آن اندکی (چند درصد) بهبود خواهد یافت ولی نه آنچنان.


ایندکس‌های XML‌ایی ثانویه یا secondary XML indexes

سه نوع ایندکس XML ایی ثانویه نیز قابل تعریف هستند:
- VALUE : کار آن بهینه سازی کوئری‌های content و wildcard است.
- PATH : بهینه سازی انتخاب‌های مبتنی بر XPath را انجام می‌دهد.
- Property: برای بهینه سازی انتخاب خواص و ویژگی‌ها بکار می‌رود.

این ایندکس‌ها یک سری non-clustered indexes بر روی node tables هستند. برای ایجاد سه نوع ایندکس یاد شده به نحو ذیل می‌توان عمل کرد:
 CREATE XML INDEX invoice_path_idx ON xmlInvoice3(invoice)
 USING XML INDEX invoice_idx FOR PATH
در اینجا یک path index جدید ایجاد شده‌است. ایندکس‌های ثانویه نیاز به ذکر ایندکس اولیه نیز دارند.
پس از ایجاد ایندکس ثانویه بر روی مسیرها، اگر اینبار کوئری دوم را اجرا کنیم، به Query Plan ذیل خواهیم رسید:


همانطور که مشاهده می‌کنید، نسبت به حالت primary index، وضعیت clustered index seek به index seek تغییر کرده‌است و همچنین دقیقا مشخص است که از کدام ایندکس استفاده شده‌است.
در ادامه دو نوع ایندکس دیگر را نیز ایجاد می‌کنیم:
 CREATE XML INDEX invoice_value_idx ON xmlInvoice3(invoice)
 USING XML INDEX invoice_idx FOR VALUE
 
 CREATE XML INDEX invoice_prop_idx ON xmlInvoice3(invoice)
 USING XML INDEX invoice_idx FOR PROPERTY
 

سؤال: اکنون پس از تعریف 4 ایندکس یاد شده، کوئری دوم از کدام ایندکس استفاده خواهد کرد؟

در اینجا مجددا کوئری دوم را اجرا کرده و به قسمت Query Plan آن دقت خواهیم کرد:


برای مشاهده دقیق نام ایندکس مورد استفاده، کرسر ماوس را بر روی index seek قرار می‌دهیم. در اینجا اگر به قسمت object گزارش ارائه شده دقت کنیم، نام invoice_value_idx یا همان value index ایجاد شده، قابل مشاهده‌است؛ به این معنا که در کوئری دوم، اهمیت مقادیر بیشتر است از اهمیت مسیرها.

کوئری‌هایی مانند کوئری ذیل از property index استفاده می‌کنند:
 SELECT * FROM xmlInvoice3
WHERE invoice.exist('/Invoice//CustomerName[text() = "Vahid"]') = 1
در اینجا با بکارگیری // به دنبال CustomerName در تمام قسمت‌های سند Invoice خواهیم گشت. البته کوئری پلن آن نسبتا پیچیده‌است و شامل primary index اسکن و clusterd index اسکن نیز می‌شود. برای بهبود قابل ملاحظه‌ی آن می‌توان به نحو ذیل از عملگر self استفاده کرد:
 SELECT * FROM xmlInvoice3
WHERE invoice.exist('/Invoice//CustomerName[. = "Vahid"]') = 1


خلاصه نکات بهبود کارآیی برنامه‌های مبتنی بر فیلدهای XML

- در حین استفاده از XPath، ذکر  محور parent یا استفاده از .. (دو دات)، سبب ایجاد مراحل اضافه‌ای در Query Plan می‌شوند. تا حد امکان از آن اجتناب کنید و یا از روش‌هایی مانند cross apply و xml.nodes برای مدیریت اینگونه موارد تو در تو استفاده نمائید.
- ordinals را به انتهای Path منتقل کنید (مانند ذکر [1] جهت مشخص سازی نودی خاص).
- از ذکر predicates در وسط یک Path اجتناب کنید.
- اگر اسناد شما fragment با چند root elements نیستند، بهتر است document بودن آ‌ن‌ها را در حین ایجاد ستون XML مشخص کنید.
- xml.value را به xml.query ترجیح دهید.
- عملیات casting در XQuery سنگین بوده و استفاده از ایندکس‌ها را غیرممکن می‌کند. در اینجا استفاده از اسکیما می‌تواند مفید باشد.
- نوشتن sub queryها بهتر هستند از چندین XQuery در یک عبارت SQL.
- در ترکیب اطلاعات رابطه‌ای و XML، استفاده از متدهای xml.exist و sql:column نسبت به xml.value جهت استخراج و مقایسه اطلاعات، بهتر هستند.
- اگر قصد تهیه خروجی XML از جدولی رابطه‌ای را دارید، روش select for xml کارآیی بهتری را نسبت به روش FLOWR دارد. روش FLOWR برای کار با اسناد XML موجود طراحی و بهینه شده‌است؛ اما روش select for xml در اصل برای کار با اطلاعات رابطه‌ای بهینه سازی گردیده‌است.