به همین جهت CacheMethodAttribute تعریف شد و وجود آن توسط (if (cacheMethodAttribute == null در ابتدای کار Interceptor بررسی میشود.
بازخوردهای دوره
استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
به سازنده CacheInterceptor پارامتر UnitOfWork را پاس دادم.
ولی uow به صورت HttpContextLifecycle نمیشه.مشکل این تعریف چیه؟
با تشکر
var container = new Container(); container.Configure(x => { x.For(typeof(IUnitOfWork)).Use(typeof(UnitOfWork)).SetLifecycleTo<HttpContextLifecycle>(); x.Scan(scan => { scan.WithDefaultConventions(); scan.Assembly("Test.Services"); }); x.Policies.SetAllProperties(y => { y.WithAnyTypeFromNamespace("Test.Services.Interfaces"); }); var dynamicProxy = new ProxyGenerator(); x.For<ITestService>() .DecorateAllWith(myTypeInterface => dynamicProxy.CreateInterfaceProxyWithTarget(myTypeInterface, container.GetInstance<CacheInterceptor>())); });
ولی uow به صورت HttpContextLifecycle نمیشه.مشکل این تعریف چیه؟
با تشکر
بازخوردهای دوره
استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
طول عمر Container ایی که تعاریف اولیه را دارد باید به صورت singleton تعریف شود. اگر قرار باشد هر جایی مجزا وهله سازی شود، Containerهای متفاوتی خواهید داشت با اشیاء متفاوتی. یک مثال در این مورد: DI06.zip
بازخوردهای دوره
استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
سلام
مهندس بسیار عالی و خسته نباشید.
یک سوال برای من پیش آمد، آیا نمیشد مانند مثال سوم این پست قسمت کار با کش با Action بازنویسی شود؟ یا اینکه فقط قصد آموزش بوده که به این شکل کد نوشته شده است؟
مهندس بسیار عالی و خسته نباشید.
یک سوال برای من پیش آمد، آیا نمیشد مانند مثال سوم این پست قسمت کار با کش با Action بازنویسی شود؟ یا اینکه فقط قصد آموزش بوده که به این شکل کد نوشته شده است؟
بازخوردهای دوره
استفاده از AOP Interceptors برای حذف کدهای تکراری کش کردن اطلاعات در لایه سرویس برنامه
مقدمه رو یکبار دیگر مطالعه کنید.
در نگارشهای اولیهی ASP.NET Core، پشتیبانی از authorization، صرفا توسط ویژگی [Authorize]، قابل اعمال به اکشن متد خاصی بود. برای بهبود تنظیم این قابلیت، میانافزار جدید Authorization به ASP.NET Core 3.0 اضافه شدهاست و تنظیم آن جهت کار با امکانات امنیتی برنامه، الزامی است؛ در غیر اینصورت در حین مرور این صفحات و قسمتهای محافظت شده، برنامه با خطای زیر متوقف خواهد شد:
محل صحیح تعریف میانافزار Authorization در ASP.NET Core 3.0
توصیه شدهاست میانافزار جدید Authorization، با فراخوانی متد UseAuthorization، بلافاصله پس از فراخوانی متد UseAuthentication معرفی شود. در این حالت این میانافزار، با یک Policy پیشفرض تنظیم میشود که قابل تغییر و بازنویسی است:
در مورد مفهوم متد MapDefaultControllerRoute، میتوانید به نظرات تکمیلی مطلب Endpoint routing مراجعه کنید.
در ASP.NET Core 3.0، پس از تنظیمات فوق است که قطعه کد زیر و اعمال فیلتر Authorize، مجددا کار میکند:
روش تعریف فیلتر Authorize به صورت سراسری در ASP.NET Core 3.0
میتوان AuthorizeFilter را به صورت یک فیلتر سراسری (global filter in MVC) تعریف کرد تا به تمام کنترلرها و اکشن متدها اعمال شود. هرچند این روش هنوز هم در ASP.NET Core 3.0 کار میکند، اما روش توصیه شدهی جدید آن به صورت زیر است:
در این حالت با اعمال RequireAuthorization به MapDefaultControllerRoute، سبب خواهیم شد تا اعتبارسنجی کاربر برای استفادهی از تمام قسمتهای برنامه، اجباری شود. در این بین اگر کنترلری و یا اکشن متدی نباید محافظت شود و دسترسی آزادانهی به آنها مدنظر است، میتوان از فیلتر AllowAnonymous استفاده کرد:
سفارشی سازی سیاستهای Authorization در ASP.NET Core 3.0
اگر بخواهیم DefaultPolicy میانافزار Authorization را بازنویسی کنیم، میتوان از متد services.AddAuthorization به صورت زیر استفاده کرد که در آن authentication و داشتن یک Scope خاص را اجباری میکند:
تنظیم سیاستهای دسترسی، زمانیکه هیچ نوع تنظیمی از پیش تعریف نشدهاست
با تنظیم FallbackPolicy، میتوان تمام endpointهایی را که توسط فیلتر [Authorize] و یا ()RequireAuthorization محافظت نشدهاند، محافظت کرد.
DefaultPolicy توسط فیلتر [Authorize] و یا ()RequireAuthorization مورد استفاده قرار میگیرد؛ اما FallbackPolicy زمانی بکار خواهد رفت که هیچ نوع سیاست دسترسی تنظیم نشدهباشد. حالت پیشفرض FallbackPolicy، پذیرش تمام درخواستها بدون اعتبارسنجی است.
کارکرد مثال زیر با مثالهای قبلی که از DefaultPolicy استفاده میکردند، یکی است و هدف آن، اجبار به اعتبارسنجی کاربران در همهجا (تمام endpoints)، منهای مواردی است که از فیلتر AllowAnonymous استفاده میشود:
امکان اعمال سفارشی میانافزار Authorization به Endpointهای مجزا
برای مثال در مبحث «بررسی سلامت برنامه»، فریمورک، اعتبارسنجی درخواستهای آنرا پیش بینی نکردهاست؛ اما اینبار میتوان اعتبارسنجی را به endpoint آن اعمال کرد:
همانطور که مشاهده میکنید، در اینجا RequireAuthorization، به صورت سفارشی به یک endpoint خاص اعمال شدهاست و تنظیمات آن، با تنظیمات سایر قسمتهای برنامه یکی نیست.
Endpoint xyz contains authorization metadata, but a middleware was not found that supports authorization. Configure your application startup by adding app.UseAuthorization() inside the call to Configure(..) in the application startup code.
محل صحیح تعریف میانافزار Authorization در ASP.NET Core 3.0
توصیه شدهاست میانافزار جدید Authorization، با فراخوانی متد UseAuthorization، بلافاصله پس از فراخوانی متد UseAuthentication معرفی شود. در این حالت این میانافزار، با یک Policy پیشفرض تنظیم میشود که قابل تغییر و بازنویسی است:
public void Configure(IApplicationBuilder app) { ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); }
در ASP.NET Core 3.0، پس از تنظیمات فوق است که قطعه کد زیر و اعمال فیلتر Authorize، مجددا کار میکند:
public class HomeController : ControllerBase { [Authorize] public IActionResult BuyWidgets() { ... } }
میتوان AuthorizeFilter را به صورت یک فیلتر سراسری (global filter in MVC) تعریف کرد تا به تمام کنترلرها و اکشن متدها اعمال شود. هرچند این روش هنوز هم در ASP.NET Core 3.0 کار میکند، اما روش توصیه شدهی جدید آن به صورت زیر است:
public void Configure(IApplicationBuilder app) { ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute().RequireAuthorization(); }); }
[AllowAnonymous] public class HomeController : ControllerBase { ... }
سفارشی سازی سیاستهای Authorization در ASP.NET Core 3.0
اگر بخواهیم DefaultPolicy میانافزار Authorization را بازنویسی کنیم، میتوان از متد services.AddAuthorization به صورت زیر استفاده کرد که در آن authentication و داشتن یک Scope خاص را اجباری میکند:
public void ConfigureServices(IServiceCollection services) { ... services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .RequireScope("MyScope") .Build(); }); }
با تنظیم FallbackPolicy، میتوان تمام endpointهایی را که توسط فیلتر [Authorize] و یا ()RequireAuthorization محافظت نشدهاند، محافظت کرد.
DefaultPolicy توسط فیلتر [Authorize] و یا ()RequireAuthorization مورد استفاده قرار میگیرد؛ اما FallbackPolicy زمانی بکار خواهد رفت که هیچ نوع سیاست دسترسی تنظیم نشدهباشد. حالت پیشفرض FallbackPolicy، پذیرش تمام درخواستها بدون اعتبارسنجی است.
کارکرد مثال زیر با مثالهای قبلی که از DefaultPolicy استفاده میکردند، یکی است و هدف آن، اجبار به اعتبارسنجی کاربران در همهجا (تمام endpoints)، منهای مواردی است که از فیلتر AllowAnonymous استفاده میشود:
public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(options => { options.FallbackPolicy = new AuthorizationPolicyBuilder() .RequireAuthenticatedUser() .RequireScope("MyScope") .Build(); }); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } [AllowAnonymous] public class HomeController : ControllerBase { }
امکان اعمال سفارشی میانافزار Authorization به Endpointهای مجزا
برای مثال در مبحث «بررسی سلامت برنامه»، فریمورک، اعتبارسنجی درخواستهای آنرا پیش بینی نکردهاست؛ اما اینبار میتوان اعتبارسنجی را به endpoint آن اعمال کرد:
public void Configure(IApplicationBuilder app) { ... app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints .MapHealthChecks("/healthz") .RequireAuthorization(new AuthorizeAttribute(){ Roles = "admin", }); }); }
عموما در برنامههای وب مرسوم است که پیغام به کاربر را در همان لابلای html صفحه نمایش میدهند. مثلا یک برچسب و سپس تنظیم متن آن در کد برنامه به صورت پویا.
با استفاده از پلاگینهای jQuery اینکار را به صورت شکیلتری میتوان انجام داد. برای مثال:
پلاگین کم حجمی برای این منظور موجود است به نام jQuery Notice (یکی از چند ده نمونه موجود)
<script type="text/javascript">
$(document).ready(function()
{
jQuery.noticeAdd({
text: 'پیغامی به کاربر',
stay: false
});
});
</script>
<div id="myElement" style="position: absolute">This stays at the top</div>
با استفاده از jQuery اینکار به صورت زیر قابل انجام است:
<script type="text/javascript">
$(document).ready(function()
{
$(window).scroll(function() {
$('#myElement').css('top', $(this).scrollTop() + "px");
});
});
</script>
ولی این روش جهت نمایش پیغامی پویا به کاربر مشکل دارد.
نیاز است به ازای هر پیغام پویا یکبار به نحوی این اسکریپت به صفحه تزریق شود که روش انجام کار در ASP.Net به صورت زیر میتواند باشد:
using System;
using System.Web.UI;
using System.Web;
public class CAddJqueryNotice
{
/// <summary>
/// نمایش یک پیغام بر اساس پلاگین نوتیس
/// </summary>
/// <param name="title">عنوان</param>
/// <param name="msg">پیغام</param>
/// <param name="rtl">راست به چپ؟</param>
/// <param name="duration">مدت زمان نمایش</param>
/// <param name="autoHide">به صورت خودکار بسته شود؟</param>
public static void Show(string title, string msg, bool rtl, int duration, bool autoHide)
{
string scriptBlock
= string.Format(@"<script type=""text/javascript"">
$(document).ready(function() {{
jQuery.noticeAdd({{
text: '<b>{0}</b><br/><div align=left dir={1}>{2}</div>',
stay: {3},
stayTime: {4}
}});
}});
</script>",
title,
(rtl ? "rtl" : "ltr"),
msg,
(autoHide ? "false" : "true"),
duration);
if (HttpContext.Current == null || HttpContext.Current.Handler == null) return;
Page page = HttpContext.Current.Handler as Page;
if (page != null)
page.ClientScript.RegisterStartupScript(
page.GetType(),
"script" + new Guid().ToString("N"),
scriptBlock,
false);
}
}
برای آزمایش آن یک دکمه را در صفحه قرار داده و در روال رخداد گردان کلیک آن کد زیر را اضافه کنید:
CAddJqueryNotice.Show( "لطفا دوباره سعی کنید", "مشکلی رخ داده است", true, 2000, true);
بدیهی است قبل از استفاده از کد فوق، باید چند سطر زیر را به هدر master page سایت خود اضافه کنید:
<script src="jquery-1.3.2.js" type="text/javascript"></script>
<link href="jquery.notice.css" type="text/css" media="screen" rel="stylesheet" />
<script src="jquery.notice.js" type="text/javascript"></script>
اگر به میزان مصرف حافظه اولیهی برنامههای دات نت دقت کنیم، نسبت به مثلا یک برنامهی MFC چند برابر به نظر میرسند و ... این علت دارد:
زمانیکه یک برنامهی مبتنی بر دات نت اجرا میشود، ابتدا JIT compiler شروع به کار کرده و شروع به کامپایل برنامه میکند. این بارگزاری هم در همان پروسهی اصلی برنامه انجام میشود. به همین جهت میزان مصرف حافظهی برنامههای دات نت عموما بالا به نظر میرسد.
اکنون سؤال اینجا است که آیا می توان این حافظهای را که دیگر مورد استفاده نیست (و توسط JIT compiler اخذ شده) به سیستم بازگرداند و محاسبهی مجددی را در این مورد انجام داد. پاسخ به این سؤال را در متد ReEvaluateWorkingSet زیر میتوان مشاهده کرد:
using System;
using System.Diagnostics;
namespace Toolkit
{
public static class Memory
{
public static void ReEvaluateWorkingSet()
{
try
{
Process loProcess = Process.GetCurrentProcess();
//it doesn't matter what you set maxWorkingSet to
//setting it to any value apparently causes the working set to be re-evaluated and excess discarded
loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet + 1);
}
catch
{
//The above code requires Admin privileges.
//So it's important to trap exceptions in case you're running without admin rights.
}
}
}
}
در این متد ابتدا پروسه جاری دریافت شده و سپس MaxWorkingSet به یک عدد دلخواه تنظیم میشود. مهم نیست که این عدد چه چیزی باشد، زیرا این تنظیم سبب میشود که در پشت صحنه به شکل حساب شدهای حافظهای که مورد استفاده نیست به سیستم بازگردانده شود و سپس عددی که در task manager نمایش داده میشود، مجددا محاسبه گردد. همچنین باید دقت داشت که این کد تنها با دسترسی مدیریتی قابل اجرا است و به همین دلیل وجود این try/catch ضروری است.
نحوه استفاده از متد ReEvaluateWorkingSet در برنامههای WinForms :
فایل Program.cs را یافته و سپس در روال رویداد گردان Idle برنامه، متد ReEvaluateWorkingSet را فراخوانی کنید (مثلا هر زمان که برنامه minimized شد اجرا میشود):
//Program.cs
namespace MemUsage
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//...
Application.Idle += applicationIdle;
}
static void applicationIdle(object sender, EventArgs e)
{
Memory.ReEvaluateWorkingSet();
}
}
}
نحوه استفاده از متد ReEvaluateWorkingSet در برنامههای WPF :
فایل App.xaml.cs را یافته و سپس در روال رویدادگردان Deactivated برنامه، متد ReEvaluateWorkingSet را فراخوانی کنید:
//App.xaml.cs
public App()
{
this.Deactivated += appDeactivated;
}
void appDeactivated(object sender, EventArgs e)
{
Memory.ReEvaluateWorkingSet();
}
تاثیر آن هم قابل ملاحظه است (حداقل از لحاظ روانی!). تست کنید!