نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 22 - توزیع برنامه توسط IIS
یک نکته‌ی تکمیلی: ساده‌تر شدن به روز رسانی برنامه‌ها در ASP.NET Core 6x
تا پیش از دات نت 6، فایل‌های باینری برنامه‌های ASP.NET Core توسط IIS قفل می‌شوند و امکان جایگزینی آن‌ها بدون متوقف کردن برنامه، میسر نیست. برای رفع این مشکل، پشتیبانی از shadow copy به ASP.NET Core Module for IIS جدید، اضافه شده که روش فعالسازی آن با انجام تغییراتی در Web.Config به صورت زیر است:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- To customize the asp.net core module uncomment and edit the following section. 
  For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->

  <system.webServer>
    <handlers>
      <remove name="aspNetCore"/>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModulev2" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
      <handlerSettings>
        <handlerSetting name="experimentalEnableShadowCopy" value="true" />
        <handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
        <!-- Only enable handler logging if you encounter issues-->
        <!--<handlerSetting name="debugFile" value=".\logs\aspnetcore-debug.log" />-->
        <!--<handlerSetting name="debugLevel" value="FILE,TRACE" />-->
      </handlerSettings>
    </aspNetCore>
  </system.webServer>
</configuration>
مطالب
چک لیست نصب SQL Server

عموما هنگام نصب SQL Server ، پیش و پس از آن، بهتر است موارد زیر جهت بالا بردن کیفیت و کارآیی سرور، رعایت شوند:

1- پیش فرض‌های نصب SQL Server در مورد محل قرارگیری فایل‌های دیتا و لاگ و غیره صحیح نیست. هر کدام باید در یک درایو مجزا مسیر دهی شوند برای مثال:
Data drive D:
Transaction Log drive E:
TempDB drive F:
Backup drive G:
این مورد TempDB را کسانی که با SharePoint کار کرده باشند به خوبی علتش را درک خواهند کرد. پیش فرض نصب افراد تازه کار، نصب SQL Server و تمام مخلفات آن در همان درایو ویندوز است (یعنی همان چندبار کلیک بر روی دکمه‌ی Next برای نصب). SharePoint هم به نحو مطلوبی تمام کارهایش مبتنی بر transactions است. یعنی استفاده‌ی کامل از TempDB . نتیجه؟ پس از مراجعه به درایو ویندوز مشاهده خواهید کرد که فقط چند مگ فضای خالی باقی مانده! حالا اینجا است که بدو این مقاله و اون مقاله رو بخون که چطور TempDB را باید از درایو C به جای دیگری منتقل کرد. چیزی که همان زمان نصب اولیه SQL Server باید در مورد آن فکر می‌شد و نه الان که سیستم از کار افتاده.
همچنین وجود این مسیرهای مشخص و پیش فرض و آگاهی از سطوح دسترسی مورد نیاز آن‌ها، از سر دردهای بعدی جلوگیری خواهد کرد. برای مثال : انتقال فایل‌های دیتابیس اس کیوال سرور 2008

2- پس از رعایت مورد 1 ، نوبت به تنظیمات آنتی ویروس نصب شده روی سرور است. این پوشه‌های ویژه را که جهت فایل‌های دیتا و لاگ و غیره بر روی درایوهای مختلف معرفی کرده‌اید یا خواهید نمود، باید از تنظیمات آنتی ویروس شما Exclude شوند. همچنین در حالت کلی فایل‌هایی با پسوندهای LDF/MDF/NDF باید جزو فایل‌های صرفنظر شونده از دید آنتی ویروس شما معرفی گردند.
این مورد علاوه بر بالا بردن کارآیی SQL Server ، در حین Boot سیستم نیز تاثیر گذار است. گاها دیده شده است که آنتی ویروس‌ها این فایل‌های حجیم را در حین راه اندازی اولیه سیستم، پیش از SQL Server ، جهت بررسی گشوده و به علت حجم بالای آن‌ها این قفل‌ها تا مدتی رها نخواهند شد. در نتیجه آغاز سرویس SQL Server را با مشکلات جدی مواجه خواهند کرد که عموما عیب یابی آن کار ساده‌ای نیست.

3- پیش فرض میزان حافظه‌ی مصرفی SQL Server صحیح نیست. این مورد باید دقیقا بلافاصله پس از پایان عملیات نصب اولیه اصلاح شود. برای مطالعه بیشتر: تنظیمات پیشنهادی حداکثر حافظه‌ی مصرفی اس کیوال سرور

4- آیا مطمئن هستید که از تمام امکانات نگارش جدید SQL Server ایی که نصب کرده‌اید در حال استفاده می‌باشید؟
برای مطالعه بیشتر: تنظیم درجه سازگاری یک دیتابیس اس کیوال سرور

5- بهتر است فشرده سازی خودکار بک آپ‌ها در SQL Server 2008 فعال شوند.
برای مطالعه بیشتر: +

6- از paging بیش از حد اطلاعات، از حافظه‌ی فیزیکی سرور به virtual memory و انتقال آن به سخت دیسک سیستم جلوگیری کنید. برای این منظور:
در قسمت Run ویندوز تاپیک کنید : GPEDIT.MSC و پس از اجرای آن با مراجعه به Group policy editor ظاهر شده به مسیر زیر مراجعه کنید:
windows settings -> security settings -> local policies -> user rights assignment -> lock pages in memory
در اینجا به یوزر اکانت سرویس SQL Server دسترسی lock pages in memory را بدهید.
علاوه بر آن در همین قسمت (user rights assignment) گزینه‌ی "Perform Volume Maintenance tasks" را نیز یافته و دسترسی لازم را به یوزر اکانت سرویس SQL Server بدهید.

7- به روز رسانی اطلاعات آماری SQL Server را به حالت غیرهمزمان تنظیم کنید.
اگر مطالب مرتبط با SQL Server این سایت را مرور کرده باشید حتما با یک سری DMV که دقیقا به شما خواهند گفت بر اساس اطلاعات آماری جمع شده برای مثال بهتر است روی چه فیلدهایی Index درست کنید، آشنا شده‌اید. حالت پیش فرض به روز رسانی این اطلاعات آماری، synchronous است یا همزمان. به این معنا که تا اطلاعات آماری یک کوئری ذخیره نشود، حاصل کوئری به کاربر بازگشت داده نخواهد شد که این امر می‌تواند بر روی کارآیی سیستم تاثیر گذار باشد. اما امکان تنظیم آن به حالت غیر همزمان نیز مطابق کوئری‌های زیر وجود دارد (این مورد از SQL Server 2005 به بعد اضافه شده است):

ALTER DATABASE dbName SET AUTO_UPDATE_STATISTICS ON
ALTER DATABASE dbName SET AUTO_UPDATE_STATISTICS_ASYNC ON

8- نصب آخرین سرویس پک موجود فراموش نشود. برای مثال این سایت آمار تمام به روز رسانی‌ها را نگهداری می‌کند.

9- حتما رویه‌ای را برای تهیه بک آپ‌های خودکار پیش بینی کنید. برای مثال : +

10- میزان فضای خالی باقیمانده درایوهای سرور را مونیتور کنید. اطلاعات بیشتر: +

11- با نصب سرور جدید و تنظیم collation آن به فارسی، به نکات "یافتن تداخلات Collations در SQL Server" دقت داشته باشید.

