مطالب
آموزش فایرباگ - #2 - تب Console
در قسمت قبلی با تب‌های فایرباگ آشنا شدیم . در این قسمت و قسمت‌های بعدی ، با هر تب به صورت کامل آشنا خواهیم شد .

در این قسمت با تب Console  آشنا خواهیم شد .
در قسمت قبل در مورد این تب گفتیم :

در این تب دو بخش وجود دارد :
در بخش Log هشدار‌ها ، پیغام‌ها ، درخواست‌های XHR و ... نمایش داده می‌شوند .
بخش دیگر هم که در سمت راست قرار دارد ، مخصوص اجرای کدهای جاوا اسکریپت می‌باشد .

 

پس یک قسمت داریم برای نوشتن کدهای جاوا اسکریپت و یک قسمت هم برای مشاهده‌ی رویداد‌ها .

اکنون 2 سوال مطرح می‌شود :
  1. برای اجرای یک کد در حالت معمول چگونه عمل می‌کنیم ؟
    پاسخ : کدهای مورد نظر را بین تگ باز و بسته‌ی script قرار می‌دهیم و صفحه مورد نظر را در مرورگر باز می‌کنیم و مرورگر کد را اجرا می‌کند .
  2. در صورتی که کدهای ما خطا داشته باشند ، چگونه خطایابی می‌کنیم ؟
    پاسخ : در بین کدهای نوشته شده چند alert قرار می‌دهیم و سعی می‌کنیم مشکل را پیدا کنیم .

همین 2 سوال اهمیت و قدرت فایرباگ و قسمت Console آن را برای ما آشکار می‌کند ، زیرا ما می‌توانیم بدون Reload کردن صفحه ، کدهایمان را اجرا و نتیجه را مشاهده کنیم یا بوسیله توابع موجود خیلی ساده کدهایمان را خطایابی کنیم .



چگونه کدهای نوشته شده را سریع اجرا کنم ؟ کلید میانبر ( HotKey ) اجرای کد‌ها چیست ؟
پاسخ : فشردن کلید CTRL + Enter

امتحان کنید :

کد زیر را در بخش کدنویسی تایپ کنید و بوسیله کلید میانبر گفته شده ، آن را اجرا کنید .
document.getElementsByTagName("div");
 
تصویر ذیل نتیجه اجرای کدی هست که اجرا کردیم .



چند نکته :
  • قسمت هایی که با رنگ قرمز و یک دات ( . ) بعد از نام تگ قرار گرفته اند ، کلاس‌های CSS ای هستند که المنت دارد .
  • قسمت هایی که با رنگ آبی تیره و یک شارپ ( # ) بعد از نام تگ قرار گرفته اند ، ID تگ‌ها هستند .
  • قسمت هایی که کمرنگ هستند ، المنت هایی هستند که در صفحه قابل نمایش نیستند .
  • اگر یک تگ چند کلاس داشته باشد ، فقط اولین کلاس نمایش داده می‌شود .


دکمه‌ها و حالت‌های نمایش بخش کد نویسی

4 دکمه در قسمت کدنویسی وجود دارد :
  1. Run : اجرای کد
  2. Clear : خالی کردن بخش کد نویسی
  3. Copy : کپی کردن کد موجود در بخش کد نویسی در حافظه
  4. History : کدهای نوشته شده در نشست ( Session ) فعلی مرورگر

بخش کد نویسی می‌تواند به 2 شکل نمایش داده شود :
  1. بصورت جعبه چند خطی ( Command Editor )
  2. بصورت تک خطی ( Command Line )
با دکمه‌ی قرمز رنگ موجود در پایین - چپ می‌توانید بین این دو حالت سوئیچ کنید .


9 دکمه هم در بالای بخش log وجود دارد ( به ترتیب از چپ به راست ) :


  • دکمه ای با عنوان “Break On All Errors” . زمانی فعال شود ، در اولین اجرای یک کد از داخل صفحه ، به تب Script منتقل می‌شود و در خطی که کد در حال اجرا است توقف می‌کند .
  • Clear : بخش log را خالی می‌کند .
  • Persist : فعال بودن این دکمه باعث می‌شود که محتویات بخش Console در بارگزاری مجدد صفحه حفظ شود .
  • Profile : بوسیله این گزینه می‌توانید کدهای اجرایی خود در مدت زمان فعال بودن این دکمه ، تحت نظر بگیرید .
    به این صورت که پس از غیر فعال کردن این دکمه ( کلیک مجدد بروی آن ) می‌توانید تابع‌های اجرا شده ، تعداد فراخوانی آنها ، مدت زمان اجرای هر یک ، میانگین زمان اجرای هر بار یک تابع و ... را مشاهده کنید .
  • 5 دکمه‌ی بعدی هم برای فیلتر کردن Log هستند .


نمایش تعداد خطاهای اتفاق افتاده ، در نوار وضعیت ( Status Bar ) :

اگر در زمان فعال بودن فایرباگ ، در صفحه خطایی رخ دهد ، در نوار وضعیت عدد 1 را نمایش می‌دهد و به ازای هر خطای جدید ، یکی به تعداد خطاها اضافه می‌کند .
البته اگر از ورژن‌های جدید فایرفاکس استفاده می‌کنید ، نوار وضعیت را نخواهید داشت و تعداد خطاها را در کنار آیکون فایرباگ خواهید داشت .


در کنار همه این امکانات ، فایرباگ یک مجموعه کامل از توابع کاربردی برای توسعه جاوا اسکریپت و خطایابی جاوا اسکریپت در اختیار ما می‌گذارد .

چند متد کمکی برای نوشتن Log‌های مختلف در Console :
console.debug('This is a Debug message'); 
console.info('This is an Information'); 
console.warn('This is a Warning message'); 
console.error('This is an Error message');


در قسمت بعدی توابع مربوط به توسعه جاوا اسکریپت ( Command Line API & Console API ) در فایرباگ را بررسی خواهیم کرد .
مطالب
اجرای سرویسهای NodeJS در ASP.NET Core
 این نوشتار در مورد نحوه اجرای سرویس‌های NodeJS در ASP.NET Core می‌باشد؛ زیرا تعداد زیادی از Package‌های سورس باز و با کیفیت بالا به فرم Node package manager یا به اصطلاح NPM موجود و قابل دریافت می‌باشند. NPM بزرگترین مخزن دنیا از لحاظ وجود بسته‌های نرم افزاری سورس باز است. به همین جهت بسته Microsoft.AspNetCore.NodeServices، جهت استفاده از این بسته‌ها در برنامه‌های ASP.NET Core ارائه شده‌است. برای استفاده از سرویسهای Node ابتدا باید ارجاعی را به بسته Microsoft.AspNetCore.NodeServices داشته باشید. برای این منظور میتوانید از دستور dotnet add package Microsoft.AspNetCore.NodeServices استفاده نمایید. البته اگر از آخرین نسخه NET Core. استفاده میکنید، لزومی به ارجاع به بسته فوق نمی‌باشد و به صورت پیشفرض در بسته Microsoft.AspNetCore.All موجود است.
سپس متد ()ConfigurationServices را جهت اضافه کردن میان افزار Node Services به خط لوله درخواستها (request pipeline) ویرایش میکنیم:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddNodeServices();
}

