خطا در اجرای پروژه
System.Data.SqlClient.SqlException (0x80131904): FILESTREAM feature is disabled.
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
Visual Studio
ASP. Net
طراحی و توسعه وب
PHP
اسکیوال سرور
سی شارپ
عمومی دات نت
ویندوز
مسایل اجتماعی و انسانی برنامه نویسی
متفرقه
System.Web.dll System.Web.ApplicationServices.dll
C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\WebMatrix.Data.dll C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\WebMatrix.WebData.dll
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=.;Initial Catalog=SimpleMembershipProviderDB;Integrated Security=True;" providerName="System.Data.SqlClient"/> </connectionStrings> <system.web> <roleManager enabled="true" defaultProvider="SimpleRoleProvider"> <providers> <clear/> <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/> </providers> </roleManager> <membership defaultProvider="SimpleMembershipProvider"> <providers> <clear/> <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/> </providers> </membership> </system.web> </configuration>
using System; using System.Security.Principal; using System.Web.Security; using WebMatrix.WebData; namespace MemberShipConsoleApplication { class Program { static void Main(string[] args) { WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); AddUserAndRolSample(); Login(); if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated) RunApp(); } static void AddUserAndRolSample() { if (WebSecurity.UserExists("iman")) return; // No implements in SimpleMembershipProvider : // Membership.CreateUser("iman", "123"); WebSecurity.CreateUserAndAccount("iman", "123"); Roles.CreateRole("admin"); Roles.CreateRole("User"); Roles.AddUserToRole("iman", "admin"); } static void Login() { for (int i = 0; i < 3; i++) { Console.Write("UserName: "); var userName = Console.ReadLine(); Console.Write("Password: "); var password = Console.ReadLine(); if (Membership.ValidateUser(userName, password)) { var user = Membership.GetUser(userName); var identity = new GenericIdentity(user.UserName); var principal = new RolePrincipal(identity); System.Threading.Thread.CurrentPrincipal = principal; Console.Clear(); return; } Console.WriteLine("User Name Or Password Not Valid."); } } static void RunApp() { Console.WriteLine("Welcome To MemberShip. User Name: {0}", System.Threading.Thread.CurrentPrincipal.Identity.Name); if (System.Threading.Thread.CurrentPrincipal.IsInRole("admin")) Console.WriteLine("Hello Admin User!"); Console.Read(); } } }
InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables)
انتقال Context و Migrations به یک اسمبلی دیگر
تا اینجا اگر مثال بررسی شده را دنبال کرده باشید، دو پوشهی Entities و Migrations را به همراه فایلهای موجودیتها، Context برنامه و Migrations آنها، در همان پروژهی اصلی برنامه، خواهید داشت:
در ادامه قصد داریم بانک اطلاعاتی آزمایشی برنامه را drop کرده، پوشهی Migrations را حذف و صرفا دو فایل ApplicationDbContextSeedData و DBInitialization آنرا نگه داریم.
کلاس Person را به اسمبلی جدید Entities و کلاس ApplicationDbContext را به اسمبلی جدید DataLayer منتقل میکنیم:
اسمبلی جدید Core1RtmEmptyTest.Entities از نوع NET Core Class Library. است و صرفا حاوی کلاسهای موجودیتهای برنامهاست.
اسمبلی جدید Core1RtmEmptyTest.DataLayer نیز از نوع NET Core Class Library. بوده و حاوی تعاریف Context برنامه، به همراه Migrations و تنظیمات آن خواهد بود.
تا اینجا با این نقل و انتقالات، نیاز است وابستگیهای DataLayer را اصلاح کنیم. بنابراین فایل project.json آنرا گشوده و به نحو ذیل تکمیل نمائید:
{ "version": "1.0.0-*", "dependencies": { "Core1RtmEmptyTest.Entities": "1.0.0-*", "Microsoft.EntityFrameworkCore": "1.0.0", "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0", "Microsoft.Extensions.Configuration.Abstractions": "1.0.0", "NETStandard.Library": "1.6.0" }, "frameworks": { "netstandard1.6": { "imports": "dnxcore50" } } }
وابستگی Microsoft.Extensions.Configuration.Abstractions برای کار با IConfigurationRoot اضافه شدهاست (دسترسی به تنظیمات برنامه از طریق تزریق وابستگیها).
به علاوه اکنون به پروژهی وب اصلی مراجعه کرده و فایل project.json آنرا جهت افزودن ارجاعاتی به این دو اسمبلی جدید، ویرایش کنید:
{ "dependencies": { // same as before "Core1RtmEmptyTest.Entities": "1.0.0-*", "Core1RtmEmptyTest.DataLayer": "1.0.0-*" } }
فعال سازی Migrations و قرار دادن فایلهای آن در اسمبلی Core1RtmEmptyTest.DataLayer
در ادامه اگر مانند قسمت قبل بخواهیم مهاجرتها را اضافه کنیم، به خطای ذیل خواهیم رسید:
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest>dotnet ef migrations add InitialDatabase Your target project 'Core1RtmEmptyTest' doesn't match your migrations assembly 'Core1RtmEmptyTest.DataLayer'. Either change your target project or change your migrations assembly.
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest.DataLayer>dotnet ef migrations add InitialDatabase No executable found matching command "dotnet-ef"
{ // same as before "tools": { "Microsoft.EntityFrameworkCore.Tools": { "version": "1.0.0-preview2-final", "imports": [ "portable-net45+win8" ] } }, // same as before }
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest.DataLayer>dotnet ef migrations add InitialDatabase Could not invoke this command on the startup project 'Core1RtmEmptyTest.DataLayer'. This preview of Entity Framework tools does not support commands on class library projects in ASP.NET Core and .NET Core applications.
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest.DataLayer>dotnet ef --startup-project ../Core1RtmEmptyTest/ migrations add InitialDatabase Done. To undo this action, use 'dotnet ef migrations remove'
اعمال کلاسهای Migrations تولید شده به بانک اطلاعاتی
پس از تولید موفقیت آمیز فایلهای مهاجرت، برای اعمال آنها به بانک اطلاعاتی، اینبار نیز دستور را از همان پوشهی DataLayer با پارامتر پروژهی آغازین اجرا میکنیم:
D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest.DataLayer>dotnet ef --startup-project ../Core1RtmEmptyTest/ database update Applying migration '13950527070105_InitialDatabase'. Done.
بنابراین به صورت خلاصه
- ابتدا قسمت tools تنظیمات پروژهی data layer را برای فعال سازی دستورات خط فرمان EF ویرایش کنید.
- سپس از طریق خط فرمان به پوشهی data layer وارد شوید. اینبار باید دستورات EF را از ریشهی این پوشه، بجای پوشهی اصلی برنامه صادر کرد.
- در اینجا دستورات افزودن مهاجرتها و به روز رسانی بانک اطلاعاتی، همانند قبل هستند. فقط ذکر محل واقع شدن پوشهی آغازین برنامه توسط پارامتر startup-project الزامی است.
BEGIN TRANSACTION; INSERT INTO SP RELATION {S# S#(‘S5’), P# P#(‘P1’), QTY QTY(1000)}}; IF any error occurred THEN GOTO UNDO; END IF; UPDATE P WHERE P# = P#(‘P1’) TOTAL:=TOTAL + QTY(1000); IF any error occurred THEN GOTO UNDO; END IF; COMMIT; GOTO FINISH; UNDO: ROLLBACK; FINISH: RETURN;
Read Sav_Amt Sav_Amt := Sav-Amt - 500 if Sav-Amt <0 then do put (“insufficient fund”) rollback end else do Write Sav_Amt Read Chk_Amt Chk_Amt := Chk_Amt + 500 Write Chk-Amt put (“transfer complete”) End transaction
tx_begin(); execute T1 //at site D execute T2 //at site C Execute T3 //at site B … tX_commit ();
begin transaction(); s1; sp1:= create savepoint(0); s2; sp2:= create savepoint(0); if (condition) rollback (spi); … … commit
Alter table, Create, delete, insert, open, drop, fetch, grant, revoke, select, truncate table, update
Set implicit_transaction on
SET TRANSACTION READ ONLY
SET TRANSACTION READ WRITE
READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE
ساخت منوهای چند سطحی در ASP.NET MVC
برای مطالعه بیشتر:
- MVC #19
- چگونه نرم افزارهای تحت وب سریعتری داشته باشیم؟ قسمت دوم
- Implementing second level caching in EF code first
- استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
و ...
دریافت قالب WpfFramework.vsix و نحوه نصب و راه اندازی آن
//table="[dbo].[Users]" return table .Replace("dbo.", string.Empty) .Replace("`", string.Empty) .Replace("[", string.Empty) .Replace("]", string.Empty) .Trim() return table .Replace("`", string.Empty) .Replace("[", string.Empty) .Replace("]", string.Empty) .Replace("dbo.", string.Empty) .Trim();
نکته : فقط در Vs.Net با نسخههای Ultimate و Premium میتونید از Code UI Test استفاده کنید که البته به دلیل اینکه در ایران پیدا کردن نسخههای دیگر Vs.Net به غیر از Ultimate سختتر است به طور قطع این محدودیت برای برنامه نویسان ما وجود نخواهد داشت. برای اینکه از نسخه Vs.Net خود اطمینان حاصل کنید از منوی Help گزینه About Microsoft Visual Studio رو انتخاب کنید. پنجره ای به شکل زیر مشاهده خواهید کرد که در آن مشخصات کامل Vs.Net ذکر شده است.
در این مرحله قصد داریم برای فرم زیر Unit Test طراحی کنیم. پروژه به صورت زیر است:
کاملا واضح است که در این فرم دو عدد به عنوان ورودی دریافت میشود و بعد از کلیک بر روی CalculateSum نتیجه در textbox سوم نمایش داده میشود. برای تست عملکرد صحیح فرم بالا ایتدا به Solution مورد نظر از منوی test Project یک Coded UI Test Project اضافه میکنیم. به دلیل اینکه این قبلا در این Solution پروژه تست از نوع Coded UI Test نبود بلافاصله یک پنجره نمایش داده میشود. مطمئن شوید گزینه اول انتخاب شده و بعد بر روی Ok کلیک کنید.(گزینه اول به معنی است که قصد داریم عملیات مورد نظر بر روی UI را رکورد کنیم و گزینه دوم به معنی است که قصد داریم از عملیات رکورد شده قبلی استفاده کنیم). یک کلاس به نام CodeUITest1 به همراه یک متد تست به نام CodedUITestMethod1 ساخته میشود. اولین چیزی که جلب توجه میکند این است که این کلاس به جای TestClassAttribute دارای نشان CodeUITestAttrbiute است. در گوشه سمت راست Vs.Net خود یک پنجره کوچک به نام UI Map Test Builder مانند شکل زیر خواهید دید.دکمه قرمز رنگ به نام Record Button است و عملیات تست را رکورد خواهد کرد. دکمه دایره ای به رنگ مشکی برای تعیین Assertion به کار میرود. و در نهایت گزینه آخر کدهای مورد نظر مراحل قبل را به صورت خودکار تولید خواهد کرد.
#روش کار
روش کار به این صورت است که ابتدا شما مراحل تست خود را شبیه سازی خواهید کرد و بعد از آن Test Builder مراحل تست شما را به صورت کامل به صورت کدهای قابل فهم تولید خواهد کرد. (دقیقا شبیه به ایجاد UnitTest به روش Arrange/Act/Assert است با این تفاوت که این مراحل توسط UI Map رکورد شده و نیازی به کد نویسی ندارد). در پایان باید یک Data Driven Coded UI Test طراحی کنید تا بتوانید از این مراحل رکورد استفاده نمایید.
#چگونگی شبیه سازی :
پروژه را اجرا نمایید. زمانی که فرم مورد نظر ظاهر شد بر روی گزینه Record در TestBuilder کلیک کنید. عملیات ذخیره سازی شروع شده است. در نتیجه به فرم مربوطه رفته و در Textbox اول مقدار 10 و در textbox دوم مقدار 5 را وارد نمایید. با کلیک بر روی دکمه CalculateSum مقدار 15 نمایش داده خواهد شد. از برنامه خارج شوید و بعد بر روی گزینه Generate Code در TestBuilder کلیک کنید با از کلیدهای ترکیبی Alt + G استفاده نمایید.(اگر در این مرحله، از برنامه خارج نشده باشید با خطا مواجه خواهید شد.) در پنجره نمایش داده شده یک نام به متد اختصاص دهید. عملیات تولید کد شروع خواهد شد. بعد کدی مشابه زیر را در متد مربوطه مشاهده خواهید کرد.
[TestMethod] public void CodedUITestMethod1() { this.UIMap.CalculateSum(); this.UIMap.txtSecondValueMustBe10(); }
public void CodedUITestMethod1 ()
{
#region Variable Declarations
WinEdit uITxtFirstNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtFirstNumberWindow.UITxtFirstNumberEdit;
WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
WinButton uICalculateSumButton = this.UIدوعددصحیحواردنماییدWindow.UICalculateSumWindow.UICalculateSumButton;
#endregion
// Type '10' in 'txtFirstNumber' text box
uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText;
// Type '{Tab}' in 'txtFirstNumber' text box
Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys, ModifierKeys.None);
// Type '10' in 'txtSecondNumber' text box
uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText;
// Click 'Calculate Sum' button
Mouse.Click(uICalculateSumButton, new Point(83, 12));
// Type '10' in 'txtFirstNumber' text box
uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText1;
// Type '{Tab}' in 'txtFirstNumber' text box
Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys1, ModifierKeys.None);
// Type '10' in 'txtSecondNumber' text box
uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText1;
// Type '{Tab}' in 'txtSecondNumber' text box
Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys, ModifierKeys.None);
// Click 'Calculate Sum' button
Mouse.Click(uICalculateSumButton, new Point(49, 11));
// Type '10' in 'txtFirstNumber' text box
uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText2;
// Type '{Tab}' in 'txtFirstNumber' text box
Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys2, ModifierKeys.None);
// Type '5' in 'txtSecondNumber' text box
uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText2;
// Type '{Tab}' in 'txtSecondNumber' text box
Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys1, ModifierKeys.None);
// Click 'Calculate Sum' button
Mouse.Click(uICalculateSumButton, new Point(74, 16));
}
چگونگی ایجاد Assertion
اگر به کد متد تست CodedUITestMethod1 در بالا دقت کنید یک متد به صورت this.UIMap.txtSecondValueMustBe10 فراخوانی شده است. این در واقع یک Assertion است که در هنگام عملیات رکورد ایجاد کردم و به این معنی است که مقدار TextBox دوم حتما باید 10 باشد. حال روش تولید Assertionها را بررسی خواهیم کرد.
بعد از شروع شدن مرحله رکورد اگر قصد دارید برای یک کنترل خاص Assert بنویسید، دکمه assertion (به رنگ مشکی و به صورت دایره است) را بر روی کنترل مورد نظر drag&drop کنید. یک border آبی برای کنترل مورد نظر ایجاد خواهد شد:
به محض اتمام عملیات drag&drop منوی زیر ظاهر خواهد شد:
از گزینه Add Assertion استفاده کنید و برای کنترل مورد نظر یک assert بنویسید. در شکل زیر یک assert برای textbox دوم نوشتم به صورتی که مقدار آن باید با 5 برابر باشد.از گزینه آخر برای نمایش پیغام مورد نظر خودتون در هنگامی که aseert با شکست مواجه میشود استفاده کنید.
کد تولید شده زیر برای عملیات assert بالا است:
public void txtSecondValueMustBe10() { #region Variable Declarations WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit; #endregion // Verify that the 'ControlType' property of 'txtSecondNumber' text box equals '10' Assert.AreEqual(this.txtSecondValueMustBe10ExpectedValues.UITxtSecondNumberEditControlType, uITxtSecondNumberEdit.ControlType.ToString()); }
مرحله اول انجام شد. برای تست این مراحل باید یک Data DrivenTest بسازید که در پست بعدی به صورت کامل شرح داده خواهد شد.
ASP.NET MVC #15
فیلترها در ASP.NET MVC
پایه قسمتهای بعدی مانند مباحث امنیت، اعتبار سنجی کاربران، caching و غیره، مبحثی است به نام فیلترها در ASP.NET MVC. تابحال با سه فیلتر به نامهای ActionName، NonAction و AcceptVerbs آشنا شدهایم. به اینها Action selector filters هم گفته میشود. زمانیکه قرار است یک درخواست رسیده به متدی در یک کنترلر خاص نگاشت شود، فریم ورک ابتدا به متادیتای اعمالی به متدها توجه کرده و بر این اساس درخواست را به متدی صحیح هدایت خواهد کرد. ActionName، نام پیش فرض یک متد را بازنویسی میکند و توسط AcceptVerbs اجرای یک متد، به افعالی مانند POST، GET، DELETE و امثال آن محدود میشود که در قسمتهای قبل در مورد آنها بحث شد.
علاوه بر اینها یک سری فیلتر دیگر نیز در ASP.NET MVC وجود دارند که آنها نیز به شکل متادیتا به متدهای کنترلرها اعمال شده و کار نهاییاشان تزریق کدهایی است که باید پیش و پس از اجرای یک اکشن متد، اجرا شوند. 4 نوع فیلتر در ASP.NET MVC وجود دارند:
الف) IAuthorizationFilter
این نوع فیلترها پیش از اجرای هر متد یا فیلتر دیگری در کنترلر جاری اجرا شده و امکان لغو اجرای آنرا فراهم میکنند. پیاده سازی پیشفرض آن توسط کلاس AuthorizeAttribute در فریم ورک وجود دارد.
بدیهی است این نوع اعمال را مستقیما داخل متدهای کنترلرها نیز میتوان انجام داد (بدون نیاز به هیچگونه فیلتری). اما به این ترتیب حجم کدهای تکراری در سراسر برنامه به شدت افزایش مییابد و نگهداری آنرا در طول زمان مشکل خواهد ساخت.
ب) IActionFilter
ActionFilterها پیش (OnActionExecuting) و پس از (OnActionExecuted) اجرای متدهای کنترلر جاری اجرا میشوند و همچنین پیش از ارائه خروجی نهایی متدها. به این ترتیب برای مثال میتوان نحوه رندر یک View را تحت کنترل گرفت. این اینترفیس توسط کلاس ActionFilterAttribute در فریم ورک پیاده سازی شده است.
ج) IResultFilter
ResultFilter بسیار شبیه به ActionFilter است با این تفاوت که تنها پیش از (OnResultExecuting) بازگرداندن نتیجه متد و همچنین پس از (OnResultExecuted) اجرای متد، فراخوانی میگردد. کلاس ActionFilterAttribute موجود در فریم ورک، پیاده سازی پیش فرضی از آنرا ارائه میدهد.
د) IExceptionFilter
ExceptionFilterها پس از اجرای تمامی فیلترهای دیگر، همواره اجرا خواهند شد؛ صرفنظر از اینکه آیا در این بین استثنایی رخ داده است یا خیر. بنابراین یکی از کاربردهای آنها میتواند ثبت وقایع مرتبط با استثناهای رخداده باشد. پیاده سازی پیش فرض آن توسط کلاس HandleErrorAttribute در فریم ورک موجود است.
علت معرفی 4 نوع فیلتر متفاوت هم به مسایل امنیتی بر میگردد. میشد تنها موارد ب و ج معرفی شوند اما از آنجائیکه نیاز است مورد الف همواره پیش از اجرای متدی و همچنین تمامی فیلترهای دیگر فراخوانی شود، احتمال بروز اشتباه در نحوه و ترتیب معرفی این فیلترها وجود داشت. به همین دلیل روش معرفی صریح مورد الف در پیش گرفته شد. برای مثال فرض کنید که اگر از روی اشتباه فیلتر کش شدن اطلاعات پیش از فیلتر اعتبار سنجی کاربر جاری اجرا میشد چه مشکلات امنیتی ممکن بود بروز کند.
مثالی جهت درک بهتر ترتیب و نحوه اجرای فیلترها:
یک پروژه جدید خالی ASP.NET MVC را آغاز کنید. سپس فیلتر سفارشی زیر را به برنامه اضافه نمائید:
using System.Diagnostics;
using System.Web.Mvc;
namespace MvcApplication12.CustomFilters
{
public class LogAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext);
}
private void Log(string stage, ControllerContext ctx)
{
ctx.HttpContext.Response.Write(
string.Format("{0}:{1} - {2} < br/> ",
ctx.RouteData.Values["controller"], ctx.RouteData.Values["action"], stage));
}
}
}
مرسوم است برای ایجاد فیلترهای سفارشی، همانند مثال فوق با ارث بری از پیاده سازیهای توکار اینترفیسهای چهارگانه یاد شده، کار شروع شود.
سپس یک کنترلر جدید را به همراه دو متد، به برنامه اضافه نمائید. برای هر کدام از متدها هم یک View خالی را ایجاد کنید. اکنون این ویژگی جدید را به هر کدام از این متدها اعمال نموده و برنامه را اجرا کنید.
using System.Web.Mvc;
using MvcApplication12.CustomFilters;
namespace MvcApplication12.Controllers
{
public class HomeController : Controller
{
[Log]
public ActionResult Index()
{
return View();
}
[Log]
public ActionResult Test()
{
return View();
}
}
}
سپس ویژگی Log را از متدها حذف کرده و به خود کنترلر اعمال کنید:
[Log]
public class HomeController : Controller
در این حالت ویژگی اعمالی، پیش از اجرای متد درخواستی جاری اجرا خواهد شد یا به عبارتی به تمام متدهای قابل دسترسی کنترلر اعمال میگردد.
تقدم و تاخر اجرای فیلترهای همخانواده
همانطور که عنوان شد، همیشه ابتدا AuthorizationFilter اجرا میشود و در آخر ExceptionFilter. سؤال: اگر در این بین مثلا دو نوع ActionFilter متفاوت به یک متد اعمال شدند، کدامیک ابتدا اجرا میشود؟
تمام فیلترها از کلاسی به نام FilterAttribute مشتق میشوند که دارای خاصیتی است به نام Order. بنابراین جهت مشخص سازی ترتیب اجرای فیلترها تنها کافی است این خاصیت مقدار دهی شود. برای مثال جهت اعمال دو فیلتر سفارشی زیر:
using System.Diagnostics;
using System.Web.Mvc;
namespace MvcApplication12.CustomFilters
{
public class AuthorizationFilterA : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
Debug.WriteLine("OnAuthorization : AuthorizationFilterA");
}
}
}
using System.Diagnostics;
using System.Web.Mvc;
namespace MvcApplication12.CustomFilters
{
public class AuthorizationFilterB : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
Debug.WriteLine("OnAuthorization : AuthorizationFilterB");
}
}
}
خواهیم داشت:
using System.Web.Mvc;
using MvcApplication12.CustomFilters;
namespace MvcApplication12.Controllers
{
public class HomeController : Controller
{
[AuthorizationFilterA(Order = 2)]
[AuthorizationFilterB(Order = 1)]
public ActionResult Index()
{
return View();
}
}
}
در اینجا با توجه به مقادیر order، ابتدا AuthorizationFilterB اجرا میگردد و سپس AuthorizationFilterA.
علاوه بر اینها محدوده اجرای فیلترها نیز بر بر این حق تقدم اجرایی تاثیر گذار هستند. برای مثال در پشت صحنه زمانیکه قرار است یک فیلتر جدید اجرا شود، وهله سازی آن به نحوه زیر است که بر اساس مقادیر order و FilterScope صورت میگیرد:
var filter = new Filter(actionFilter, FilterScope, order);
مقادیر FilterScope را در ادامه ملاحظه مینمائید:
namespace System.Web.Mvc {
public enum FilterScope {
First = 0,
Global = 10,
Controller = 20,
Action = 30,
Last = 100,
}
}
به صورت پیش فرض، ابتدا فیلتری با محدوده اجرای کمتر، اجرا خواهد شد. در اینجا Global به معنای اجرای شدن در تمام کنترلرها است.
تعریف فیلترهای سراسری
برای اینکه فیلتری را عمومی و سراسری تعریف کنیم، تنها کافی است آنرا در متد Application_Start فایل Global.asax.cs به نحو زیر معرفی نمائیم:
GobalFilters.Filters.Add(new AuthorizationFilterA() { Order = 2});
به این ترتیب AuthorizationFilterA، به تمام کنترلرها و متدهای قابل دسترسی آنها در برنامه به صورت خودکار اعمال خواهد شد.
یکی از کاربردهای فیلترهای سراسری، نوشتن برنامههای پروفایلر است. برنامههایی که برای مثال مدت زمان اجرای متدها را ثبت کرده و بر این اساس بهتر میتوان کارآیی قسمتهای مختلف برنامه را دقیقا زیرنظر قرار داد.
یک نکته
کلاس کنترلر در ASP.NET MVC نیز یک فیلتر است:
public abstract class Controller : ControllerBase, IActionFilter, IAuthorizationFilter, IDisposable, IExceptionFilter, IResultFilter
به همین دلیل، امکان تحریف متدهای OnActionExecuting، OnActionExecuted و امثال آن که پیشتر ذکر شد، در یک کنترلر نیز وجود دارد.
کلاس کنترلر دارای محدوده اجرایی First و Order ایی مساوی Int32.MinValue است. به این ترتیب کنترلرها پیش از اجرای هر فیلتر دیگری اجرا خواهند شد.
ASP.NET MVC دارای یک سری فیلتر و متادیتای توکار مانند OutputCache، HandleError، RequireHttps، ValidateInpute و غیره است که توضیحات بیشتر آنها به قسمتهای بعد موکول میگردد.