مسیرراه‌ها
Entity framework code-first
شروع به کار با EF Code first

برای تکمیل بحث نیاز است تغییرات انجام شده از نگارش 4 به 6 را نیز مد نظر داشته باشید:


آشنایی با مباحث Migrations



آشنایی با تنظیمات نگاشت‌ها به دو روش استفاده از ویژگی‌ها و Fluent API



اعتبارسنجی و بررسی استثناءها



ردیابی تغییرات



استفاده از SQL خام و بانک‌های اطلاعاتی متفاوت

      نکات مهم کوئری نویسی در EF



      استفاده از EF در WPF


      لایه بندی پروژه‌های EF Code first



      پروژ‌ه‌های انجام شده با EF Code first

       
      مطالب
      اجرای SSIS Package از طریق برنامه کاربردی

      مقدمه

      در اکثر موارد در یک Landscape عملیاتی، چنانچه به تجمیع و انتقال داده‌ها از بانک‌های اطلاعاتی مختلف نیاز باشد، از SSIS Package اختصار (SQL Server Integration Service) استفاده می‌شود و معمولاً با تعریف یک Job در سطح SQL Server به اجرای Package در زمانهای مشخص می‌پردازند. چنانچه در موقعیتی لازم باشد که از طریق برنامه کاربردی توسعه یافته، به اجرای Package مبادرت ورزیده شود و البته نخواهیم Job تعریف شده را از طریق کد برنامه، اجرا کنیم و در واقع این امکان را داشته باشیم که همانند یک رویه ذخیره شده تعریف شده در سطح بانک اطلاعاتی به اجرای عمل فوق بپردازیم، یک راه حل می‌تواند تعریف یک CLR Stored Procedures باشد. در این مقاله به بررسی این موضوع پرداخته می‌شود، در ابتدا لازم است به بیان تئوری موضوع پرداخته شود (قسمت‌های 1 الی 5) در ادامه به ذکر پیاده سازی روش پیشنهادی پرداخته می‌شود.

      1- اجرای Integration Service Package 

      جهت اجرای یک Package از ابزارهای زیر می‌توان استفاده کرد:
      • command-line ابزار خط فرمان dtexec.exe
      • ابزار اجرائی پکیج dtexecui.exe
      • استفاده از SQL Server Agent job
      توجه: همچنین یک Package را در زمان طراحی در  Business Intelligence Development Studio) BIDS)  می‌توان اجرا نمود.

      2- استفاده از dtexec جهت اجرای Package

      با استفاده از ابزار dtexec می‌توان Package‌های ذخیره شده در فایل سیستم، یک SQL Instance و یا Package‌های ذخیره شده در Integration Service را اجرا نمود.

      توجه:
      در سیستم عامل‌های 64 بیتی، ابزار dtexec موجود در Integration Service با نسخه 64 بیتی نصب می‌شود. چنانچه بایست Package‌های معینی را در حالت 32 بیتی اجرا کنید، لازم است ابزار dtexec نسخه 32 بیتی نصب شود. ابزار dtexec دستیابی به تمامی ویژگی‌های پیکربندی و اجرای Package از قبیل اتصالات، مشخصات(Properties)، متغیرها، logging و شاخص‌های پردازشی را فراهم می‌کند.
      توجه: زمانی که از نسخه‌ی ابزار dtexec که با SQL Server 2008 ارائه شده استفاده می‌کنید برای اجرای یک SSIS Package نسخه 2005، Integration Service به صورت موقت Package را به نسخه 2008 ارتقا می‌دهد، اما نمی‌توان از ابزار dtexec برای ذخیره این تغییرات استفاده کرد.

      2-1- ملاحظات نصب dtexec روی سیستم‌های 64 بیتی

      به صورت پیش فرض، یک سیستم عامل 64 بیتی که هر دو نسخه 64 بیتی و 32 بیتی ابزار خط فرمان Integration Service را دارد، نسخه 32 بیتی نصب شده را در خط فرمان اجرا خواهد کرد. نسخه 32 بیتی بدین دلیل اجرا می‌شود که در متغیر محیطی (Path (Path environment variable مسیر directory نسخه 32 بیتی قرار گرفته است.به طور معمول:
      (<drive>:\Program Files(x86)\Microsoft SQL Server\100\DTS\Binn)
      توجه: اگر از SQL Server Agent برای اجرای Package استفاده می‌کنید، SQL Server Agent به طور خودکار از ابزار نسخه 64 بیتی استفاده می‌کند. SQL Server Agent از Registry و نه از متغیر محیطی Path استفاده می‌کند. برای اطمینان از اینکه نسخه 64 بیتی این ابزار را در خط فرمان اجرا می‌کنید، directory را به directory ای تغییر دهید که شامل نسخه 64 بیتی این ابزار است(<drive>:\Program Files\Microsoft SQL Server\100\DTS\Binn) و ابزار را از این مسیر اجرا کنید و یا برای همیشه مسیر قرار گرفته در متغیر محیطی path را با مسیری که نسخه 64 بیتی قرار دارد، جایگزین کنید.

      2-2- تفسیر کدهای خروجی

      هنگامی که یک Package اجرا می‌شود، dtexec یک کد خروجی (Return Code) بر می‌گرداند:

       مقدار توصیف
       0  Package با موفقیت اجرا شده است.
       1  Package با خطا مواجه شده است.
       3 Package در حال اجرا توسط کاربر لغو شده است.
       4  Package پیدا نشده است.
       5  Package بارگذاری نشده است.
       6  ابزار با یک خطای نحوی یا خطای معنایی در خط فرمان برخورد کرده است.

      2-3- قوانین نحوی dtexec

      تمامی گزینه‌ها (Options) باید با یک علامت Slash (/)  و یا Minus (-)  شروع شوند.
      یک آرگومان باید در یک quotation mark محصور شود چنانچه شامل یک فاصله خالی باشد.
      گزینه‌ها و آرگومان‌ها بجز رمزعبور حساس به حروف کوچک و بزرگ نیستند.

      2-3-1- Syntax

       dtexec /option [value] [/option [value]]…


      2-3-2- Parameters

      نکته: در Integration Service، ابزار خط فرمان dtsrun که برایData Transformation Service) DTS)‌های نسخه SQL Server 2000 استفاده می‌شد، با ابزار خط فرمان dtexec جایگزین شده است.
      • تعدادی از گزینه‌های خط فرمان dtsrun به طور مستقیم در dtexec معادل دارند برای مثال نام Server و نام Package.
      • تعدادی از گزینه‌های dtsrun به طور مستقیم در dtexec معادل ندارند.
      • تعدادی گزینه‌های خط فرمان جدید dtsexec وجود دارد که در ویژگی‌های جدید Integration Service پشتیبانی می‌شود.

      2-3-3- مثال

      1) به منظور اجرای یک SSIS Package که در SQL Server ذخیره شده است، با استفاده از Windows Authentication :
       dtexec /sq <Package Name> /ser <Server Name>

      2) به منظور اجرای یک SSIS Package که در پوشه File System در SSIS Package Store ذخیره شده است :
       dtexec /dts “\File System\<Package File Name>”

      3) به منظور اجرای یک SSIS Package که در سیستم فایل ذخیره شده است و مشخص کردن گزینه logging:
       dtexec /f “c:\<Package File Name>” /l “DTS.LogProviderTextFile; <Log File Name>”

      4) به منظور اجرای یک SSIS Package که در SQL Server ذخیره شده با استفاده از SQL Server Authentication برای نمونه(user:ssis;pwd:ssis@ssis)و رمز (Package(123:
       dtexec  /server “<Server Name>”  /sql “<Package Name>”  / user “ssis” /Password “ssis@ssis” /De “123”


      3- تنظیمات سطح حفاظتی یک Package

      به منظور حفاظت از داده‌ها در Package‌های Integration Service می‌توانید یک سطح حفاظتی (protection level) را تنظیم کنید که به حفاظت از داده‌های صرفاً حساس یا تمامی داده‌های یک Package کمک نماید. به  علاوه می‌توانید این داده‌ها را با یک Password یا یک User Key رمزگذاری نمائید یا به رمزگذاری داده‌ها در بانک اطلاعاتی اعتماد کنید. همچنین سطح حفاظتی که برای یک Package استفاده می‌کنید، الزاماً ایستا (static) نیست و در طول چرخه حیات یک Package می‌تواند تغییر کند. اغلب سطح حفاظتی در طول توسعه یا به محض (deploy) استقرار Package تنظیم می‌شود.
      توجه: علاوه بر سطوح حفاظتی که توصیف شد، Package‌ها در بانک اطلاعاتی msdb ذخیره می‌شوند که همچنین می‌توانند توسط نقش‌های ثابت در سطح بانک اطلاعاتی (fixed database-level roles) حفاظت شوند. Integration Service شامل 3 نقش ثابت بانک اطلاعاتی برای نسبت دادن مجوزها به Package است که عبارتند از db_ssisadmin  ،db_ssisltduser و db_ssisoperator

      3-1- درک سطوح حفاظتی

      در یک Package اطلاعات زیر به عنوان حساس تعریف می‌شوند:
      • بخش password در یک connection string. گرچه، اگر گزینه ای را که همه چیز را رمزگذاری کند، انتخاب کنید تمامی connection string حساس در نظر گرفته می‌شود.
      • گره‌های task-generated XML که برچسب (tagged) هایی حساس هستند.
      • هر متغییری که به عنوان حساس نشان گذاری شود.

      3-1-1- Do not save sensitive

      هنگامی که Package ذخیره می‌شود از ذخیره مقادیر ویژگی‌های حساس در Package جلوگیری می‌کند. این سطح حفاظتی رمزگذاری نمی‌کند اما در عوض از ذخیره شدن ویژگی هایی که حساس نشان گذاری شده اند به همراه Package جلوگیری می‌کند.

      3-1-2- Encrypt all with password

      به منظور رمزگذاری تمامی Package از یک Password استفاده می‌شود. Package توسط Password ای رمزگذاری می‌شود  که کاربر هنگامی که Package را ایجاد یا Export می‌کند، ارائه می‌دهد. به منظور باز کردن Package در SSIS Designer یا اجرای Package توسط ابزار خط فرمان dtexec کاربر بایست رمز Package را ارائه نماید. بدون رمز کاربر قادر به دستیابی و اجرای Package نیست.

      3-1-3- Encrypt all with user key

      به منظور رمزگذاری تمامی Package از یک کلید که مبتنی بر Profile کاربر جاری می‌باشد، استفاده می‌شود. تنها کاربری که Package را ایجاد یا Export می‌کند، می‌تواند Package را در SSIS Designer باز کند و یا Package را توسط ابزار خط فرمان dtexec اجرا کند.

      3-1-4- Encrypt sensitive with password

      به منظور رمزگذاری تنها مقادیر ویژگی‌های حساس در Package از یک Password استفاده می‌شود. برای رمزگذاری از DPAPI استفاده می‌شود. داده‌های حساس به عنوان بخشی از Package ذخیره می‌شوند اما آن داده‌ها با استفاده از Password رمزگذاری می‌شوند. به منظور باز نمودن Package در SSIS Designer کاربر باید رمز Package را ارائه دهد. اگر رمز ارائه نشود، Package بدون داده‌های حساس باز می‌شود و کاربر باید مقادیر جدیدی برای داده‌های حساس فراهم کند. اگر کاربر سعی نماید Package را بدون ارائه رمز اجرا کند، اجرای Package با خطا مواجه می‌شود.

      3-1-5- Encrypt sensitive with user key

      به منظور رمزگذاری تنها مقادیر ویژگی‌های حساس در Package از یک کلید که مبتنی بر Profile کاربر جاری می‌باشد، استفاده می‌شود. تنها کاربری که از همان Profile استفاده می‌کند، Package را می‌تواند بارگذاری (load) کند. اگر کاربر متفاوتی Package را باز نماید، اطلاعات حساس با مقادیر پوچی جایگزین می‌شود و کاربر باید مقادیر جدیدی برای داده‌های حساس فراهم کند. اگر کاربر سعی نماید Package را بدون ارائه رمز اجرا کند، اجرای Package با خطا مواجه می‌شود. برای رمزگذاری از DPAPI استفاده می‌شود.

      3-1-6- (Rely on server storage for encryption (ServerStorage

      با استفاده از نقش‌های بانک اطلاعاتی، SQL Server تمامی Package را حفاظت می‌کند. این گزینه تنها زمانی پشتیبانی می‌شود که Package در بانک اطلاعاتی msdb ذخیره شده است.

      4- استفاده از نقش‌های Integration Service

      برای کنترل کردن دستیابی به Package، SSIS شامل 3 نقش ثابت در سطح بانک اطلاعاتی است. نقش‌ها می‌توانند تنها روی Package هایی که در بانک اطلاعاتی msdb ذخیره شده اند، بکار روند. با استفاده از SSMS می‌توانید نقش‌ها را به Package‌ها نسبت دهید، این انتساب نقش‌ها در بانک اطلاعاتی msdb ذخیره می‌شود.

       Write action  Read action Role
       Import packages
      Delete own packages
      Delete all packages
      Change own package roles
      Change all package roles

      * به نکته رجوع شود

       Enumerate own packages
      Enumerate all packages
      View own packages
      View all packages
      Execute own packages
      Execute all packages
      Export own packages
      Export all packages
      Execute all packages in SQL Server Agent
       db_ssisadmin
      or
      sysadmin

      Import packages
      Delete own packages
      Change own package roles

      Enumerate own packages
      Enumerate all packages
      View own packages
      Execute own packages
      Export own packages
      db_ssisltduser
      None
      Enumerate all packages
      View all packages
      Execute all packages
      Export all packages
      Execute all packages in SQL Server Agent
      db_ssisoperator
      Stop all currently running packages
      View execution details of all running packages
      Windows administrators
      * نکته: اعضای نقش‌های db_ssisadmin و dc_admin ممکن است قادر باشند مجوزهای خودشان را تا سطح sysadmin ارتقا دهند براساس این ترفیع مجوز امکان اصلاح و اجرای Package‌ها از طریق SQL Server Agent میسر می‌شود. برای محافظت در برابر این ارتقا، با استفاده از یک (account) حساب Proxy  با دسترسی محدود، Job هایی که این Package‌ها را اجرا می‌کنند، پیکربندی شوند یا  تنها اعضای نقش sysadmin به نقش‌های db_ssisadmin و dc_admin افزوده شوند.

      همچنین جدول sysssispackages در بانک اطلاعاتی msdb شامل Package هایی است که در SQL Server ذخیره می‌شوند. این جدول شامل ستون هایی که اطلاعاتی درباره نقش هایی که به Package‌ها نسبت داده شده است، می‌باشد.
      به صورت پیش فرض، مجوزهای نقش‌های ثابت بانک اطلاعاتی db_ssisadmin و db_ssisoperator و شناسه منحصر به فرد کاربری (unique security identifier) که Package را ایجاد کرده برای خواندن Package بکار می‌رود، و مجوزهای نقش db_ssisadmin و شناسه منحصر به فرد کاربری که Package را ایجاد کرده برای نوشتن Package به کار می‌رود. یک User باید عضو نقش db_ssisadmin و db_ssisltduser یا db_ssisoperator برای داشتن دسترسی خواندن Package باشد. یک User باید عضو نقش db_ssisadmin برای داشتن دسترسی نوشتن Package باشد.

      5- اتصال به صورت Remote به Integration Service

      زمانی که یک کاربر بدون داشتن دسترسی کافی تلاش کند به یک Integration Service به صورت Remote متصل شود، با پیغام خطای "Access is denied" مواجه می‌شود. برای اجتناب از این پیغام خطا می‌توان تضمین کرد که کاربر مجوز مورد نیاز DCOM را دارد.
      به منظور پیکربندی کردن دسترسی کاربر به صورت Remote به سرویس Integration  مراحل زیر را دنبال کنید:
      - Component Service را باز نمایید ( در Run عبارت dcomcnfg را تایپ کنید).
      - گره Component Service را باز کنید، گره Computer و سپس My Computer را باز نمایید و روی DCOM Config کلیک نمایید.
      - گره DCOM Config را باز کنید و از لیست برنامه هایی که می‌توانند پیکربندی شوند MsDtsServer را انتخاب کنید.
      - روی Properties برنامه MsDtsServer رفته و قسمت Security را انتخاب کنید.
      - در قسمت Lunch and Activation Permissions، مورد Customize را انتخاب و سپس روی Edit کلیک نمایید تا پنجره Lunch Permission باز شود.
      - در پنجره Lunch Permission، کاربران را اضافه و یا حذف کنید و مجوزهای مناسب را به کاربران یا گروه‌های مناسب نسبت دهید. مجوزهای موجود عبارتند از Local Lunch، Remote Lunch، Local Activation و Remote Activation .
      - در قسمت Access Permission مراحل فوق را به منظور نسبت دادن مجوزهای مناسب به کاربران یا گروه‌های مناسب انجام دهید.
      - سرویس Integration  را Restart کنید.
      مجوز دسترسی  Lunch به منظور شروع و خاتمه سرویس، اعطا  یا رد  می‌شود و مجوز دسترسیActivation به منظور متصل شدن به سرویس، اعطا (grant) یا رد (deny) می‌شود.

      6- پیاده سازی

      در ابتدا به ایجاد یک CLR Stored Procedures پرداخته می‌شود نام اسمبلی ساخته شده به این نام RunningPackage.dll می‌باشد و حاوی کد زیر است:
      Partial Public Class StoredProcedures
          '------------------------------------------------
          'exec dbo.Spc_NtDtexec 'Package','ssis','ssis@ssis','1234512345'
          '------------------------------------------------
          <Microsoft.SqlServer.Server.SqlProcedure()> _
          Public Shared Sub Spc_NtDtexec(ByVal PackageName As String, _
                                         ByVal UserName As String, _
                                         ByVal Password As String, _
                                         ByVal Decrypt As String)
              Dim p As New System.Diagnostics.Process()
              p.StartInfo.FileName = "C:\Program Files\Microsoft SQL Server\100\DTS\Binn\DTExec.exe"
              p.StartInfo.RedirectStandardOutput = True
              p.StartInfo.Arguments = "/sql " & PackageName & " /User " & UserName & " /Password " & Password & " /De " & Decrypt
              p.StartInfo.UseShellExecute = False
              p.Start()
              p.WaitForExit()
              Dim output As String
              output = p.StandardOutput.ReadToEnd()
              Microsoft.SqlServer.Server.SqlContext.Pipe.Send(output)
          End Sub
      End Class
      در حقیقت توسط این رویه به اجرای برنامه dtexec.exe و ارسال پارامترهای مورد نیاز جهت اجرا پرداخته می‌شود. با توجه به توضیحات تئوری بیان شده، سطح حفاظتی Package ایجاد شده Encrypt all with password توصیه می‌شود که رمز مذکور در قالب یکی از پارامتر ارسالی به رویه ساخته شده موسوم به Spc_NtDtexec ارسال می‌گردد.

      در قدم بعدی نیاز به Register کردن dll ساخته شده در سطح بانک اطلاعاتی SQL Server است، این گام‌ها پس از اتصال به SQL Server Management Studio به شرح زیر است:
      1- فعال کردن CLR در سرویس SQL Server
      SP_CONFIGURE 'clr enabled',1
      GO
      RECONFIGURE

      2- فعال کردن ویژگی TRUSTWORTHY در بانک اطلاعاتی مورد نظر
       ALTER DATABASE <Database Name> SET TRUSTWORTHY ON
      GO
      RECONFIGURE

      3- ایجاد Assembly و Stored Procedure در بانک اطلاعاتی مورد نظر
      Assembly ساخته شده با نام RunningPacakge.dll در ریشه :C کپی شود. بعد از ثبت نمودن این Assembly لزومی به وجود آن نمی‌باشد.
      USE <Database Name>
      GO
      CREATE ASSEMBLY [RunningPackage]
      AUTHORIZATION [dbo]
      FROM 'C:\RunningPackage.dll'
      WITH PERMISSION_SET = UNSAFE
      Go
      CREATE PROCEDURE [dbo].[Spc_NtDtexec]
      @PackageName [nvarchar](50),
      @UserName [nvarchar](50),
      @Password [nvarchar](50),
      @Decrypt [nvarchar](50)
      WITH EXECUTE AS CALLER
      AS
      EXTERNAL NAME [RunningPackage].[RunningPackage.StoredProcedures].[Spc_NtDtexec]
      GO
      توجه: Application User برنامه بایست دسترسی اجرای رویه ذخیره شده Spc_NtDtexec را در بانک اطلاعاتی مورد نظر داشته باشد همچنین بایست عضو نقش db_ssisoperator در بانک اطلاعاتی msdb باشد.( منظور از Application User، لاگین است که در Connection string برنامه قرار داده اید.)

      در برنامه کاربردی تان کافی است متدی به شکل زیر ایجاد و با توجه به نیازتان در برنامه به فراخوانی آن و اجرای Package بپردازید.
          Private Sub ExecutePackage()
              Dim oSqlConnection As SqlClient.SqlConnection
              Dim oSqlCommand As SqlClient.SqlCommand
              Dim strCnt As String = String.Empty
              strCnt = "Data Source=" & txtServer.Text & ";User ID=" & txtUsername.Text & ";Password=" & txtPassword.Text & ";Initial Catalog=" & cmbDatabaseName.SelectedValue.ToString() & ";"
              Try
                  oSqlConnection = New SqlClient.SqlConnection(strCnt)
                  oSqlCommand = New SqlClient.SqlCommand
                  With oSqlCommand
                      .Connection = oSqlConnection
                      .CommandType = System.Data.CommandType.StoredProcedure
                      .CommandText = "dbo.Spc_NtDtexec"
                      .Parameters.Clear()
                      .Parameters.Add("@PackageName", System.Data.SqlDbType.VarChar, 50)
                      .Parameters.Add("@UserName", System.Data.SqlDbType.VarChar, 50)
                      .Parameters.Add("@Password", System.Data.SqlDbType.VarChar, 50)
                      .Parameters.Add("@Decrypt", System.Data.SqlDbType.VarChar, 50)
                      .Parameters("@PackageName").Value = txtPackageName.Text.Trim()
                      .Parameters("@UserName").Value = txtUsername.Text.Trim()
                      .Parameters("@Password").Value = txtPassword.Text.Trim()
                      .Parameters("@Decrypt").Value = txtDecrypt.Text.Trim()
                  End With
                  If (oSqlCommand.Connection.State <> System.Data.ConnectionState.Open) Then
                      oSqlCommand.Connection.Open()
                      oSqlCommand.ExecuteNonQuery()
                      System.Windows.Forms.MessageBox.Show("Success")
                  End If
                  If (oSqlCommand.Connection.State = System.Data.ConnectionState.Open) Then
                      oSqlCommand.Connection.Close()
                  End If
              Catch ex As Exception
                  MessageBox.Show(ex.Message, "Error")
              End Try
          End Sub 'ExecutePackage
      نظرات مطالب
      Extension Methods
      لطفا این بحث رو ادامه ندید. در یک سری از دسترسی‌های نویسنده‌ها به زودی تجدید نظر خواهد شد. چه کسانی که ابراز تمایل کرده بودند و فقط وقت من رو جهت ثبت آن‌ها نصف روزی تلف کردند؛ چه کسانی که مطالب سطحی ارسال می‌کنند.
      با تشکر

      ضمن اینکه یک مطلب را هم مد نظر داشته باشید. اینجا هدف بیشتر ذکر یک سری نکته است به همین جهت اسم سایت tips دارد (نکات ریز).
      اسم سایت encyclopedia نیست که برای هر مطلبی قرار باشد کتاب نوشته شود. اگر نوشته شد، چقدر خوب؛ اگر نه ... یک نکته ریز جدید یاد گرفتید. این هم خوب.
      همچنین هدف از این سایت خواننده عام با سطح مطالعه و اطلاعات صفر نیست و نبوده. از روز اولش اینطور نبوده و نخواهد بود.

      یک مطالب رو هم فراموش نکنید. به قول مولوی: «گر تو بهتر میزنی بستان بزن»
      در طی یک ماهی که این سایت در حالت تعلیق بود ... من هر چقدر سایت‌های فارسی زبان فنی برنامه نویسی رو گشتم که دوتا مطلب به درد بخور پیدا کنم ... چیزی نیافتم. هیچی! واقعا دریغ از یک مطلب فنی به درد بخور که منتشر شده باشد. حالا همین عده منتظرند سریع بریزند سر یک نفر شروع کنند به داد و قال.
      باز تکرار می‌کنم: «گر تو بهتر میزنی بستان بزن»


      نظرات مطالب
      ایجاد فیلتر برای هدایت همه‌ی درخواست‌ها به صفحه‌ی «در حال به‌روزرسانی» در برنامه‌های ASP.NET MVC
      - روش قدیمی استفاده‌ی از فایل app_offline.htm با انواع و اقسام برنامه‌های هاست شده‌ی در IIS کار می‌کند (حتی با ASP.NET Core)؛ البته آنچنان قابلیت سفارشی سازی ندارد.
      - بررسی وجود یک فایل به ازای هر درخواست رسیده، بار IO سنگینی را در سایت ایجاد می‌کند. خود ASP.NET و تمام مشتقات آن از file watcher برای اطلاع از تغییرات رخ‌داده استفاده می‌کنند (یک مثال). حتی در ASP.NET Core هم از همین روش برای بررسی تغییرات فایل‌های config و reload اطلاعات مرتبط با آن‌ها استفاده می‌شود.
      - یک روش دیگر برای عدم بررسی هرباره‌ی وجود فایل، ایجاد دو اکشن متد GoOffline و GoOnline است. در اولی یک متغیر استاتیک (کش کردن اطلاعات) را true می‌کنید و در دومی false. سپس این متغیر (یا کش) در فیلتر شما خوانده می‌شود، بجای اینکه هربار بررسی وجود فایل انجام شود.
      مطالب
      بررسی قابلیت Endpoint Routing در ASP.NET Core
      نحوه کار مکانیزم مسیریابی به این صورت است که ما Route هایمان را مثلا درون میان‌افزار MVC (توسط متد UseMvc) تعریف می‌کنیم و زمانیکه یک درخواست به میان‌افزار MVC میرسد، این میان‌افزار بر اساس اطلاعات Url و Route‌های تعریف شده، اولین Route ایی را که همخوانی داشته باشد، انتخاب کرده و مقادیر مسیر یابی (RouteValues) را استخراج می‌کند و بدین ترتیب می‌فهمد که کدام Controller و Action باید فراخوانی شود.

      در Pipeline چون میان‌افزارها به صورت ترتیبی اجرا می‌شوند، تا زمانیکه درخواست ما به میان‌افزار MVC (منظور جایی است که Route‌ها تعریف شده‌اند؛ ولی برای درک بهتر با میان‌افزار MVC مثال میزنیم) نمی‌توانیم از RouteValues استفاده کنیم.
      مشکل جایی پیش می‌آید که مثلا برای وبسایت‌های چند زبانه که بر اساس Url کار می‌کنند (مثلا fa/home/index) نیاز داریم قبل از رسیدن به میان‌افزار MVC، زبان جاری (همان fa) را توسط سیستم مسیریابی بخوانیم و Culture برنامه را تغییر بدهیم ولی تا زمانیکه به میان‌افزار MVC نرسیم این کار امکان پذیر نیست.

      یک مثال دیگر آن، استفاده از ویژگی EnableCorsAttribute (است که بر خلاف UseCors که مکانیزم CORS را به صورت سراسری اعمال می‌کند) میتوان آن را بر روی یک Action خاص اعمال کرد.
      از آنجایی که در مکانیزم مسیریابی قبلی، سیستم Routing بسیار وابسته به MVC است، ما نمی‌توانیم قبل از رسیدن به میان‌افزار MVC، جلوی درخواست‌های غیر مجاز را بگیریم، چرا که به Routing درسترسی نداریم و نمی‌دانیم کدام Controller و Action قرار است فراخوانی بشود.

      اما در مکانیزم Endpoint Routing، وقتی یک درخواست می‌آید، همان ابتدا مشخص می‌شود که به کدام Route نگاشت شده؛ درنتیجه بقیه‌ی میان‌افزارهای قبل از MVC هم می‌توانند از سیستم Routing استفاده کرده و اطلاعات داخل Url را درک کنند.

       این قابلیت در ASP Core 2.2، اوایل با نام Dispatcher معرفی شد که بعدا به Endpoint Routing تغییر نام پیدا کرد و به صورت پیشفرض توسط SetCompatibilityVersion ورژن Version_2_2 به بالا فعال می‌شود.
      برای استفاده از Endpoint Routing باید میان‌افزار مربوطه را توسط متد UseEndpointRouting اضافه کنیم. کار این میان‌افزار تشخیص Endpoint مورد نظر براساس Url درخواستی و Route‌های تعریف شده است و پس از آن می‌توانیم از مقادیر مسیریابی (RouteValues) استفاده کنیم (حتی قبل از رسیدن به محل تعریف Route ها).
      // ASP.NET Core 2.2
      public void Configure(IApplicationBuilder app)
      {
         app.UseEndpointRouting();
      
         app.OtherMiddleware(); // Access to route values
      
         app.UseMvc();
      }

      نکته : در ASPNET Core 3.0 متد UseEndpointRouting به UseRouting تغییر نام پیدا کرد و نیز تعریف Route (بهتر است بگوییم Endpoint ها) به شکل زیر تغییر پیدا کرده است.
      // ASP.NET Core 3.0
      public void Configure(IApplicationBuilder app)
      {
         app.UseRouting(); 
      
         app.OtherMiddleware();
      
         app.UseEndpoints(endpoints =>
         {
            // Mapping of endpoints goes here:
            endpoints.MapControllers()
            endpoints.MapRazorPages()
            endpoints.MapHub<MyChatHub>()
            endpoints.MapGrpcService<MyCalculatorService>()
         });
      }

      برای غیر فعال کردن قابلیت Endpoint Routing می‌توانید توسط MvcOptions.EnableEndpointRouting = false آن را غیرفعال کنید.

      منابع و اطلاعات بیشتر:
      مطالب
      بررسی بهبودهای ProblemDetails در ASP.NET Core 7x
      در زمان ارائه‌ی ASP.NET Core 2.1، ویژگی جدیدی به نام [ApiController] ارائه شد که با استفاده از آن، یکسری اعمال توکار جهت سهولت کار با Web API توسط خود فریم‌ورک انجام می‌شوند؛ برای مثال عدم نیاز به بررسی وضعیت ModelState و بررسی خودکار آن با علامتگذاری یک کنترلر به صورت ApiController. یکی دیگر از این ویژگی‌های توکار، تبدیل خروجی تمام status codeهای بزرگتر و یا مساوی 400 یا همان Bad Request، به شیء جدید و استاندارد ProblemDetails است:
      {
          "type": "https://example.com/probs/out-of-credit",
          "title": "You do not have enough credit.",
          "detail": "Your current balance is 30, but that costs 50.",
          "instance": "/account/12345/msgs/abc",
          "status": 403,
      }
       بازگشت یک چنین خروجی یک‌دست و استانداردی، استفاده‌ی از آن‌را توسط کلاینت‌ها، ساده و قابل پیش‌بینی می‌کند. البته باید درنظر داشت که اگر در این‌حالت، برنامه یک استثنای معمولی را سبب شود، ProblemDetails ای بازگشت داده نمی‌شود. اگر برنامه در حالت توسعه اجرا شود، با استفاده از میان‌افزار app.UseDeveloperExceptionPage، یک صفحه‌ی نمایش جزئیات خطا ظاهر می‌شود و اگر برنامه در حالت تولید و ارائه‌ی نهایی اجرا شود، یک صفحه‌ی خالی (بدون داشتن response body) با status code مساوی 500 بازگشت داده می‌شود. این کمبود ویژه و امکانات سفارشی سازی بیشتر آن، به صورت توکار به ASP.NET Core 7x اضافه شده‌اند و دیگر نیازی به استفاده از کتابخانه‌های ثالث دیگری برای انجام آن نیست.


      ProblemDetails بر اساس RFC7807 طراحی شده‌است

      RFC7807، قالب استانداردی را برای ارائه‌ی خطاهای HTTP APIها تعریف می‌کند تا نیازی به وجود تعاریف متعددی در این زمینه نباشد و خروجی آن قابل پیش‌بینی و قابل بررسی توسط تمام کلاینت‌های یک API باشد. کلاس ProblemDetails در ASP.NET Core نیز بر همین اساس طراحی شده‌است.
      این RFC دو فرمت خروجی را بر اساس مقدار مشخص شده‌ی در هدر Content-Type بازگشت داده شده، مجاز می‌داند:
      • JSON: “application/problem+json” media type
      • XML: “application/problem+xml” media type

      که با توجه به این هدر ارسالی، اگر از یک کلاینت از نوع HttpClient استفاده کنیم، می‌توان بر اساس مقدار ویژه‌ی «application/problem+json» تشخیص داد که خروجی API دریافتی، به همراه خطا است و نحوه‌ی پردازش آن به صورت زیر خواهد بود:
      var mediaType = response.Content.Headers.ContentType?.MediaType;
      if (mediaType != null && mediaType.Equals("application/problem+json", StringComparison.InvariantCultureIgnoreCase))
      {
         var problemDetails = await response.Content.ReadFromJsonAsync<ProblemDetails>(null, ct) ?? new ProblemDetails();
         // ...
      }
      در اینجا بدنه‌ی اصلی شیء ProblemDetails بازگشت داده شده، می‌تواند به همراه اعضای زیر باشد:
      - type: یک رشته‌است که به آدرس مستندات HTML ای مرتبط با خطای بازگشت داده شده، اشاره می‌کند.
      - title: رشته‌ای است که خلاصه‌ی خطای رخ‌داده را بیان می‌کند.
      - detail: رشته‌ای است که توضیحات بیشتری را در مورد خطای رخ‌داده، بیان می‌کند.
      - instance: رشته‌ای است که به آدرس محل بروز خطا اشاره می‌کند.
      - status: عددی است که بیانگر HTTP status code بازگشتی از سمت سرور است.


      البته اگر ویژگی ApiController بر روی کنترلرهای خود استفاده نمی‌کنید، می‌توانید این خروجی را به صورت زیر هم با استفاده از return Problem، تولید کنید:
      [HttpPost("/sales/products/{sku}/availableForSale")]
      public async Task<IActionResult> AvailableForSale([FromRoute] string sku)
      {
         return Problem(
                  "Product is already Available For Sale.",
                  "/sales/products/1/availableForSale",
                  400,
                  "Cannot set product as available.",
                  "http://example.com/problems/already-available");
      }


      امکان افزودن اعضای سفارشی به شیء ProblemDetails

      امکان بسط این خروجی، با افزودن اعضای سفارشی نیز پیش‌بینی شده‌است. یک نمونه‌ی متداول و پرکاربرد آن، بازگشت خطاهای مرتبط با اعتبارسنجی اطلاعات رسیده‌است:
      HTTP/1.1 400 Bad Request
      Content-Type: application/problem+json
      Content-Language: en
      {
          "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
          "title": "One or more validation errors occurred.",
          "status": 400,
          "errors": {
              "User": [
                  "The user name is not verified."
              ]
          }
      }
      در اینجا عضو جدید errors را بنابر نیاز این مساله‌ی خاص، مشاهده می‌کنید که در صورت استفاده از ویژگی ApiController بر روی کنترلرهای Web API، به صورت خودکار توسط ASP.NET Core تولید می‌شود و نیازی به تنظیم خاصی و یا کدنویسی اضافه‌تری ندارد. کلاس مخصوص آن نیز ValidationProblemDetails‌ است.


      جهت افزودن اعضای سفارشی دیگری به شیء ProblemDetails می‌توان به صورت زیر عمل کرد:
      namespace WebApplication.Controllers
      {
          [ApiController]
          [Route("[controller]")]
          public class DemoController : ControllerBase
          {
              [HttpPost]
              public ActionResult Post()
              {
                  var problemDetails = new ProblemDetails
                  {
                      Detail = "The request parameters failed to validate.",
                      Instance = null,
                      Status = 400,
                      Title = "Validation Error",
                      Type = "https://example.net/validation-error",
                  };
      
                  problemDetails.Extensions.Add("invalidParams", new List<ValidationProblemDetailsParam>()
                  {
                      new("name", "Cannot be blank."),
                      new("age", "Must be great or equals to 18.")
                  });
      
                  return new ObjectResult(problemDetails)
                  {
                      StatusCode = 400
                  };
              }
          }
      
          public class ValidationProblemDetailsParam
          {
              public ValidationProblemDetailsParam(string name, string reason)
              {
                  Name = name;
                  Reason = reason;
              }
      
              public string Name { get; set; }
              public string Reason { get; set; }
          }
      }
      شیء ProblemDetails، به همراه خاصیت Extensions است که می‌توان به آن یک <Dictionary<string, object را انتساب داد و نمونه‌ای از آن‌را در مثال فوق مشاهده می‌کنید. این مثال سبب می‌شود تا عضو جدیدی با کلید دلخواه invalidParams، به همراه لیستی از name و reasonها به خروجی نهایی اضافه شود. مقدار این کلید، از نوع object است؛ یعنی هر شیء دلخواهی را در اینجا می‌توان تعریف و استفاده کرد.


      معرفی سرویس جدید ProblemDetails در دات نت 7

      در دات نت 7 می‌توان سرویس‌های جدید ProblemDetails را به نحو زیر به برنامه اضافه کرد:
      services.AddProblemDetails();
      پس از آن به 3 روش مختلف می‌توان از امکانات این سرویس‌ها استفاده کرد:
      الف) با اضافه کردن میان‌افزار مدیریت خطاها
      app.UseExceptionHandler();
      پس از آن، هر استثنای مدیریت نشده‌ای نیز به صورت یک ProblemDetails ظاهر می‌شود و دیگر همانند قبل، سبب نمایش یک صفحه‌ی خالی نخواهد شد.

      ب) با افزودن میان‌افزار StatusCodePages
      app.UseStatusCodePages();
      در این حالت مواردی که استثناء شمرده نمی‌شوند مانند 404، در صورت بروز رسیدن به یک مسیریابی یافت نشده و یا 405، در صورت درخواست یک HTTP method غیرمعتبر نیز توسط یک ProblemDetails استاندارد مدیریت می‌شوند.

      ج) با افزودن میان‌افزار صفحه‌ی استثناءهای توسعه دهنده‌ها
      app.UseDeveloperExceptionPage();
      به این ترتیب در خروجی ProblemDetails، اطلاعات بیشتری از استثناء رخ‌داده، مانند استک‌تریس آن ظاهر خواهد شد.


      امکان بازگشت ساده‌تر یک ProblemDetails سفارشی در دات نت 7

      برای سفارشی سازی خروجی ProblemDetails، علاوه بر راه‌حلی که پیشتر در این مطلب مطرح شد، می‌توان در دات نت 7 از روش تکمیلی ذیل نیز استفاده کرد:
      builder.Services.AddProblemDetails(options =>
          options.CustomizeProblemDetails = ctx =>
                  ctx.ProblemDetails.Extensions.Add("MachineName", Environment.MachineName));
      به این ترتیب در صورت لزوم می‌توان یک عضو سفارشی سراسری را به تمام اشیاء ProblemDetails برنامه به صورت خودکار اضافه کرد و یا اگر می‌خواهیم این مورد را کمی اختصاصی‌تر کنیم، می‌توان به صورت زیر عمل کرد:

      الف) تعریف یک ErrorFeature سفارشی
      public class MyErrorFeature
      {
          public ErrorType Error  { get; set; }
      }
      ​
      public enum ErrorType
      {
          ArgumentException
      }
      در ASP.NET Core می‌توان به شیء HttpContext.Features قابل تنظیم در هر اکشن متدی، اشیاء دلخواهی را مانند شیء سفارشی فوق، اضافه کرد و سپس در قسمت options.CustomizeProblemDetails تنظیماتی که ذکر شد، به دریافت و تنظیم آن، واکنش نشان داد.

      ب) تنظیم مقدار ErrorFeature سفارشی در اکشن متدها
          [HttpGet("{value}")]
          public IActionResult MyErrorTest(int value)
          {
              if (value <= 0)
              {
                  var errorType = new MyErrorFeature
                  {
                      Error = ErrorType.ArgumentException
                  };
                  HttpContext.Features.Set(errorType);
                  return BadRequest();
              }
      ​
              return Ok(value);
          }
      پس از تعریف شیءایی که قرار است به HttpContext.Features اضافه شود، اکنون روش تنظیم و مقدار دهی آن‌را در یک اکشن متد، در مثال فوق مشاهده می‌کنید.

      ج) واکنش نشان دادن به دریافت ErrorFeature سفارشی
      services.AddProblemDetails(options =>
          options.CustomizeProblemDetails = ctx =>
          {
              var MyErrorFeature = ctx.HttpContext.Features.Get<MyErrorFeature>();
      ​
              if (MyErrorFeature is not null)
              {
                  (string Title, string Detail, string Type) details = MyErrorFeature.Error switch
                  {
                      ErrorType.ArgumentException =>
                      (
                          nameof(ArgumentException),
                          "This is an argument-exception.",
                          "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1"
                      ),
                      _ =>
                      (
                          nameof(Exception),
                          "default-exception",
                          "https://www.rfc-editor.org/rfc/rfc7231#section-6.6.1"
                      )
                  };
      ​
                  ctx.ProblemDetails.Title = details.Title;
                  ctx.ProblemDetails.Detail = details.Detail;
                  ctx.ProblemDetails.Type = details.Type;
              }
          }
      );
      پس از تنظیم HttpContext.Features در اکشن متدی، می‌توان در options.CustomizeProblemDetails فوق، توسط متد ctx.HttpContext.Features.Get به آن شیء خاص تنظیم شده، در صورت وجود دسترسی یافت و سپس جزئیات بیشتری را از آن استخراج و مقادیر ctx.ProblemDetails جاری را که قرار است به کاربر بازگشت داده شوند، بازنویسی کرد و یا تغییر داد.
       

      امکان تبدیل ساده‌تر اطلاعات استثناءهای سفارشی به یک ProblemDetails سفارشی در دات نت 7

      بجای استفاده از تنظیمات services.AddProblemDetails جهت بازنویسی مقدار شیء ProblemDetails بازگشتی، می‌توان جزئیات میان‌افزار app.UseExceptionHandler را نیز سفارشی سازی کرد و به بروز استثناءهای خاصی واکنش نشان داد. برای مثال فرض کنید یک استثنای سفارشی را به صورت زیر طراحی کرده‌اید:
      public class MyCustomException : Exception
      {
          public MyCustomException(
              string message,
              HttpStatusCode statusCode = HttpStatusCode.BadRequest
          ) : base(message)
          {
              StatusCode = statusCode;
          }
      ​
          public HttpStatusCode StatusCode { get; }
      }
      و سپس در اکشن متدی، سبب بروز آن شده‌اید:
          [HttpGet("{value}")]
          public IActionResult MyErrorTest(int value)
          {
              if (value <= 0)
              {
                  throw new MyCustomException("The value should be positive!");
              }
      ​
              return Ok(value);
          }
      اکنون می‌توان در میان‌افزار مدیریت استثناءهای برنامه، نسبت به مدیریت این استثناء خاص، واکشن نشان داد و ProblemDetails متناظری را تولید و بازگشت داد:
      app.UseExceptionHandler(exceptionHandlerApp =>
      {
          exceptionHandlerApp.Run(async context =>
          {
              context.Response.ContentType = "application/problem+json";
      ​
              if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
              {
                  var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
                  var exceptionType = exceptionHandlerFeature?.Error;
      ​
                  if (exceptionType is not null)
                  {
                      (string Title, string Detail, string Type, int StatusCode) details = exceptionType switch
                      {
                          MyCustomException MyCustomException =>
                          (
                              exceptionType.GetType().Name,
                              exceptionType.Message,
                              "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1",
                              context.Response.StatusCode = (int)MyCustomException.StatusCode
                          ),
                          _ =>
                          (
                              exceptionType.GetType().Name,
                              exceptionType.Message,
                              "https://www.rfc-editor.org/rfc/rfc7231#section-6.6.1",
                              context.Response.StatusCode = StatusCodes.Status500InternalServerError
                          )
                      };
      ​
                      await problemDetailsService.WriteAsync(new ProblemDetailsContext
                      {
                          HttpContext = context,
                          ProblemDetails =
                          {
                              Title = details.Title,
                              Detail = details.Detail,
                              Type = details.Type,
                              Status = details.StatusCode
                          }
                      });
                  }
              }
          });
      });
      ​
      در اینجا نحوه‌ی کار با سرویس توکار IProblemDetailsService و سپس دسترسی به IExceptionHandlerFeature و استثنای صادر شده را مشاهده می‌کنید. پس از آن بر اساس نوع و اطلاعات این استثناء، می‌توان یک ProblemDetails مخصوص را تولید و در خروجی ثبت کرد.
      نظرات مطالب
      ثبت جزئیات استثناهای Entity framework توسط ELMAH
      یک نکته‌ی تکمیلی: معادل این مطلب با EF Core 5x

      کدهای Interceptor به این صورت اصلاح شده و تطابق خواهند یافت:
      using System;
      using System.Data;
      using System.Data.Common;
      using System.Globalization;
      using System.Linq;
      using System.Text;
      using System.Threading;
      using System.Threading.Tasks;
      using Microsoft.EntityFrameworkCore.Diagnostics;
      using Microsoft.Extensions.Logging;
      
      namespace Utils
      {
          public class EfExceptionsInterceptor : DbCommandInterceptor
          {
              private readonly ILogger<EfExceptionsInterceptor> _logger;
      
              public EfExceptionsInterceptor(ILogger<EfExceptionsInterceptor> logger)
              {
                  _logger = logger ?? throw new ArgumentNullException(nameof(logger));
              }
      
              public override void CommandFailed(DbCommand command, CommandErrorEventData eventData)
              {
                  logError(command, eventData);
              }
      
              public override Task CommandFailedAsync(DbCommand command, CommandErrorEventData eventData, CancellationToken cancellationToken = default)
              {
                  logError(command, eventData);
                  return Task.CompletedTask;
              }
      
              private void logError(DbCommand command, CommandErrorEventData eventData)
              {
                  if (command == null || eventData == null)
                  {
                      return;
                  }
      
                  var ex = eventData.Exception;
                  if (ex == null)
                  {
                      return;
                  }
      
                  var sqlData = logSqlAndParameters(command);
                  var contextualMessage = $"{sqlData}{Environment.NewLine}OriginalException:{Environment.NewLine}{ex} {Environment.NewLine}";
                  _logger.LogError(contextualMessage);
              }
      
              private static string logSqlAndParameters(DbCommand command)
              {
                  // -- Name: [Value] (Type = {}, Direction = {}, IsNullable = {}, Size = {}, Precision = {} Scale = {})
                  var builder = new StringBuilder();
      
                  var commandText = command.CommandText ?? "<null>";
                  builder.AppendFormat(CultureInfo.InvariantCulture, "{0}Command: {0}{1}", Environment.NewLine, commandText)
                      .AppendLine();
      
                  var parameters = command.Parameters.OfType<DbParameter>().ToList();
      
                  if (parameters.Any())
                  {
                      builder.AppendFormat(CultureInfo.InvariantCulture, "{0}Parameters: ", Environment.NewLine)
                          .AppendLine();
                  }
      
                  foreach (var parameter in parameters)
                  {
                      builder.Append("-- ")
                          .Append(parameter.ParameterName)
                          .Append(": '")
                          .Append((parameter.Value == null || parameter.Value == DBNull.Value) ? "null" : parameter.Value)
                          .Append("' (Type = ")
                          .Append(parameter.DbType);
      
                      if (parameter.Direction != ParameterDirection.Input)
                      {
                          builder.Append(", Direction = ").Append(parameter.Direction);
                      }
      
                      if (!parameter.IsNullable)
                      {
                          builder.Append(", IsNullable = false");
                      }
      
                      if (parameter.Size != 0)
                      {
                          builder.Append(", Size = ").Append(parameter.Size);
                      }
      
                      if (((IDbDataParameter)parameter).Precision != 0)
                      {
                          builder.Append(", Precision = ").Append(((IDbDataParameter)parameter).Precision);
                      }
      
                      if (((IDbDataParameter)parameter).Scale != 0)
                      {
                          builder.Append(", Scale = ").Append(((IDbDataParameter)parameter).Scale);
                      }
      
                      builder.Append(')').Append(Environment.NewLine);
                  }
      
                  return builder.ToString();
              }
          }
      }
      سپس برای ثبت آن، چون یک سرویس را به صورت تزریق وابستگی‌ها دریافت کرده، یکبار باید آن‌را به سیستم تزریق وابستگی‌ها معرفی کرد:
      services.AddScoped<EfExceptionsInterceptor>();
      و بعد نحوه‌ی معرفی آن به AddDbContextPool به روش خاص زیر است که از سرویس‌پروایدر استفاده می‌کند:
      services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
                          optionsBuilder
                              .UseSqlServer(
                                  connectionString,
                                  sqlServerOptionsBuilder =>
                                  {
                                    // ...
                                  })
      .AddInterceptors(serviceProvider.GetRequiredService<EfExceptionsInterceptor>()));
      این نکته‌ای است که جهت ثبت Interceptorهای دارای تزریق وابستگی، مورد استفاده قرار می‌گیرد.
      نظرات مطالب
      ASP.NET MVC #18
      احتمالا Role provider سفارشی شما درست ثبت نشده و کار نمی‌کند. در این مورد در انتهای متن قسمت جاری بحث شده.