حال میتوانید به وهله‌ای از اینترفیس INodeServices، از طریق تزریق وابستگی‌ها دسترسی داشته باشید. اینترفیس INodeServices یک Api می‌باشد و مشخص می‌کند که کدام قطعه از کد NET. میتواند کد جاوااسکریپتی را که در محیط Node اجرا می‌شود، فراخوانی کند. همچنین میتوانید از خاصیت FromServices برای دریافت وهله‌ای از اینترفیس INodeServices در اکشن خود استفاده نمایید.
public async Task<IActionResult> Add([FromServices] INodeServices nodeServices)
{
    var num1 = 10;
    var num2 = 20;
    var result = await nodeServices.InvokeAsync<int>("AddModule.js", num1, num2);
    ViewData["ResultFromNode"] = $"Result of {num1} + {num2} is {result}";
    return View();
}

سپس کد جاوااسکریپتی متناظر با تابعی را که توسط متد InvokeAsync فراخوانی میشود، به صورت زیر می‌نویسیم:
module.exports = function(callback, num1, num2) { 
  var result = num1 + num2;
  callback(null, result); 
};
دقت کنید که نام فایل جاوااسکریپت باید همنام با پارامتر اول متد InvokeAsync و تعداد پارامترهای تابع باید مساوی با پارامترهایی باشد که به تابع فراخوانی شده جاوااسکریپت ارسال می‌شود.

حال بیاییم مثالی دیگر را مرور کنیم. میخواهیم از صفحه وب درخواستی، عکسی را تهیه کنیم. بدین منظور از کتابخانه url-to-image استفاده میکنیم. برای نصب آن دستور npm install --save url-to-image را در خط فرمان تایپ میکنیم.

بعد از اتمام نصب این بسته، متدی را برای دریافت اطلاعات ارسالی این کتابخانه تدارک میبینیم.

 [HttpPost]
        public async Task<IActionResult> GenerateUrlPreview([FromServices] INodeServices nodeServices)
        {
            var url = Request.Form["Url"].ToString();
            var fileName = System.IO.Path.ChangeExtension(DateTime.UtcNow.Ticks.ToString(), "jpeg");
            var file = await nodeServices.InvokeAsync<string>("UrlPreviewModule.js", url, System.IO.Path.Combine("PreviewImages", fileName));
            return Content($"/Home/Download?img={fileName}");
        }

        public IActionResult Download()
        {
            var image = Request.Query["img"].ToString();
            var fileName = System.IO.Path.Combine("PreviewImages", image);
            var isExists = System.IO.File.Exists(fileName);

            if (isExists)
            {
                Response.Headers.Add($"Content-Disposition", "attachment; filename=\"" + image + "\"");
                var bytes = System.IO.File.ReadAllBytes(fileName);
                return File(bytes, "image/jpeg");
            }
            else
            {
                return NotFound();
            }
        }

سپس متد UrlPreviewModule.js را به صورت زیر مینویسیم:

var urlToImage = require('url-to-image');
module.exports = function (callback, url, imageName) {
    urlToImage(url, imageName).then(function () {
        callback(null, imageName);
    }).catch(function (err) {
        callback(err, imageName);
    });
};


سرویس‌های Node به توسعه دهندگان ASP.NET Core امکان استفاده از اکوسیستم NPM را که دارای قابلیتهای فراوانی می‌باشد، میدهد. 

نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
امکان تبدیل رخدادهای توکار مرورگرها به دایرکتیوهای Blazor در Blazor6x

یکسری دایرکتیو مانند onclick@ و امثال آن، از پیش در Blazor تعریف شده‌اند که امکان مدیریت رویدادهای جاوااسکریپتی را در کدهای سی‌شارپ میسر می‌کنند. اما تعداد این‌ها زیاد نیست. برای مثال تعداد رویدادهای قابل تعریف و پشتیبانی شده‌ی توسط مرورگرها قابل ملاحظه‌است. در Blazor 6x روشی جهت دسترسی ساده‌تر به این رویدادها ارائه شده‌است که شامل این مراحل است. برای نمونه فرض کنید می‌خواهیم به رویداد paste مرورگر دسترسی پیدا کنیم و یک دایرکتیو سفارشی oncustompaste@ را برای آن تهیه کنیم:
<input @oncustompaste="HandleCustomPaste" />
برای اینکار در ابتدا قطعه کد زیر را پس از blazor.webassembly.js در فایل index.html ثبت می‌کنیم (یا می‌توان از روش export function afterStarted که در بالا عنوان شد هم استفاده کرد):
<script> 
    Blazor.registerCustomEventType('custompaste', { 
        browserEventName: 'paste', 
        createEventArgs: event => { 
            // This example only deals with pasting text, but you could use arbitrary JavaScript APIs 
            // to deal with users pasting other types of data, such as images 
            return { 
                eventTimestamp: new Date(), 
                pastedData: event.clipboardData.getData('text') 
            }; 
        } 
    }); 
</script>
در اینجا برای رویداد paste مرورگر، تعدادی آرگومان تهیه شده و بازگشت داده می‌شود. آرگومان اول در اینجا یک مقدار اختیاری و نمایشی‌است و آرگومان دوم به شیء رویداد paste، دسترسی یافته و متن آن‌را بازگشت می‌دهد.
پس از اینکار، معادل دو پارامتر بازگشت داده شده را به صورت زیر در کدهای سی‌شارپ تهیه می‌کنیم:
namespace BlazorCustomEventArgs.CustomEvents 
{ 
    [EventHandler("oncustompaste", typeof(CustomPasteEventArgs), enableStopPropagation: true, enablePreventDefault: true)] 
    public static class EventHandlers 
    { 
        // This static class doesn't need to contain any members. It's just a place where we can put 
        // [EventHandler] attributes to configure event types on the Razor compiler. This affects the 
        // compiler output as well as code completions in the editor. 
    } 
 
    public class CustomPasteEventArgs : EventArgs 
    { 
        // Data for these properties will be supplied by custom JavaScript logic 
        public DateTime EventTimestamp { get; set; } 
        public string PastedData { get; set; } 
    } 
}
ابتدا از EventArgs ارث‌بری شده و معادل تاریخ و متن بازگشت داده شده، تبدیل به یک EventArgs سفارشی می‌شود. سپس نوع آن، به ویژگی EventHandler ای که بالای یک کلاس استاتیک خالی قرار گرفته شده، ارسال می‌شود. اینکار صرفا جهت اطلاع کامپایلر صورت می‌گیرد.
یک نکته: در اینجا نام oncustompaste به همان نام custompaste کدهای جاوااسکریپتی اشاره می‌کند. نام تعریف شده‌ی در قسمت سی‌شارپ، یک on در ابتدا اضافه‌تر دارد. اینکار سبب می‌شود که اکنون بتوان یک رویدادگردان oncustompaste@ سفارشی را که قابل مدیریت در کدهای سی‌شارپ است، داشت:
@page "/"

<p>Try pasting into the following text box:</p>
<input @oncustompaste="HandleCustomPaste" />
<p>@message</p>

@code {
    string message;
    void HandleCustomPaste(CustomPasteEventArgs eventArgs)
    {
        message = $"At {eventArgs.EventTimestamp.ToShortTimeString()}, you pasted: {eventArgs.PastedData}";
    }
}
مطالب
breeze js به همراه ایجاد سایت آگهی قسمت دوم
نصب: پکیج‌های متنوعی از breeze وجود دارند. برای ما بسته‌ی زیر بهترین انتخاب می‌باشد. با نصب پکیج زیر، breeze در سمت سرور و کلاینت، به همراه ASP.NET Web API 2.2 and Entity Framework 6 نصب می‌شود:
Install-Package Breeze.WebApi2.EF6
بعد از نصب، دو فایل جاوا اسکریپتی به پروژه اضافه میشوند: breeze.debug.js  فایل اصلی breeze می‌باشد که از Backbone و Knockout پشتیبانی می‌کند و breeze.min.js که فایل فشرده شده breeze.debug میباشد. برای بهتر کار کردن با angularjs و breezejs، کتابخانه‌ی Breeze.Angular را  نصب نمایید. یکی از مواردی که این سرویس برای ما انجام می‌دهد،interceptor ایی را برای درخواست‌های http فعال می‌کند. یکی از موارد استفاده‌ی آن، ارسال token امنیتی، قبل از درخواست‌های breeze به کنترلر میباشد:
var instance = breeze.config.initializeAdapterInstance("ajax", "angular");
instance.setHttp($http);
Install-Package Breeze.Angular
 قلب تپنده‌ی breezejs در کلاینت EntityManager است که نقش data context را در کلاینت، بازی می‌کند. به برخی از خصوصیات آن می‌پردازیم:
  var manager = new breeze.EntityManager({  
  dataService: dataService,          
  metadataStore: metadataStore,                     
  saveOptions: new breeze.SaveOptions({    allowConcurrentSaves: true, tag: [{}] })   
                 });

var dataService = new breeze.DataService({  
serviceName: "/breeze/"+ "Automobile",             
hasServerMetadata: false,
namingConvention: breeze.NamingConvention.camelCase        
});
var metadataStore = new breeze.MetadataStore({});

- serviceName: نام سرویس دهنده یا کنترلر سمت سرور میباشد. درمورد کنترلر سمت سرور کمی جلوتر بحث می‌کنیم.
- metadataStore: اطلاعاتی را در مورد تمام آبجکت‌ها (جداول دیتابیس) می‌دهد. مثل نام فیلدها، نوع فیلدها و...
برای کار با متادیتا دو راه وجود دارد:
1- متا دیتا را خودتان در سمت کلاینت ایجاد نمایید:
var myMetadataStore = new breeze.MetadataStore();
myMetadataStore.addEntityType({...});
یا برای اضافه کردن فیلد شهر به جدول customer:
 var customer = function () {
                    this.City = "";
                };
myMetadataStore.registerEntityTypeCtor("Customer", customer);
2- اطلاعات  را از سرور دریافت  نمایید. در این صورت  کنترلر شما باید دارای متد Metadata باشد. بنابراین کنترلی را در سرور به نام Automobile و با محتویات زیر ایجاد نمایید. همانطور که مشاهده می‌کنید، این کنترلر از ApiController مشتق شده است که تفاوت خاصی با Api‌‌های دیگر ندارد و تنها به BreezeController مزین شده است. این attribute به NET WebApi  کمک میکند که فیلترینگ و مرتب سازی با فرمت oData را فراهم کند و همچنین درک صحیح فرمت json را نیز به کنترلر می‌دهد.
EFContextProvider: کامپوننتی که تعامل بین کنترلر breeze با Entity Framework را ساده‌تر می‌کند و در واقع یک  wrapper بر روی دیتاکانتکس یا آبجکت کانتکس می‌باشد. یکی از وظایف آن  ارسال متا دیتا، برای کلاینت‌های breeze است.
[BreezeController]
public class AutomobileController : ApiController
    {
        readonly EFContextProvider<ApplicationDbContext> _contextProvider =
        new EFContextProvider<ApplicationDbContext>();
        [HttpGet]
        public string Metadata()
        {
            return _contextProvider.Metadata();
        }
        [HttpGet]
        public IQueryable<Customer> Customers() {
           return _contextProvider.Context.Customers;
        }

        [System.Web.Http.HttpPost]
        public SaveResult SaveChanges(JObject saveBundle)
        {
           _contextProvider.BeforeSaveEntitiesDelegate = BeforeSaveEntities;
           _contextProvider.AfterSaveEntitiesDelegate = afterSaveEntities;
            return _contextProvider.SaveChanges(saveBundle);
        }
protected Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
        {
        }
private void afterSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap, List<KeyMapping> keyMappings)
        {
        }
    }
در اینجا متدی مانند Customers، از طریق کلاینت‌های breeze قابل دسترسی می‌باشد.

- saveOptions: نحوه‌ی چگونگی برخورد با ذخیره کردن اطلاعات را مشخص می‌کند. با ذخیره سازی تغییرات، متد SaveChanges سمت سرور فراخوانی می‌شود. در breeze می‌توان به قبل و بعد از ذخیره سازی اطلاعات دسترسی داشت. یکی از موارد رایج کاربرد آن، اعمال چک کردن دسترسی‌ها، قبل از ذخیره سازی می‌باشد.
برای ذخیره سازی تغییرات:
manger.saveChanges().then(function success() {
                    }, function failer(e) {
                    });
برای نادیده گرفتن تغییرات:
manger.rejectChanges()

کوئری:
بعد از تعریف Entity Manger می‌توانیم کوئری خود را اجرا نماییم. کوئری ما شامل گرفتن اطلاعات از جدول Customer، با مرتب سازی بر روی فیلد آیدی می‌باشد و با اجرا کردن کوئری می‌توانیم موفقیت یا عدم موفقیت آن‌را بررسی نماییم. 
   var query = breeze.EntityQuery
            .from("Customer")   
            .orderBy("Id");
   var result= manager.executeQuery(query);
   result.then(querySucceeded)
    .fail(queryFailed);

   query = query.where("Id", "==", 1)
با نوشتن Predicate تکی یا ترکیب آنها نیز می‌توان شرط‌های پیچیده‌تری را ایجاد کرد:
var predicate = new breeze.Predicate("Id", "==", false);
query = query.where(predicate)

var p1 = new breeze.Predicate("IsArchived", "==", false);
var p2 = breeze.Predicate("IsDone", "==", false); 
var predicate = p1.and(p2);
query = query.where(predicate).orderBy("Id")  
در اینجا خروجی مشابه زیر برای کنترلر ارسال میشود:
?$filter=IsArchived eq false&IsDone eq false  &$orderby=Id

اعتبارسنجی
:اعتبارسنجی در breeze، هم در سمت کلاینت و هم در سمت سرور امکان پذیر می‌باشد که در مثالی، در قسمت بعدی، validator سفارشی خودمان را خواهیم ساخت و به entity مورد نظر اعمال خواهیم کرد.
breeze دارای یک سری Validator در سطح پراپرتی‌ها است:
- برای انواع اقسام dataType ها مانند Int,string,..
- برای نیازهای رایجی چون: emailAddress,creditCard,maxLength,phone,regularExpression,required,url 
هم چنین در breeze امکان تغییر دادن اعتبارسنجی‌های پیش فرض نیز وجود دارند. برای مثال برای اینکه در فیلدهای required بتوان متن خالی هم وارد کرد، از دستور زیر می‌توان استفاده کرد:
breeze.Validator.required({ allowEmptyStrings: true });

ردیابی تغییرات
: هر آیتم Entity دارای EntityAspect است که وضعیت آن‌را مشخص می‌کند و می‌تواند یکی از وضعیت‌های Added،Modified،Deleted،Detached،Unchanged باشد. با مشخص کردن حالت هر آیتم، با فراخوانی SaveChanges تغییرات بر روی دیتابیس اعمال می‌گردد.
ایجاد آیتم جدید:
manager.createEntity('Customer', jsonValue);
 ویرایش اطلاعات:
manager.createEntity("Customer", jsonValue, breeze.EntityState.Modified, breeze.MergeStrategy.OverwriteChanges)
 حذف اطلاعات:
manager.createEntity("Customer", item, breeze.EntityState.Deleted)

برای اشنایی بیشتر با امکانات Breeze، قصد داریم یک سایت ایجاد آگهی را راه اندازی کنیم. پیش نیازهای ضروری این بخش typescript ،angularjs ،requirejs هستند. قصد داریم سایتی را برای آگهی‌های خرید و فروش خودرو، مشابه با سایت باما ایجاد نماییم:

امکانات این سایت:
- ثبت نام کاربران 
- ثبت آگهی توسط کاربران 
- ایجاد برچسب‌های آگهی‌ها 
- امتیاز دهی به آگهی‌ها
- جستجوی آگهی‌ها
- و....
ابتدا نصب پکیج‌های زیر 
Install-Package angularjs
Install-Package angularjs.TypeScript.DefinitelyTyped

Install-Package bootstrap
Install-Package bootstrap.TypeScript.DefinitelyTyped

Install-Package jQuery
Install-Package jquery.TypeScript.DefinitelyTyped

Install-Package RequireJS
Install-Package requirejs.TypeScript.DefinitelyTyped

bower install angularAMD

مدلهای برنامه:
ایجاد کلاس BaseEntity 
 public  class BaseEntity
    {
        public int Id { get; set; }
        public bool Status { get; set; }
        public DateTime CreatedDateTime { get; set; }
    }
ایجاد جدول آگهی
    public class Ad : BaseEntity
    {
        public string Title { get; set; }
        public float Price { get; set; }
        public double Rating { get; set; }
        public int? RatingNumber { get; set; }
        public string UserId { get; set; }
        public DateTime ModifieDateTime { get; set; }
        public string Description { get; set; }
        public virtual ICollection<Comment> Comments { get; set; }
        public virtual IdentityUser User { get; set; }
        public virtual ICollection<AdLabel> Labels { get; set; }
        public virtual ICollection<AdMedia> Medias { get; set; }
    }
ایجاد جدول برچسب 
public class Label 
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public int? ParentId { get; set; }
        public virtual Label Parent { get; set; }
        public virtual ICollection<Label> Items { get; set; }
    }
ایجاد جدول مدیا
 public class Media 
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string MimeType { get; set; }
    }
ایجاد جدول واسط برچسب‌های آگهی
  public class AdLabel
    {
        public int Id { get; set; }
        public virtual Ad Ad { get; set; }
        public virtual Label Label { get; set; }
        [Index("IX_AdLabel", 1, IsUnique = true)]
        public int AdId { get; set; }
        [Index("IX_AdLabel", 2, IsUnique = true)]
        public int LabelId { get; set; }
        public string Value { get; set; }
    }
ایجاد جدول واسط مدیا‌های مرتبط با آگهی
 public class AdMedia
    {
        public int Id { get; set; }
        public virtual Ad Ad { get; set; }
        public virtual Media Media { get; set; }
        [Index("IX_AdMedia", 1, IsUnique = true)]
        public int AdId { get; set; }
        [Index("IX_AdMedia", 2, IsUnique = true)]
        public int MediaId { get; set; }
    }
ایجاد جدول کامنت‌ها
  public class Comment : BaseEntity
    {
        public string Body { get; set; }
        public double Rating { get; set; }
        public int? RatingNumber { get; set; }
        public string EntityName { get; set; }
        public string UserId { get; set; }
        public int? ParentId { get; set; }
        public int? AdId { get; set; }
        public virtual Comment Parent { get; set; }
        public virtual Ad Ad { get; set; }
        public virtual ICollection<Comment> Items { get; set; }
        public virtual IdentityUser User { get; set; }
    }
ایجاد جدول اعضاء
public class Customer:BaseEntity
    {
        public string UserId { get; set; }
        public virtual string DisplayName { get; set; }
        public virtual string BirthDay { get; set; }
        public string City { get; set; }
        public string Address { get; set; }
        public int? MediaId { get; set; }
        public bool? NewsLetterSubscription { get; set; }
        public string PhoneNumber { get; set; }
        public virtual IdentityUser User { get; set; }
        public virtual Media Media { get; set; }
    }
ایجاد جدول امتیاز دهی به آگهی‌ها
public class Rating 
    {
      public int Id { get; set; }
       public string UserId { get; set; }
       public Double Rate { get; set; }
       public string EntityName { get; set; }
       public int DestinationId { get; set; }
    }

اضافه کردن مدلهای برنامه به ApplicationDbContext 
 public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }
        public DbSet<Ad> Ads { get; set; }
        public DbSet<AdLabel> AdLabels { get; set; }
        public DbSet<AdMedia> AdMedias { get; set; }
        public DbSet<Comment> Comments { get; set; }
        public DbSet<Label> Labels { get; set; }
        public DbSet<Media> Medias { get; set; }
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }

لود کردن فایل main.js در فایل layout.cshtml ترجیحا در انتهای body
    <script src="~/Scripts/require.js" data-main="/app/main"></script>
RequireJS  کتابخانه‌ی جاوااسکریپتی برای بارگزاری فایل‌ها در صورت نیاز می‌باشد. تنها کاری که ما باید انجام بدهیم این است که کدهای خود را داخل module‌ها قرار دهیم (در فایل‌های جداگانه) و RequireJS در صورت نیاز آنها را load خواهد کرد. همچنین RequireJS وابستگی بین module‌ها را نیز مدیریت می‌کند.

ایجاد فایل main.ts 
path: مسیر فایل‌های جاوا اسکریپتی
shim: وابستگی‌های فایل‌ها(ماژول ها) و export کردن آنها را مشخص می‌کند.
requirejs.config({
    paths: {
        "app": "app",
        "angularAmd":"/Scripts/angularAmd",
        "angular": "/Scripts/angular",
        "bootstrap": "/Scripts/bootstrap",
        "angularRoute": "/Scripts/angular-route",
        "jquery": "/Scripts/jquery-2.2.2",
    },
    waitSeconds: 0,
    shim: {
        "angular": { exports: "angular" },
        "angularRoute": { deps: ["angular"] },
        "bootstrap": { deps: ["jquery"] },
        "app": {
            deps: ["bootstrap","angularRoute"]
        }
    }
});
require(["app"]);

ایجاد فایل app.ts: کارهایی که در فایل app انجام داده‌ایم:
ایجاد کنترلر SecurityCtrl و اعمال آن به تگ body
<body ng-controller="SecurityCtrl">
...
</body>
ایجاد ماژول AdApps و قرار دادن کلاس SecurityCtrl در آن. از این به بعد برای مدیریت بهتر، تمام کدهای خود را درون ماژول‌ها قرار می‌دهیم. 
"use strict";
module AdApps {
    class SecurityCtrl {
        private $scope: Interfaces.IAdvertismentScope;
        constructor($scope: Interfaces.IAdvertismentScope) {
           // security check
      this.$scope = $scope;
        }
    }
 define(["angularAmd", "angular"], (angularAmd, ng) => {
   angularAmd = angularAmd.__proto__;
        var app = ng.module("AngularTypeScript", ['ngRoute']);
        var viewPath = "app/views/";
        var controllerPath = "app/controller/";
        app.config(['$routeProvider', $routeProvider => {
                $routeProvider
                    .when("/", angularAmd.route({
                        templateUrl: viewPath + "home.html",
                        controllerUrl: controllerPath + "home .js"
                    }))
                    .otherwise({ redirectTo: '/' });
            }
        ]);
        app.controller('SecurityCtrl', ['$scope', SecurityCtrl]);
        return angularAmd.bootstrap(app);
 })}
مطالب
Attribute Routing در ASP.NET MVC 5
Routing مکانیزم مسیریابی ASP.NET MVC است، که یک URI را به یک اکشن متد نگاشت می‌کند. MVC 5 نوع جدیدی از مسیر یابی را پشتیبانی میکند که Attribute Routing یا مسیریابی نشانه ای نام دارد. همانطور که از نامش پیداست، مسیریابی نشانه ای از Attribute‌ها برای این امر استفاده میکند. این روش به شما کنترل بیشتری روی URI‌های اپلیکیشن تان می‌دهد.
مدل قبلی مسیریابی (conventional-routing) هنوز کاملا پشتیبانی می‌شود. در واقع می‌توانید هر دو تکنیک را بعنوان مکمل یکدیگر در یک پروژه استفاده کنید.
در این پست قابلیت‌ها و گزینه‌های اساسی مسیریابی نشانه ای را بررسی میکنیم.
  • چرا مسیریابی نشانه ای؟
  • فعال سازی مسیریابی نشانه ای
  • پارامتر‌های اختیاری URI و مقادیر پیش فرض
  • پیشوند مسیر ها
  • مسیر پیش فرض
  • محدودیت‌های مسیر ها
      • محدودیت‌های سفارشی
  • نام مسیر ها
  • ناحیه‌ها (Areas)


چرا مسیریابی نشانه ای

برای مثال یک وب سایت تجارت آنلاین بهینه شده اجتماعی، می‌تواند مسیرهایی مانند لیست زیر داشته باشد:
  • {productId:int}/{productTitle}
نگاشت می‌شود به: (ProductsController.Show(int id
  • {username}
نگاشت می‌شود به: (ProfilesController.Show(string username
  • {username}/catalogs/{catalogId:int}/{catalogTitle}
نگاشت می‌شود به: (CatalogsController.Show(string username, int catalogId
در نسخه قبلی ASP.NET MVC، قوانین مسیریابی در فایل RouteConfig.cs تعریف می‌شدند، و اشاره به اکشن‌های کنترلرها به نحو زیر انجام می‌شد:
routes.MapRoute(
    name: "ProductPage",
    url: "{productId}/{productTitle}",
    defaults: new { controller = "Products", action = "Show" },
    constraints: new { productId = "\\d+" }
);
هنگامی که قوانین مسیریابی در کنار اکشن متدها تعریف می‌شوند، یعنی در یک فایل سورس و نه در یک کلاس پیکربندی خارجی، درک و فهم نگاشت URI‌ها به اکشن‌ها واضح‌تر و راحت می‌شود. تعریف مسیر قبلی، می‌تواند توسط یک attribute ساده بدین صورت نگاشت شود:
[Route("{productId:int}/{productTitle}")]
public ActionResult Show(int productId) { ... }

فعال سازی Attribute Routing

برای فعال سازی مسیریابی نشانه ای، متد MapMvcAttributeRoutes را هنگام پیکربندی فراخوانی کنید.
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
        routes.MapMvcAttributeRoutes();
    }
}
همچنین می‌توانید مدل قبلی مسیریابی را با تکنیک جدید تلفیق کنید.
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
    routes.MapMvcAttributeRoutes();
 
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

پارامترهای اختیاری URI و مقادیر پیش فرض

می توانید با اضافه کردن یک علامت سوال به پارامترهای مسیریابی، آنها را optional یا اختیاری کنید. برای تعیین مقدار پیش فرض هم از فرمت parameter=value استفاده می‌کنید.
public class BooksController : Controller
{
    // eg: /books
    // eg: /books/1430210079
    [Route("books/{isbn?}")]
    public ActionResult View(string isbn)
    {
        if (!String.IsNullOrEmpty(isbn))
        {
            return View("OneBook", GetBook(isbn));
        }
        return View("AllBooks", GetBooks());
    }
 
    // eg: /books/lang
    // eg: /books/lang/en
    // eg: /books/lang/he
    [Route("books/lang/{lang=en}")]
    public ActionResult ViewByLanguage(string lang)
    {
        return View("OneBook", GetBooksByLanguage(lang));
    }
}
در این مثال، هر دو مسیر books/ و books/1430210079/ به اکشن متد "View" نگاشت می‌شوند، مسیر اول تمام کتاب‌ها را لیست میکند، و مسیر دوم جزئیات کتابی مشخص را لیست می‌کند. هر دو مسیر books/lang/ و books/lang/en/ به یک شکل نگاشت می‌شوند، چرا که مقدار پیش فرض این پارامتر en تعریف شده.



پیشوند مسیرها (Route Prefixes)

برخی اوقات، تمام مسیرها در یک کنترلر با یک پیشوند شروع می‌شوند. بعنوان مثال:
public class ReviewsController : Controller
{
    // eg: /reviews
    [Route("reviews")]
    public ActionResult Index() { ... }
    // eg: /reviews/5
    [Route("reviews/{reviewId}")]
    public ActionResult Show(int reviewId) { ... }
    // eg: /reviews/5/edit
    [Route("reviews/{reviewId}/edit")]
    public ActionResult Edit(int reviewId) { ... }
}
همچنین می‌توانید با استفاده از خاصیت [RoutePrefix] یک پیشوند عمومی برای کل کنترلر تعریف کنید:
[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
    // eg.: /reviews
    [Route]
    public ActionResult Index() { ... }
    // eg.: /reviews/5
    [Route("{reviewId}")]
    public ActionResult Show(int reviewId) { ... }
    // eg.: /reviews/5/edit
    [Route("{reviewId}/edit")]
    public ActionResult Edit(int reviewId) { ... }
}
در صورت لزوم، می‌توانید برای بازنویسی (override) پیشوند مسیرها از کاراکتر ~ استفاده کنید:
[RoutePrefix("reviews")]
public class ReviewsController : Controller
{
    // eg.: /spotlight-review
    [Route("~/spotlight-review")]
    public ActionResult ShowSpotlight() { ... }
 
    ...
}

مسیر پیش فرض

می توانید خاصیت [Route] را روی کنترلر اعمال کنید، تا اکشن متد را بعنوان یک پارامتر بگیرید. این مسیر سپس روی تمام اکشن متدهای این کنترلر اعمال می‌شود، مگر آنکه یک [Route] بخصوص روی اکشن‌ها تعریف شده باشد.
[RoutePrefix("promotions")]
[Route("{action=index}")]
public class ReviewsController : Controller
{
    // eg.: /promotions
    public ActionResult Index() { ... }
 
    // eg.: /promotions/archive
    public ActionResult Archive() { ... }
 
    // eg.: /promotions/new
    public ActionResult New() { ... }
 
    // eg.: /promotions/edit/5
    [Route("edit/{promoId:int}")]
    public ActionResult Edit(int promoId) { ... }
}

محدودیت‌های مسیر ها

با استفاده از Route Constraints می‌توانید نحوه جفت شدن پارامتر‌ها در قالب مسیریابی را محدود و کنترل کنید. فرمت کلی {parameter:constraint} است. بعنوان مثال:
// eg: /users/5
[Route("users/{id:int}"]
public ActionResult GetUserById(int id) { ... }
 
// eg: users/ken
[Route("users/{name}"]
public ActionResult GetUserByName(string name) { ... }
در اینجا، مسیر اول تنها در صورتی انتخاب می‌شود که قسمت id در URI یک مقدار integer باشد. در غیر اینصورت مسیر دوم انتخاب خواهد شد.
جدول زیر constraint‌ها یا محدودیت هایی که پشتیبانی می‌شوند را لیست می‌کند.
 مثال  توضیحات  محدودیت
 {x:alpha}  کاراکترهای الفبای لاتین را تطبیق (match) می‌دهد (a-z, A-Z).  alpha
 {x:bool}  یک مقدار منطقی را تطبیق می‌دهد.  bool
 {x:datetime}  یک مقدار DateTime را تطبیق می‌دهد.  datetime
 {x:decimal}  یک مقدار پولی را تطبیق می‌دهد.  decimal
 {x:double}  یک مقدار اعشاری 64 بیتی را تطبیق می‌دهد.  double
 {x:float}  یک مقدار اعشاری 32 بیتی را تطبیق می‌دهد.  float
 {x:guid}  یک مقدار GUID را تطبیق می‌دهد.  guid
 {x:int}  یک مقدار 32 بیتی integer را تطبیق می‌دهد.  int
 {(x:length(6}
{(x:length(1,20}
 رشته ای با طول تعیین شده را تطبیق می‌دهد.  length
 {x:long}  یک مقدار 64 بیتی integer را تطبیق می‌دهد.  long
 {(x:max(10}  یک مقدار integer با حداکثر مجاز را تطبیق می‌دهد.  max
 {(x:maxlength(10}  رشته ای با حداکثر طول تعیین شده را تطبیق می‌دهد.  maxlength
 {(x:min(10}  مقداری integer با حداقل مقدار تعیین شده را تطبیق می‌دهد.  min
 {(x:minlength(10}  رشته ای با حداقل طول تعیین شده را تطبیق می‌دهد.  minlength
 {(x:range(10,50}  مقداری integer در بازه تعریف شده را تطبیق می‌دهد.  range
 {(${x:regex(^\d{3}-\d{3}-\d{4}  یک عبارت با قاعده را تطبیق می‌دهد.  regex

توجه کنید که بعضی از constraint ها، مانند "min" آرگومان‌ها را در پرانتز دریافت می‌کنند.
می توانید محدودیت‌های متعددی روی یک پارامتر تعریف کنید، که باید با دونقطه جدا شوند. بعنوان مثال:
// eg: /users/5
// but not /users/10000000000 because it is larger than int.MaxValue,
// and not /users/0 because of the min(1) constraint.
[Route("users/{id:int:min(1)}")]
public ActionResult GetUserById(int id) { ... }
مشخص کردن اختیاری بودن پارامتر ها، باید در آخر لیست constraints تعریف شود:
// eg: /greetings/bye
// and /greetings because of the Optional modifier,
// but not /greetings/see-you-tomorrow because of the maxlength(3) constraint.
[Route("greetings/{message:maxlength(3)?}")]
public ActionResult Greet(string message) { ... }

محدودیت‌های سفارشی

با پیاده سازی قرارداد IRouteConstraint می‌توانید محدودیت‌های سفارشی بسازید. بعنوان مثال، constraint زیر یک پارامتر را به لیستی از مقادیر قابل قبول محدود می‌کند:
public class ValuesConstraint : IRouteConstraint
{
    private readonly string[] validOptions;
    public ValuesConstraint(string options)
    {
        validOptions = options.Split('|');
    }
 
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
        }
        return false;
    }
}
قطعه کد زیر نحوه رجیستر کردن این constraint را نشان می‌دهد:
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
        var constraintsResolver = new DefaultInlineConstraintResolver();
 
        constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
 
        routes.MapMvcAttributeRoutes(constraintsResolver);
    }
}
حالا می‌توانید این محدودیت سفارشی را روی مسیرها اعمال کنید:
public class TemperatureController : Controller
{
    // eg: temp/celsius and /temp/fahrenheit but not /temp/kelvin
    [Route("temp/{scale:values(celsius|fahrenheit)}")]
    public ActionResult Show(string scale)
    {
        return Content("scale is " + scale);
    }
}

نام مسیر ها

می توانید به مسیرها یک نام اختصاص دهید، با این کار تولید URI‌ها هم راحت‌تر می‌شوند. بعنوان مثال برای مسیر زیر:
[Route("menu", Name = "mainmenu")]
public ActionResult MainMenu() { ... }
می‌توانید لینکی با استفاده از Url.RouteUrl تولید کنید:
<a href="@Url.RouteUrl("mainmenu")">Main menu</a>

ناحیه‌ها (Areas)

برای مشخص کردن ناحیه ای که کنترلر به آن تعلق دارد می‌توانید از خاصیت [RouteArea] استفاده کنید. هنگام استفاده از این خاصیت، می‌توانید با خیال راحت کلاس AreaRegistration را از ناحیه مورد نظر حذف کنید.
[RouteArea("Admin")]
[RoutePrefix("menu")]
[Route("{action}")]
public class MenuController : Controller
{
    // eg: /admin/menu/login
    public ActionResult Login() { ... }
 
    // eg: /admin/menu/show-options
    [Route("show-options")]
    public ActionResult Options() { ... }
 
    // eg: /stats
    [Route("~/stats")]
    public ActionResult Stats() { ... }
}
با این کنترلر، فراخوانی تولید لینک زیر، رشته "Admin/menu/show-options/" را بدست میدهد:
Url.Action("Options", "Menu", new { Area = "Admin" })
به منظور تعریف یک پیشوند سفارشی برای یک ناحیه، که با نام خود ناحیه مورد نظر متفاوت است می‌توانید از پارامتر AreaPrefix استفاده کنید. بعنوان مثال:
[RouteArea("BackOffice", AreaPrefix = "back-office")]
اگر از ناحیه‌ها هم بصورت مسیریابی نشانه ای، و هم بصورت متداول (که با کلاس‌های AreaRegistration پیکربندی می‌شوند) استفاده می‌کنید باید مطمئن شوید که رجیستر کردن نواحی اپلیکیشن پس از مسیریابی نشانه ای پیکربندی می‌شود. به هر حال رجیستر کردن ناحیه‌ها پیش از تنظیم مسیرها بصورت متداول باید صورت گیرد. دلیل آن هم مشخص است، برای اینکه درخواست‌های ورودی بدرستی با مسیرهای تعریف شده تطبیق داده شوند، باید ابتدا attribute routes، سپس area registration و در آخر default route رجیستر شوند. بعنوان مثال:
public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
    routes.MapMvcAttributeRoutes();
 
    AreaRegistration.RegisterAllAreas();
 
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

مطالب
اختصاصی کردن Razor برای #C در MVC با استفاده از Extension Method
در این مقاله ما میخواهیم RazorViewEngine را با استفاده از یک Extension Method  به گونه ای تنظیم کنیم که فقط به دنبال View‌هایی که مربوط به C# هستند بگردد. در ابتدای مقاله توضیح خلاصه ای درباره Extension Method خواهیم داشت و سپس نحوه اختصاصی کردن Razor برای C# را خواهیم دید.
Extension Method‌ها بسیار کارآمد هستند و  نحوه ایجاد و استفاده از آنها بسیار راحت است. به گونه ای که میتوان آنها را حتی برای کلاس‌های از قبل تعریف شده .Net نیز ایجاد کرد و در سرتاسر برنامه از آن استفاده کرد.
با مثالی ساده نحوه ایجاد و استفاده از Extension Method را میبینیم. در این مثال ما سعی داریم متدی برای کلاس string در .Net بنویسیم که دو رشته را به هم بچسباند.
1. ابتدا کلاسی در دایرکتوری دلخواه ایجاد میکنیم.
namespace ApplicationTest01.Utilities
{
    public class StringHelper
    {
    }
}
2. سپس متد مورد نظر را مینویسیم:
namespace ApplicationTest01.Utilities
{
    public class StringHelper
    {
        public string StringConcatenate(string firstPhrase, string secondPhrase)
        {
            return firstPhrase + secondPhrase;
        }
    }
}
3. کلاس و متدی که در آن تعریف میکنیم بایستی public و static باشند. namespace کلاس را نیز به namespace کلاس string در .Net (یعنی System) تغییر میدهیم.
namespace System
{
    public static class StringHelper
    {
        public static string StringConcatenate(string firstPhrase, string secondPhrase)
        {
            return firstPhrase + secondPhrase;
        }
    }
}
4. در Extension Method‌ها ورودی اول تابع به پارامتری اشاره دارد که قرار است هنگام استفاده از آن (یا صدا زدن آن) متد، عملیات مورد نظر را روی آن اجرا کند. به همین جهت عبارت this را به پارامتر ورودی اول تابع میدهیم.
namespace System
{
    public static class StringHelper
    {
        public static string StringConcatenate(this string firstPhrase, string secondPhrase)
        {
            return firstPhrase + secondPhrase;
        }
    }
}
برای استفاده از این متد کافیست پس از یک عبارت string متد را فراخوانی کنیم:
        public ActionResult Index()
        {
            "1234".StringConcatenate("567");
            string s1 = "dotnet";
            string s2 = "tips";
            s1.StringConcatenate(s2);
            return View();
        }
همانطور که میبینید در ظاهر تابع فقط یک ورودی دارد ولی ما دو ورودی برای آن در نظر گرفتیم. در واقع ورودی اول تابع قبل از "." (دات) آمده است و عبارت this به آن اشاره دارد.
برای اختصاصی کردن RazorViewEngine برای C#، مشابه روند فوق یک Extension Method ایجاد میکنیم که namespace کلاس آن با namespace کلاس RazorViewEngine (یعنی System.Web.Mvc ) یکی باشد. خروجی Extension Method ما از نوع  RazorViewEngine میباشد:
namespace System.Web.Mvc
{
    public static class EngineFilter
    {
        public static RazorViewEngine DisableVbhtml(this RazorViewEngine engine)
        {
            return engine;
        }
    }
}
از آن جایی که کلاس RazorViewEngine برای شناسایی view‌ها شامل Peroperty هایی از جنس []string می‌باشد، ابتدا متدی ساده به نام FilterOutVbhtml برای فیلتر کردن string‌های حاوی عبارت "vbhtml" مینویسیم.
namespace System.Web.Mvc
{
    public static class EngineFilter
    {
        public static RazorViewEngine DisableVbhtml(this RazorViewEngine engine)
        {
            return engine;
        }
 
        private static string[] FilterOutVbhtml(string[] source)
        {
            return source.Where(s => !s.Contains("vbhtml")).ToArray();
        }
    }
}
در ادامه، در بدنه متد DisableVbhtml پروپرتی‌های RazorViewEngine را فرا خوانی کرده و با استفاده از متد FilterOutVbhtml آنها را فیلتر میکنیم.
namespace System.Web.Mvc
{
    public static class EngineFilter
    {
        public static RazorViewEngine DisableVbhtml(this RazorViewEngine engine)
        {
            engine.AreaViewLocationFormats = FilterOutVbhtml(engine.AreaViewLocationFormats);
            engine.AreaMasterLocationFormats = FilterOutVbhtml(engine.AreaMasterLocationFormats);
            engine.AreaPartialViewLocationFormats = FilterOutVbhtml(engine.AreaPartialViewLocationFormats);
            engine.ViewLocationFormats = FilterOutVbhtml(engine.ViewLocationFormats);
            engine.MasterLocationFormats = FilterOutVbhtml(engine.MasterLocationFormats);
            engine.PartialViewLocationFormats = FilterOutVbhtml(engine.PartialViewLocationFormats);
            engine.FileExtensions = FilterOutVbhtml(engine.FileExtensions);
            return engine;
        }
 
        private static string[] FilterOutVbhtml(string[] source)
        {
            return source.Where(s => !s.Contains("vbhtml")).ToArray();
        }
    }
}
سپس در فایل Global.asax در Application_Start یکبار ViewEngine‌ها را حذف میکنیم -Engine مربوط به aspx به صورت پیش فرض فعال میباشد- و سپس RazorViewEngine را به همراه فراخوانی Extension Method خودمان به ViewEngine‌ها اضافه میکنیم.
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine().DisableVbhtml());
امیدوارم مطالب فوق برای شما مفید و کارآمد باشد.
منبع: stackoverflow.com
نظرات مطالب
ساختار پروژه های Angular
زمانی که تعداد فایل‌ها و دایرکتوری‌ها در پروژه زیاد می‌شود(البته این جزء جدانشدنی پروژه‌های مقیاس بزرگ است) برای جلوگیری از لود یک باره کنترلرها و دایرکتیوها، بهتر از lazy loading برای لود فایل‌های مورد نیاز استفاده شود.  متاسفانه Angular به صورت رسمی از lazy loading پشتیبانی نمی‌کند اما با کمی تغییر در ساختار و استفاده از کتابخانه‌های جانبی مثل requireJs یا ScriptJs می‌توان به این مهم دست یافت.
(با عنوان این مطلب که قصد داشتم این مورد را طی یک پست جداگانه بررسی کنم)
برای مثال:
ابتدا ماژول app خود را به این شکل تنظیم کنید:
(function()
{
    var app = angular.module('app', []);

    app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide)
    {
        app.controllerProvider = $controllerProvider;
        app.compileProvider    = $compileProvider;
        app.routeProvider      = $routeProvider;
        app.filterProvider     = $filterProvider;
        app.provide            = $provide;

    });
})();

با استفاده از سرویس controllerProvider$ می‌توان چرخه ساخت کنترلر را به دست گرفت. هم چنین سرویس compileProvider$ برای نمونه سازی دایرکتیو‌ها و filterProvider$ برای فیلتر‌ها استفاده می‌شوند.
ساخت کنترلر‌ها و دایرکتیو‌ها نیز به صورت زیر انجام خواهد شد:
angular.module('app').controllerProvider.resgister('SomeLazyController', function($scope)
{
    $scope.key = '...';
});
و هم چنین یک نمونه از ساخت directive
$compileProvider.directive('SomeLazyDirective', function()
{
    return {
        restrict: 'A',
        templateUrl: 'templates/some-lazy-directive.html'
    }
})

فقط کافیست در هنگام پیاده سازی routing (که در این مقاله شرح داده شده است) نوع بارگذاری کنترلرها و دایرکتیو و ... را به صورت lazy انجام دهید :
$routeProvider.when('/about', {templateUrl:'views/about.html', resolve:{deps:function($q, $rootScope)
{
    var deferred = $q.defer();
    var dependencies =
    [
        'controllers/AboutViewController.js',
        'directives/some-directive.js'
    ];
 
    //*نکته اول 
    $script(dependencies, function()
    {
        // *نکته دوم
        $rootScope.$apply(function()
        {
            deferred.resolve();
        });
    });
 
    return deferred.promise;
}}})
*نکته اول: تمام وابستگی‌ها توسط scriptJs مدیریت می‌شوند.
*نکته دوم: تمام وابستگی‌ها مروبط به این scope بعد از فراخوانی تابع deffered.resolved بارگذاری خواهند شد.
نقطه شروع برنامه نیز به صورت زیر است:
$script(['appModule.js'], function()
{
    angular.bootstrap(document, ['app'])
});
angular.bootstrap
نظرات مطالب
تغییر عملکرد و یا ردیابی توابع ویندوز با استفاده از Hookهای دات نتی
الان توی صفحه زیر
http://www.codeproject.com/Articles/27637/EasyHook-The-reinvention-of-Windows-API-hooking
بالای خط
 a deep look under a hook
نوشته برای رهگیری اکسپلورر اونو در کامنت صدا بزنید. گفته میاد با صدازدن تابع "createfileW" توابع و پارامترای فایل اجرایی مورد نظر رو رهگیری میکنه
نظرات مطالب
استخراج اطلاعات از صفحات وب با کمک HtmlAgilityPack
- شما باید از طریق نیوگت با دستور Install-Package HtmlAgilityPack این بسته رو نصب کنید. یا اینکه فایل DLL اون رو از سایتش دریافت و به ارجاعات پروژه اضافه کنید.
- کدهای کلاس Crawler چند کامنت بالاتر ارسال شدن. تابع GetXHtmlFromUri که ملاحظه می‌کنید.
- مواردی مانند Birthday, LargePhotoUri یک سری متغیر هستند که از طرف نویسنده مقاله تعریف شدن. مهم نیستند. حذفشون کنید.
- یک مثال دیگر در مورد استفاده از کتابخانه HtmlAgilityPack با کد قابل دریافت.
نظرات مطالب
فعال سازی قسمت ارسال فایل و تصویر ویرایشگر آنلاین RedActor در ASP.NET MVC
من چیزی رو از فایل css آن حذف نکردم. فقط یک tahoma به اول تعاریف فونت‌های آن اضافه کردم.
به علاوه باید دقت داشت که تمام این ادیتورها در یک iframe بارگذاری می‌شوند. یعنی در حین نمایش، body آن‌ها تابع css خودشون است و نه css سایت. به همین جهت نیاز به کمی تغییر دارند:
body{
font-family:Tahoma,sans-serif;
font-size:9pt;
margin:0;padding:0;
overflow-x:hidden;
background:#fff;
direction:rtl
}