مطالب
کنترلرهای تو در تو در angularJs و نحوه ارث بری متدها و property ها در آن - بخش دوم
در بخش قبلی به چگونگی ساخت کنترلرهای تو در تو در AngularJs پرداختیم. همچنین بررسی نمودیم که propertyهای تعریف شده در کنترلر ما چگونه قابل استفاده توسط کنترلر فرزند میباشند.
حال روشی دیگر را برای ارث بری تابع‌ها و propertyها، در کنترلر‌های تو در تو معرفی می‌نماییم. لازم به ذکر است که سورس پروژه را می‌توانید از لینک زیر دریافت نمایید:
 کد جاوااسکریپت زیر سه کنترلر تو در تو را پیاده سازی می‌کند:
var firstControllerObj = function ($scope) {
    // Initialize the model object
    $scope.firstModelObj = {
        firstName: "John"
    };
};

var secondControllerObj = function ($scope) {
    // Initialize the model object
    $scope.secondModelObj = {
        lastName: "Doe"
    };

    // Define utility functions
    $scope.getFullName = function () {
        return $scope.firstModelObj.firstName + " " +
          $scope.secondModelObj.lastName;
    };
};

var thirdControllerObj = function ($scope) {
    // Initialize the model object
    $scope.thirdModelObj = {
        middleName: "Al",
        lastName: "Smith"
    };

    // Define utility functions
    $scope.getFullName = function () {
        return $scope.firstModelObj.firstName + " " +
          $scope.thirdModelObj.middleName + " " +
          $scope.thirdModelObj.lastName;
    };
};
کد html زیر هم از کنترلر‌های تعریف شده‌ی در بالا، به صورت زیر استفاده کرده است. نگاهی کوتاه و مختصر به این فایل و سپس به شرح فرآیند ارث بری property‌ها می‌پردازیم:
    <div ng-controller="firstControllerObj">
        <h3>First controller</h3>
        <strong>First name:</strong> {{firstModelObj.firstName}}<br />
        <br />
        <label>Set the first name: <input type="text" ng-model="firstModelObj.firstName" /></label><br />
        <br />
        
        
        <div ng-controller="secondControllerObj">
            <h3>Second controller (inside First)</h3>
            <strong>First name (from First):</strong> {{firstModelObj.firstName}}<br />
            <strong>Last name (from Second):</strong> {{secondModelObj.lastName}}<br />
            <strong>Full name:</strong> {{getFullName()}}<br />
            <br />
            <label>Set the first name: <input type="text" ng-model="firstModelObj.firstName" /></label><br />
            <label>Set the last name: <input type="text" ng-model="secondModelObj.lastName" /></label><br />
            <br />
            

            <div ng-controller="thirdControllerObj">
                <h3>Third controller (inside Second and First)</h3>
                <strong>First name (from First):</strong> {{firstModelObj.firstName}}<br />
                <strong>Middle name (from Third):</strong> {{thirdModelObj.middleName}}<br />
                <strong>Last name (from Second):</strong> {{secondModelObj.lastName}}<br />
                <strong>Last name (from Third):</strong> {{thirdModelObj.lastName}}<br />
                <strong>Full name (redefined in Third):</strong> {{getFullName()}}<br />
                <br />
                <label>Set the first name: <input type="text" ng-model="firstModelObj.firstName" /></label><br />
                <label>Set the middle name: <input type="text" ng-model="thirdModelObj.middleName" /></label><br />
                <label>Set the last name: <input type="text" ng-model="thirdModelObj.lastName" /></label>
            </div>
        </div>
    </div>
اگر نگاهی به کنترلرهای فوق بیاندازیم می‌بینیم که در firstControllerObj تنها یک شیء درون scope قرار داده شده که firstModelObj نامیده شده است و این شی تنها دارای یک property با نام firstName است. در کنترلر دوم نیز یک شیء جدید با نام secondModelObj ساخته شده است؛ با این تفاوت که property آن lastName نام دارد. حال به بدنه‌ی فایل html دو کنترلر توجه کنیم. می‌بینیم که در کنترلر دوم مستقیما پراپرتی firstName از شیء درون کلاس اول فراخوانی شده است. با اجرای پروژه‌ی نمونه خواهیم دید که با تغییر input شامل firstName در کنترلر دوم، این مدل در کنترلر اول نیز تغییر خواهد کرد.
بنابراین ما می‌توانیم در کنترلر فرزند به objectهای درون کنترلر والد دسترسی داشته باشیم و مقادیر آنرا تغییر دهیم. باید توجه داشت که این نوع تعریف مدل با آنچه که در بخش اول گفته شد، تفاوتی اساسی دارد. در بخش قبل با تغییر مدل توسط کنترلر فرزند، آن مدل در کنترلر والد تغییری نمی‌کرد؛ اما در روشی که در این مقاله شرح داده شد مشاهده نمودیم که با تغییر مدل در کنترلر فرزند مقدار مدل در کنترلر والد نیز تغییر می‌کند. 
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 13 - بررسی سیستم ردیابی تغییرات
نکته تکمیلی
وقتی یک موجودیت داخل سیستم ردیابی است، وارد کردن دوباره آن به سیستم ردیابی بدون نتیجه است، برای مثال ما میخواهیم یک رکورد را از پایگاه داده حذف کنیم:
public IActionResult OnPost(int id)
{
    var categoryToDelete = _categoryService.GetForDelete(id);
    if (categoryToDelete is null)
        return BadRequest();
    _categoryService.Remove(id); // Not works! => First overload
    _dbContext.SaveChanges();
    return RedirectToPage("Index");
}
و در لایه سرویس دو متود برای حذف رکورد‌ها وجود دارد:
public void Remove(int id)
{
    var category = new Category() { Id = id };
    _context.Entry(category).State = EntityState.Deleted;
}

public void Remove(Category value)
=> _context.Remove(value);
چون ما یکبار category را خوانده ایم و وارد سرویس ردیابی شده است دیگر نمیتوان از اولین overload متود Remove استفاده کرد یعنی فقط با دادن  Id آنرا حذف کرد و باید از overload دوم استفاده کرد.
مطالب
فراخوانی یک متداز یک کنترل WPF از XAML
در بعضی مواقع نیاز است که یک متد از یک کنترل درون XAML فراخوانی شود. برای مثال لازم است یکی از متد‌های یک کنترل در یک استایل فراخوانی شود. یکی از روش‌های انجام این کار استفاده از خصوصیت‌های پیوست شده( AttachedPropery) است. شیوه‌ی کار به این صورت است که یک خصوصیت از نوع Bool ایجاد می‌کنیم. هنگامیکه مقدار این خصوصیت تغییر کند یک رویه فراخوانی می‌شود که کار فراخوانی متد مورد نظر را انجام میدهد:
public class SelectAllBehavior
    {
        public static bool GetSelectAll(TextBoxBase target)
        {
            return (bool)target.GetValue(SelectAllProperty);
        }

        public static void SetSelectAll(TextBoxBase target, bool value)
        {
            target.SetValue(SelectAllProperty, value);
        }

        public static readonly DependencyProperty SelectAllProperty = DependencyProperty.RegisterAttached("SelectAll", typeof(bool), typeof(SelectAllBehavior), new UIPropertyMetadata(false, OnSelectAllPropertyChanged));

        static void OnSelectAllPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((TextBoxBase)o).SelectAll();
        }
    }
 برای استفاده از کلاس فوق درون یک استایل باید به شکل زیر عمل کرد:
<Style TargetType="{x:Type TextBoxBase}" >
    <Style.Triggers>
        <Trigger Property="IsFocused" Value="True">
            <Setter Property="x: SelectAllBehavior.SelectAll" Value="True"/>
        </Trigger>
    </Style.Triggers>
</Style>

در تکه کد بالا ،هنگامی که خصوصیت IsFocused مربوط به کنترل TextBox برابر True می‌شود، یعنی Focus روی TextBox قرار می‌گیرد،مقدار خصوصیت پیوست شده نیز برابر True می‌شود که همانطور که گفته شد باعث فراخوانی OnSelectAllPropertyChanged می‌شود.

تا اینجا فراخوانی یک متد از کنترل از طریق استایل توضیح داده شد، همانطور که در عنوان مطلب آورده شده است. اما اگر بخواهید مثال فوق را به درستی اجرا کنید یعنی هنگام کلیک روی TextBox متن درون آن انتخاب شود، نیاز به اضافه کردن کدهای دیگری وجود دارد. چرا که به صورت پیش فرض زمانی که MouseLeftButtonUp اجرا می‌شود در صورتی که حالت متن به صورت انتخاب شده باشد، از حالت انتخاب خارج میشود. این بار برای اینکار از   خصوصیت‌های پیوست شده( AttachedPropery)  برای کنترل رویداد PreviewMouseLeftButtonDown استفاده میکنیم و هنگام فشرده شدن کلید سمت چپ موس رویدادهای Click و بقیه LeftMouseButtonDown‌ها را غیرفعال می‌کنیم.

 public class PreviewMouseLeftButtonDownBehavior
{
    public static readonly DependencyProperty PreviewMouseLeftButtonDownProperty = DependencyProperty.RegisterAttached("PreviewMouseLeftButtonDown"
                  , typeof(bool?)
                  , typeof(PreviewMouseLeftButtonDownBehavior)
                  , new UIPropertyMetadata(null, OnPreviewMouseLeftButtonDown)
                  );

    public static void SetPreviewMouseLeftButtonDown(DependencyObject target, bool? value)
    {
        target.SetValue(PreviewMouseLeftButtonDownProperty, value);
    }
    public static object GetPreviewMouseLeftButtonDown(DependencyObject target)
    {
        return target.GetValue(PreviewMouseLeftButtonDownProperty);
    }

    public static void OnPreviewMouseLeftButtonDown(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var control = obj as Control;
        if (control == null)
            return;
        if (e.NewValue != null && e.OldValue == null)
            control.PreviewMouseLeftButtonDown += control_PreviewMouseLeftButtonUp;
        else if ((e.NewValue == null) && (e.OldValue != null))
        {
            control.PreviewMouseLeftButtonDown -= control_PreviewMouseLeftButtonUp;
        }
    }
    static void control_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        var tb = (sender as TextBox);
        if (tb != null)
        {
            if (!tb.IsKeyboardFocusWithin)
            {
                e.Handled = true;
                tb.Focus();
            }
        }
    }
}

و استایل را به صورت زیر تغییر میدهیم:

<Style TargetType="{x:Type TextBoxBase}" >
    <Setter Property="x:PreviewMouseLeftButtonDownBehavior.IsPreviewMouseLeftButtonDown" Value="True"/>
    <Style.Triggers>
        <Trigger Property="IsKeyboardFocusWithin" Value="True">
            <Setter Property="InputLanguageManager.InputLanguage" Value="fa-ir" />
        </Trigger>
        <Trigger Property="IsFocused" Value="True">
            <Setter Property="xamlServices:SelectAllTextBoxBehavior.SelectAll" Value="True"/>
        </Trigger>
    </Style.Triggers>
</Style>

در این مثال با Focus رو هر TextBox که استایل فوق را به کار گرفته باشد، متن در حالت انتخاب شده قرار میگیرد. (البته این مشروط به این است که نیاز نباشد رویداد PreviewMouseLeftButtonDown  کنترل مورد نظر درون برنامه مقیدسازی(Bind) شود.)

نظرات مطالب
Url Routing در ASP.Net WebForms
زمانیکه فرم default رو در یک پوشه‌ی دیگر گذاشتید، برای اون یک route جدا تعریف کردید؟ الان آیا برنامه اطلاع داره زمانیکه به مسیر ریشه سایت وارد شد باید به کجا هدایت بشه؟
routes.MapPageRoute("defaultRoute", "{*value}", "~/folder/default.aspx");
اشتراک‌ها
TokuMX توزیع جدید از مونگو دیبی جهت کارایی بالاتر

در این توزیع به جای استفاده از ساختار پیش فرض درخت Btree از ساختار دیگری به نام Fractal Tree Index استفاده میشود که باعث بهبود کارایی بعضی از قسمت‌ها شده است و همچنین شامل تراکنش ACID و MVCC میباشد ولی پشتیبانی از Full Text Search در حال حاضر ندارد.

به نظر این مجموعه با تمرکز با کارایی write نوشته شده است.

TokuMX  توزیع جدید از مونگو دیبی جهت کارایی بالاتر
اشتراک‌ها
الگوریتم ها و ساختارهای داده در C#

لیست نسبتا کاملی از نحوه پیاده سازی الگوریتم‌های عمومی مانند انواع مرتب سازی (Bubble Sort, Selection Sort, ...)، جستجو، کار با درخت ها، کار با گراف و ... و همچنین انواع ساختار داده نظیر آرایه، پشته، صف و ... در C#.

الگوریتم ها و ساختارهای داده در C#
مطالب
افزودن ASP.NET Identity به یک پروژه Web Forms
  • با نصب و اجرای Visual Studio 2013 Express for Web یا Visual Studio 2013 شروع کنید.
  • یک پروژه جدید بسازید (از صفحه شروع یا منوی فایل)
  • گزینه #Visual C و سپس ASP.NET Web Application را انتخاب کنید. نام پروژه را به "WebFormsIdentity" تغییر داده و OK کنید.

  • در دیالوگ جدید ASP.NET گزینه Empty را انتخاب کنید.

دقت کنید که دکمه Change Authentication غیرفعال است و هیچ پشتیبانی ای برای احراز هویت در این قالب پروژه وجود ندارد.


افزودن پکیج‌های ASP.NET Identity به پروژه

روی نام پروژه کلیک راست کنید و گزینه Manage NuGet Packages را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده عبارت "Identity.E" را وارد کرده و  این پکیج را نصب کنید.

دقت کنید که نصب کردن این پکیج وابستگی‌ها را نیز بصورت خودکار نصب می‌کند: Entity Framework و ASP.NET Idenity Core.


افزودن فرم‌های وب لازم برای ثبت نام کاربران

یک فرم وب جدید بسازید.

در دیالوگ باز شده نام فرم را به Register تغییر داده و تایید کنید.

فایل ایجاد شده جدید را باز کرده و کد Markup آن را با قطعه کد زیر جایگزین کنید.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Register.aspx.cs" Inherits="WebFormsIdentity.Register" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body style="
    <form id="form1" runat="server">
    <div>
        <h4 style="Register a new user</h4>
        <hr />
        <p>
            <asp:Literal runat="server" ID="StatusMessage" />
        </p>                
        <div style="margin-bottom:10px">
            <asp:Label runat="server" AssociatedControlID="UserName">User name</asp:Label>
            <div>
                <asp:TextBox runat="server" ID="UserName" />                
            </div>
        </div>
        <div style="margin-bottom:10px">
            <asp:Label runat="server" AssociatedControlID="Password">Password</asp:Label>
            <div>
                <asp:TextBox runat="server" ID="Password" TextMode="Password" />                
            </div>
        </div>
        <div style="margin-bottom:10px">
            <asp:Label runat="server" AssociatedControlID="ConfirmPassword">Confirm password</asp:Label>
            <div>
                <asp:TextBox runat="server" ID="ConfirmPassword" TextMode="Password" />                
            </div>
        </div>
        <div>
            <div>
                <asp:Button runat="server" OnClick="CreateUser_Click" Text="Register" />
            </div>
        </div>
    </div>
    </form>
</body>
</html>

این تنها یک نسخه ساده شده Register.aspx است که از چند فیلد فرم و دکمه ای برای ارسال آنها به سرور استفاده می‌کند.

فایل کد این فرم را باز کرده و محتویات آن را با قطعه کد زیر جایگزین کنید.

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Linq;

namespace WebFormsIdentity
{
   public partial class Register : System.Web.UI.Page
   {
      protected void CreateUser_Click(object sender, EventArgs e)
      {
         // Default UserStore constructor uses the default connection string named: DefaultConnection
         var userStore = new UserStore<IdentityUser>();
         var manager = new UserManager<IdentityUser>(userStore);

         var user = new IdentityUser() { UserName = UserName.Text };
         IdentityResult result = manager.Create(user, Password.Text);

         if (result.Succeeded)
         {
            StatusMessage.Text = string.Format("User {0} was created successfully!", user.UserName);
         }
         else
         {
            StatusMessage.Text = result.Errors.FirstOrDefault();
         }
      }
   }
}

کد این فرم نیز نسخه ای ساده شده است. فایلی که بصورت خودکار توسط VS برای شما ایجاد می‌شود متفاوت است.

کلاس IdentityUser پیاده سازی پیش فرض EntityFramework از قرارداد IUser است. قرارداد IUser تعریفات حداقلی یک کاربر در ASP.NET Identity Core را در بر می‌گیرد.

کلاس UserStore پیاده سازی پیش فرض EF از یک فروشگاه کاربر (user store) است. این کلاس چند قرارداد اساسی ASP.NET Identity Core را پیاده سازی می‌کند: IUserStore, IUserLoginStore, IUserClaimStore و IUserRoleStore.

کلاس UserManager دسترسی به API‌های مربوط به کاربران را فراهم می‌کند. این کلاس تمامی تغییرات را بصورت خودکار در UserStore ذخیره می‌کند.

کلاس IdentityResult نتیجه یک عملیات هویتی را معرفی می‌کند (identity operations).

پوشه App_Data را به پروژه خود اضافه کنید.

فایل Web.config پروژه را باز کنید و رشته اتصال جدیدی برای دیتابیس اطلاعات کاربران اضافه کنید. این دیتابیس در زمان اجرا (runtime) بصورت خودکار توسط EF ساخته می‌شود. این رشته اتصال شبیه به رشته اتصالی است که هنگام ایجاد پروژه بصورت خودکار برای شما تنظیم می‌شود.

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
   <connectionStrings>
      <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\WebFormsIdentity.mdf;Initial Catalog=WebFormsIdentity;Integrated Security=True"
            providerName="System.Data.SqlClient" />
   </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>

همانطور که مشاهده می‌کنید نام این رشته اتصال DefaultConnection است.

روی فایل Register.aspx کلیک راست کنید و گزینه Set As Start Page را انتخاب کنید. اپلیکیشن خود را با کلیدهای ترکیبی Ctrl + F5 اجرا کنید که تمام پروژه را کامپایل نیز خواهد کرد. یک نام کاربری و کلمه عبور وارد کنید و روی Register کلیک کنید.

ASP.NET Identity از اعتبارسنجی نیز پشتیبانی می‌کند، مثلا در این مرحله می‌توانید از اعتبارسنج هایی که توسط ASP.NET Identity Core عرضه می‌شوند برای کنترل رفتار فیلد‌های نام کاربری و کلمه عبور استفاده کنید. اعتبارسنج پیش فرض کاربران (User) که UserValidator نام دارد خاصیتی با نام AllowOnlyAlphanumericUserNames دارد که مقدار پیش فرضش هم true است. اعتبارسنج پیش فرض کلمه عبور (MinimumLengthValidator) اطمینان حاصل می‌کند که کلمه عبور حداقل 6 کاراکتر باشد. این اعتبارسنج‌ها بصورت property‌ها در کلاس UserManager تعریف شده اند و می‌توانید آنها را overwrite کنید و اعتبارسنجی سفارشی خود را پیاده کنید. از آنجا که الگوی دیتابیس سیستم عضویت توسط Entity Framework مدیریت می‌شود، روی الگوی دیتابیس کنترل کامل دارید، پس از Data Annotations نیز می‌توانید استفاده کنید.


تایید دیتابیس LocalDbIdentity که توسط EF ساخته می‌شود

از منوی View گزینه Server Explorer را انتخاب کنید.

گره (DefaultConnection (WebFormsIdentity و سپس Tables را باز کنید. روی جدول AspNetUsers کلیک راست کرده و Show Table Data را انتخاب کنید.


پیکربندی اپلیکیشن برای استفاده از احراز هویت OWIN

تا این مرحله ما تنها امکان ایجاد حساب‌های کاربری را فراهم کرده ایم. حال نیاز داریم امکان احراز هویت کاربران برای ورود آنها به سایت را فراهم کنیم. ASP.NET Identity برای احراز هویت مبتنی بر فرم (forms authentication) از OWIN Authentication استفاده می‌کند. OWIN Cookie Authentication مکانیزمی برای احراز هویت کاربران بر اساس cookie‌ها و claim‌ها است (claims-based). این مکانیزم می‌تواند توسط Entity Framework روی OWIN یا IIS استفاده شود.
با چنین مدلی، می‌توانیم از پکیج‌های احراز هویت خود در فریم ورک‌های مختلفی استفاده کنیم، مانند ASP.NET MVC و ASP.NET Web Forms. برای اطلاعات بیشتر درباره پروژه Katana و نحوه اجرای آن بصورت Host Agnostic به لینک Getting Started with the Katana Project مراجعه کنید.


نصب پکیج‌های احراز هویت روی پروژه

روی نام پروژه خود کلیک راست کرده و Manage NuGet Packages را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده عبارت "Identity.Owin" را وارد کنید و این پکیج را نصب کنید.

به دنبال پکیجی با نام Microsoft.Owin.Host.SystemWeb بگردید و آن را نیز نصب کنید.

پکیج Microsoft.Aspnet.Identity.Owin حاوی یک سری کلاس Owin Extension است و امکان مدیریت و پیکربندی OWIN Authentication در پکیج‌های ASP.NET Identity Core را فراهم می‌کند.

پکیج Microsoft.Owin.Host.SystemWeb حاوی یک سرور OWIN است که اجرای اپلیکیشن‌های مبتنی بر OWIN را روی IIS و با استفاده از ASP.NET Request Pipeline ممکن می‌سازد. برای اطلاعات بیشتر به OWIN Middleware in the IIS integrated pipeline مراجعه کنید.


افزودن کلاس‌های پیکربندی Startup و Authentication

روی پروژه خود کلیک راست کرده و گزینه Add و سپس Add New Item را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده عبارت "owin" را وارد کنید. نام کلاس را "Startup" تعیین کرده و تایید کنید.

فایل Startup.cs را باز کنید و قطعه کد زیر را با محتویات آن جایگزین کنید تا احراز هویت OWIN Cookie Authentication پیکربندی شود.

using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;

[assembly: OwinStartup(typeof(WebFormsIdentity.Startup))]

namespace WebFormsIdentity
{
   public class Startup
   {
      public void Configuration(IAppBuilder app)
      {
         // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
         app.UseCookieAuthentication(new CookieAuthenticationOptions
         {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Login")
         });
      }
   }
}

این کلاس حاوی خاصیت OwinAttribute است که کلاس راه انداز OWIN را نشانه گذاری می‌کند. هر اپلیکیشن OWIN یک کلاس راه انداز (startup) دارد که توسط آن می‌توانید کامپوننت‌های application pipeline را مشخص کنید. برای اطلاعات بیشتر درباره این مدل، به OWIN Startup Class Detection مراجعه فرمایید.


افزودن فرم‌های وب برای ثبت نام و ورود کاربران

فایل Register.cs را باز کنید و قطعه کد زیر را وارد کنید. این قسمت پس از ثبت نام موفقیت آمیز کاربر را به سایت وارد می‌کند.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using System;
using System.Linq;
using System.Web;

namespace WebFormsIdentity
{
   public partial class Register : System.Web.UI.Page
   {
      protected void CreateUser_Click(object sender, EventArgs e)
      {
         // Default UserStore constructor uses the default connection string named: DefaultConnection
         var userStore = new UserStore<IdentityUser>();
         var manager = new UserManager<IdentityUser>(userStore);
         var user = new IdentityUser() { UserName = UserName.Text };

         IdentityResult result = manager.Create(user, Password.Text);

         if (result.Succeeded)
         {
            var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
            var userIdentity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
            authenticationManager.SignIn(new AuthenticationProperties() { }, userIdentity);
            Response.Redirect("~/Login.aspx");
         }
         else
         {
            StatusMessage.Text = result.Errors.FirstOrDefault();
         }
      }
   }
}
از آنجا که ASP.NET Identity و OWIN Cookie Authentication هر دو مبتنی بر Claims هستند، فریم ورک از برنامه نویس اپلیکیشن انتظار دارد تا برای کاربر یک آبجکت از نوع ClaimsIdentity تولید کند. این آبجکت تمام اطلاعات اختیارات کاربر را در بر می‌گیرد، مثلا اینکه کاربر به چه نقش هایی تعلق دارد. همچنین در این مرحله می‌توانید اختیارات (Claims) جدیدی به کاربر اضافه کنید.
شما با استفاده از AuthenticationManager که متعلق به OWIN است می‌توانید کاربر را به سایت وارد کنید. برای این کار شما متد SignIn را فراخوانی می‌کنید و آبجکتی از نوع ClaimsIdentity را به آن پاس می‌دهید. این کد کاربر را به سایت وارد می‌کند و یک کوکی برای او می‌سازد. این فراخوانی معادل همان FormAuthentication.SetAuthCookie است که توسط ماژول FormsAuthentication استفاده می‌شود.
روی پروژه خود کلیک راست کرده، فرم وب جدیدی با نام Login بسازید.

فایل Login.aspx را باز کنید و کد Markup آن را مانند قطعه کد زیر تغییر دهید.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="WebFormsIdentity.Login" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title></title>
</head>
<body style="font-family: Arial, Helvetica, sans-serif; font-size: small">
   <form id="form1" runat="server">
      <div>
         <h4 style="font-size: medium">Log In</h4>
         <hr />
         <asp:PlaceHolder runat="server" ID="LoginStatus" Visible="false">
            <p>
               <asp:Literal runat="server" ID="StatusText" />
            </p>
         </asp:PlaceHolder>
         <asp:PlaceHolder runat="server" ID="LoginForm" Visible="false">
            <div style="margin-bottom: 10px">
               <asp:Label runat="server" AssociatedControlID="UserName">User name</asp:Label>
               <div>
                  <asp:TextBox runat="server" ID="UserName" />
               </div>
            </div>
            <div style="margin-bottom: 10px">
               <asp:Label runat="server" AssociatedControlID="Password">Password</asp:Label>
               <div>
                  <asp:TextBox runat="server" ID="Password" TextMode="Password" />
               </div>
            </div>
            <div style="margin-bottom: 10px">
               <div>
                  <asp:Button runat="server" OnClick="SignIn" Text="Log in" />
               </div>
            </div>
         </asp:PlaceHolder>
         <asp:PlaceHolder runat="server" ID="LogoutButton" Visible="false">
            <div>
               <div>
                  <asp:Button runat="server" OnClick="SignOut" Text="Log out" />
               </div>
            </div>
         </asp:PlaceHolder>
      </div>
   </form>
</body>
</html>

محتوای فایل Login.aspx.cs را نیز مانند لیست زیر تغییر دهید.

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using System;
using System.Web;
using System.Web.UI.WebControls;

namespace WebFormsIdentity
{
   public partial class Login : System.Web.UI.Page
   {
      protected void Page_Load(object sender, EventArgs e)
      {
         if (!IsPostBack)
         {
            if (User.Identity.IsAuthenticated)
            {
               StatusText.Text = string.Format("Hello {0}!", User.Identity.GetUserName());
               LoginStatus.Visible = true;
               LogoutButton.Visible = true;
            }
            else
            {
               LoginForm.Visible = true;
            }
         }
      }

      protected void SignIn(object sender, EventArgs e)
      {
         var userStore = new UserStore<IdentityUser>();
         var userManager = new UserManager<IdentityUser>(userStore);
         var user = userManager.Find(UserName.Text, Password.Text);

         if (user != null)
         {
            var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
            var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);

            authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = false }, userIdentity);
            Response.Redirect("~/Login.aspx");
         }
         else
         {
            StatusText.Text = "Invalid username or password.";
            LoginStatus.Visible = true;
         }
      }

      protected void SignOut(object sender, EventArgs e)
      {
         var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
         authenticationManager.SignOut();
         Response.Redirect("~/Login.aspx");
      }
   }
}
  • متد Page_Load حالا وضعیت کاربر جاری را بررسی می‌کند و بر اساس وضعیت Context.User.Identity.IsAuthenticated تصمیم گیری می‌کند. 
نمایش نام کاربر جاری: فریم ورک ASP.NET Identity روی  System.Security.Principal.Identity  متدهایی نوشته است که به شما امکان دریافت نام و شناسه کاربر جاری را می‌دهد. این متدها در اسمبلی Microsoft.AspNet.Identity.Core وجود دارند. این متدها جایگزین  HttpContext.User.Identity.Name  هستند.
  • متد SignIn
این متد، متد CreateUser_Click را که پیشتر بصورت خودکار ایجاد شده جایگزین می‌کند و پس از ایجاد موفقیت آمیز حساب کاربری، کاربر جاری را به سایت وارد می‌کند. فریم ورک OWIN متدهایی روی System.Web.HttpContext افزوده است که به شما این امکان را می‌دهند که یک ارجاع از نوع IOwinContext بگیرید. این متد‌ها در اسمبلی Microsoft.Owin.Host.SystemWeb وجود دارند. کلاس OwinContext خاصیتی از نوع IAuthenticationManager دارد که امکانات احراز هویت موجود برای درخواست جاری را معرفی می‌کند.
  • پروژه را با Ctrl + F5 اجرا کنید و کاربر جدیدی بسازید. پس از وارد کردن نام کاربری و کلمه عبور و کلیک کردن دکمه Register باید بصورت خودکار به سایت وارد شوید و نام خود را مشاهده کنید.

  • همانطور که مشاهده می‌کنید در این مرحله حساب کاربری جدید ایجاد شده و به سایت وارد شده اید. روی Log out کلیک کنید تا از سایت خارج شوید. پس از آن باید به صفحه ورود هدایت شوید.
  • حالا یک نام کاربری یا کلمه عبور نامعتبر وارد کنید و روی Log in کلیک کنید. 
متد UserManager.Find مقدار null بر می‌گرداند، بنابراین پیام خطای "Invalid username or password" نمایش داده خواهد شد.

مطالب
آشنایی با Saltarelle کامپایلر قدرتمند #C به جاوااسکریپت

شاید ساده‌ترین تعریف برای  Saltarelle  این باشد که «کامپایلریست که کد‌های C# را به جاوا اسکریپت تبدیل می‌کند». محاسن زیادی را می‌توان برای اینگونه کامپایلر‌ها نام برد؛ مخصوصا در پروژه‌های سازمانی که نگهداری از کد‌های جاوا اسکریپت بسیار سخت و گاهی خارج از توان است و این شاید مهمترین عامل ظهور ابزارهای جدید از قبیل Typescript باشد.

در هر صورت اگر حوصله و وقت کافی برای تجهیز تیم نرم افزاری، به دانش یک زبان جدید مانند Typescript نباشد، استفاده از توان و دانش تیم تولید، از زبان C# ساده‌ترین راه حل است و اگر ابزاری مطمئن برای استفاده از حداکثر قدرت JavaScript همراه با امکانات نگهداری و توسعه کد‌ها وجود داشته باشد، بی شک Saltarelle یکی از بهترین‌های آنهاست.

قبلا کامپایلر هایی از این دست مانند  Script# وجود داشتند، اما فاقد همه امکانات C# بوده وعملا قدرت کامل C# در کد نویسی وجود نداشت. اما با توجه به ادعای توسعه دهندگان این کامپایلر سورس باز در استفاده‌ی حداکثری از کلیه ویژگی‌های C# 5 و با وجود Library ‌های متعدد می‌توان Saltarelle  را عملا یک کامپایلر موفق در این زمینه دانست.

برای استفاده از Saltarelle در یک برنامه وب ساده باید یک پروژه Console Application به Solution اضافه کرد و پکیج Saltarelle.Compiler را از nuget نصب نمایید. بعد از نصب این پکیج، کلیه Reference ‌ها از پروژه حدف می‌شوند و هر بار Build توسط کامپایلر Saltarelle  انجام می‌شود. البته با اولین Build، مقداری Error را خواهید دید که برای از بین بردنشان نیاز است پکیج Saltarelle.Runtime را نیز در این پروژه نصب نمایید:

PM> Install-Package Saltarelle.Compiler
PM> Install-Package Saltarelle.Runtime

در صورتیکه کماکان Build  نهایی با Error همرا بود، یکبار این پروژه را Unload  و سپس مجددا Load نمایید



UI یک پروژه وب MVC است و Client یک Console Application که پکیج‌های مورد نیاز Saltarelle  روی آن نصب شده است.

در صورتیکه پروژه را Build نماییم و نگاهی به پوشه‌ی Debug بیاندازیم، یک فایل JavaScript همنام پروژه وجود دارد:


برای اینکه بعد از هر بار Build ، فایل اسکریپت به پوشه‌ی مربوطه در پروژه UI منتقل شود کافیست کد زیر را در Post Build  پروژه Client بنویسیم: 

copy "$(TargetDir)$(TargetName).js" "$(SolutionDir)SalratelleSample.UI\Scripts"

اکنون پس از هر بار Build ، فایل اسکریپت مورد نظر در پوشه‌ی Scripts پروژه UI  آپدیت می‌شود:


در ادامه کافیست فایل اسکریپت را به layout اضافه کنیم. 

<script src="~/Scripts/SaltarelleSample.Client.js"></script>

در پوشه‌ی Saltarelle.Runtime در پکیج‌های نصب شده، یک فایل اسکریپت به نام mscorlib.min.js نیز وجود دارد که حاوی اسکریپت‌های مورد نیاز Saltarelle در هنگام اجراست. آن را به پوشه اسکریپت‌های پروژه UI کپی نمایید و سپس به Layout  اضافه کنید. 

<script src="~/Scripts/mscorlib.min.js"></script>
<script src="~/Scripts/SaltarelleSample.Client.js"></script>

حال نوبت به اضافه نمودن library‌های مورد نیازمان است. برای دسترسی به آبجکت هایی از قبیل document, window, element و غیره در جاوااسکریپت می‌توان پکیج Saltarelle.Web را در پروژه‌ی Client نصب نمود و برای دسترسی به اشیاء و فرمانهای jQuery، پکیج Salratelle.jQuery را نصب نمایید. 

> Install-Package Saltarelle.Web
> Install-Package Saltarelle.jQuery

به این library‌ها imported library می‌گویند. در واقع، در زمان کامپایل، برای این library‌ها فایل اسکریپتی تولید نمی‌شود و فقط آبجکت‌های #C هستند که که هنگام کامپایل تبدیل به کدهای ساده اسکریپت می‌شوند که اگر اسکریپت مربوط به آنها به صفحه اضافه نشده باشد، اجرای اسکریپت با خطا مواجه می‌شود.

به طور ساده‌تر وقتی از jQuery library استفاده می‌کنید هیچ فایل اسکریپت اضافه‌ای تولید نمی‌شود، اما باید اسکریپت jQuery به صفحه شما اضافه شده باشد.

<script src="~/Scripts/jquery-1.10.2.min.js"></script>

مثال ما یک اپلیکیشن ساده برای خواندن فید‌های همین سایت است. ابتدا کد‌های سمت سرور را در پروژه UI  می نویسیم.

کلاس‌های مورد نیاز ما برای این فید ریدر: 

public class Feed
    {
        public string FeedId { get; set; }
        public string Title { get; set; }
        public string Address { get; set; }

    }
    public class Item
    {
        public string Title { get; set; }
        public string Link { get; set; }
        public string Description { get; set; }
    }

و یک کلاس برای مدیریت منطق برنامه 

 public class SiteManager
    {
        private static List<Feed> _feeds;
        public static List<Feed> Feeds
        {
            get
            {
                if (_feeds == null)
                    _feeds = CreateSites();
                return _feeds;
            }
        }
        private static List<Feed> CreateSites()
        {
            return new List<Feed>() { 
                new Feed(){
                    FeedId = "1",
                    Title = "آخرین تغییرات سایت",
                    Address = "https://www.dntips.ir/rss.xml"
                },
                 new Feed(){
                    FeedId = "2",
                    Title = "مطالب سایت",
                    Address = "https://www.dntips.ir/feeds/posts"
                },
                 new Feed(){
                    FeedId = "3",
                    Title = "نظرات سایت",
                    Address = "https://www.dntips.ir/feeds/comments"
                },
                 new Feed(){
                    FeedId = "4",
                    Title = "خلاصه اشتراک ها",
                    Address = "https://www.dntips.ir/feed/news"
                },
            };
        }

        public static IEnumerable<Item> GetNews(string id)
        {
            XDocument feedXML = XDocument.Load(Feeds.Find(s=> s.FeedId == id).Address);
            var feeds = from feed in feedXML.Descendants("item")
                        select new Item
                        {
                            Title = feed.Element("title").Value,
                            Link = feed.Element("link").Value,
                            Description = feed.Element("description").Value
                        };
            return feeds;
        }

    }

کلاس SiteManager فقط یک لیست از فید‌ها دارد و متدی که با گرفتن شناسه‌ی فید ، یک لیست از آیتم‌های موجود در آن فید ایجاد می‌کند.

حال دو ApiController برای دریافت داده‌ها ایجاد می‌کنیم

public class FeedController : ApiController  
{
        // GET api/<controller>
        public IEnumerable<Feed> Get()
        {
            return SiteManager.Feeds;
        }
    }

public class ItemsController : ApiController
    {
        // GET api/<controller>/5
        public IEnumerable<Item> Get(string id)
        {
            return SiteManager.GetNews(id);
        }
    }

در View پیش‌فرض که Index از کنترلر Home  است،  یک Html ساده برای فرم  صفحه اضافه می‌کنیم 

<div>
    <div>
        <h2>Feeds</h2>
        <ul id="Feeds">
           
        </ul>
    </div>
    <div>
        <h2>Items</h2>
        <p id="FeedItems">
        </p>
    </div>
   
</div>

در المنت Feeds لیست فید‌ها را قرار می‌دهیم و در FeedItems آیتم‌های مربوط به هر فید. حال به سراغ کد‌های سمت کلاینت می‌رویم و به جای جاوا اسکریپت از Saltarelle استفاده می‌کنیم.

کلاس Program را از پروژه Client باز می‌کنیم و متد Main را به شکل زیر تغییر می‌دهیم:

static void Main()
        {
            jQuery.OnDocumentReady(() => {
                FillFeeds();
            });
        }

بعد از کامپایل شدن، کد #C شارپ بالا به صورت زیر در می‌آید: 

$SaltarelleSample_Client_$Program.$main = function() {
$(function() {
$SaltarelleSample_Client_$Program.$fillFeeds();
});
};
$SaltarelleSample_Client_$Program.$main();

و این همان متد معروف jQuery است که Saltarelle.jQuery برایمان ایجاد کرده است.

متد FillFeeds را به شکل زیر پیاده سازی می‌کنیم

private static void FillFeeds()
        {
            jQuery.Ajax(new jQueryAjaxOptions()
            {
                Url = "/api/feed",
                Type = "GET",
                Success = (d,t,r) => {

                    // Fill 
                    var ul = jQuery.Select("#Feeds");
                    jQuery.Each((List<Feed>)d, (idx,i) => {
                        var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer");
                        li.Click(eve => {
                            FillData(i.FeedId);
                        });
                        ul.Append(li);
                    });
                }
            });
        }

آبجکت jQuery، متدی به نام Ajax دارد که یک شی از کلاس jQueryAjaxOptions را به عنوان پارامتر می‌پذیرد. این کلاس کلیه خصوصیات متد Ajax در jQuery را پیاده سازی می‌کند. نکته شیرین آن توانایی نوشتن lambda برای Delegate هاست.

خاصیت Success یک Delegate است که 3 پارامتر ورودی را می‌پذیرد.

public delegate void AjaxRequestCallback(object data, string textStatus, jQueryXmlHttpRequest request);

data همان مقداریست که api باز می‌گرداند که یک لیست از Feed هاست. برای زیبایی کار، من یک کلاس Feed در پروژه Client اضافه می‌کنم که خصوصیاتی مشترک با کلاس اصلی سمت سرور دارد و مقدار برگشی Ajax را به آن تبدیل می‌کنم.

کلاس Feed و Item

 [PreserveMemberCase()]
    public class Feed
    {
        //[ScriptName("FeedId")]
        public string FeedId;

        //[ScriptName("Title")]
        public string Title;

        //[ScriptName("Address")]
        public string Address;

    }

    [PreserveMemberCase()]
    public class Item
    {
        // [ScriptName("Title")]
        public string Title;

        // [ScriptName("Link")]
        public string Link;

        // [ScriptName("Description")]
        public string Description;
    }
Attrubute‌های زیادی در Saltarelle وجود دارند و از آنجایی که کامپایلر اسم فیلد‌ها را camelCase کامپایل می‌کند من برای جلوگیری از آن از PreserveMemberCase  بر روی هر کلاس استفاده کردم. می‌توانید اسم هر فیلد را سفارشی کامپایل نمایید. 
jQuery.Each((List<Feed>)d, (idx,i) => {
                        var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer");
                        li.Click(eve => {
                            FillData(i.FeedId);
                        });
                        ul.Append(li);
                    });

به ازای هر آیتمی که در شیء بازگشتی وجود دارد، با استفاد از متد each در jQuery یک li ایجاد می‌کنیم. همان طور که می‌بینید کلیه خواص، به شکل Fluent قابل اضافه شدن می‌باشد. سپس برای li یک رویداد کلیک که در صورت وقوع، متد FillData را با شناسه فید کلیک شده فراخوانی می‌کند و در آخر li را به المنت ul اضافه می‌کنیم.

برای هر کلیک هم مانند مثال بالا api را با شناسه‌ی فید مربوطه فراخوانی کرده و به ازای هر آیتم، یک سطر ایجاد می‌کنیم.

private static void FillData(string p)
        {
            jQuery.Ajax(new jQueryAjaxOptions()
            {
                Url = "/api/items/" + p,
                Type = "GET",
                Success = (d, t, r) => {
                    var content = jQuery.Select("#FeedItems");
                    content.Html("");
                    foreach (var item in (List<Item>)d)
                    {
                        var row = jQuery.Select("<div>").AddClass("row").CSS("direction", "rtl");
                        var link = jQuery.Select("<a>").Attribute("href", item.Link).Text(item.Title);
                        row.Append(link);
                        content.Append(row);
                    }
                }
            });
        }
خروجی برنامه به شکل زیر است: 

در این مثال ما از Saltarelle.jQuery برای استفاده از jQuery.js استفاده نمودیم. library‌های متعددی برای Saltarelle  از قبیل  linq,angular,knockout,jQueryUI,nodeJs ایجاد شده و همچنین قابلیت‌های زیادی برای نوشتن imported library‌های سفارشی نیز وجود دارد. 

مطمئنا استفاده از چنین کامپایلرهایی راه حلی سریع برای رهایی از مشکلات متعدد کد نویسی با جاوا اسکریپت در نرم افزارهای بزرگ مقیاس است. اما مقایسه آنها با ابزارهایی از قبیل typescript احتیاج به زمان و تجربه کافی در این زمینه دارد.

مطالب
بررسی روش ارتقاء به NET Core 1.1.
نگارش پایدار NET Core 1.1. روز قبل منتشر شد. در ادامه نحوه‌ی ارتقاء پروژه‌‌های نگارش 1.0 RTM را به این نگارش بررسی خواهیم کرد.


دریافت نصاب NET Core 1.1.

برای این منظور به آدرس https://www.microsoft.com/net/download/core مراجعه کرده و فایل NET Core 1.1 SDK - Installer. را دریافت و نصب کنید. برای ظاهر شدن این گزینه باید حالت Current را بجای LTS (Long Term Support) انتخاب کرد:


همچنین در اینجا بسته NET Core 1.1 runtime - Installer. را هم جداگانه می‌توان دریافت و نصب کرد.


به روز رسانی فایل‌های global.json پروژه‌ها

اولین کاری را که باید پس از نصب نگارش‌های جدید NET Core. انجام داد، به روز رسانی شماره نگارش SDK درج شده‌ی در فایل‌های global.json تمام پروژه‌های موجود است. در غیراینصورت NuGet بسته‌های جدید مرتبط با آن‌ها را دریافت نخواهد کرد و آن‌ها را در لیست به روز شده‌ها نخواهید یافت.
برای این منظور خط فرمان را گشوده و دستور ذیل را صادر کنید:
 C:\>dotnet --version
1.0.0-preview2-1-003177
خروجی آن عبارتی است که باید قسمت نگارش SDK درج شود:
{
  "projects": [ "src", "test" ],
  "sdk": {
    "version": "1.0.0-preview2-1-003177"
  }
}


اصلاح فایل project.json پس از به روز رسانی فایل global.json

در ادامه باید فایل project.json نیز اندکی ویرایش شود تا شماره platform جدید را نیز درج کند. همچنین محل قرارگیری یکسری از بسته‌ها نیز باید تغییر کنند. در غیر اینصورت با اولین کامپایل Solution چنین خطاهایی را دریافت خواهید کرد:
 Can not find runtime target for framework '.NETCoreApp,Version=v1.0' compatible with one of the target runtimes: 'win10-x64, win81-x64, win8-x64, win7-x64'.
The project does not list one of 'win10-x64, win81-x64, win8-x64, win7-x64' in the 'runtimes' section.
برای رفع این مشکل، عبارت netcoreapp را در Solution جاری جستجو کرده و آن‌ها را به نحو ذیل تغییر دهید:
"frameworks": {
    "netcoreapp1.1": {
        "dependencies": {
            "Microsoft.NETCore.App": {
                "type": "platform",
                "version": "1.1.0"
            }
        },
        "imports": [
            "dnxcore50",
            "portable-net45+win8"
        ]
    }
},

یک نکته: اگر هنوز Microsoft.NETCore.App را در لیست dependencies ابتدای فایل project.json دارید، آن‌را حذف کنید؛ چون در قسمت frameworks فوق درج شده‌است. در غیراینصورت پیام تکراری بودن این کلید را دریافت خواهید کرد.

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


به روز رسانی بسته‌های نیوگت پایدار

قبل از هر کاری مطمئن شوید که آخرین بسته‌ی خود NuGet را نیز نصب کرده‌اید (مهم). به روز رسانی‌های اخیر آن بیشتر در جهت سازگاری با پروژه‌های NET Core. است.
https://dist.nuget.org/index.html

در ادامه برای به روز رسانی بسته‌های نیوگت، می‌توان بر روی گره References کلیک راست کرد و سپس انتخاب گزینه‌ی Manage NuGet Packages و در آخر انتخاب برگه‌ی Updates و انتخاب کتابخانه‌های به روز شده. این روش برای حالت داشتن چندین پروژه در یک Solution اندکی کند است.


روش سریعتر که تمام پروژه‌ها را نیز به صورت خودکار بررسی و به روز می‌کند، مراجعه به کنسول پاورشل نیوگت و سپس صدور دستور ذیل است:
 PM> Update-Package
اگر در میان کار خطایی را دریافت کردید، این دستور را مجددا اجرا کنید (جهت اطمینان حداقل دوبار این دستور را صادر کنید).
به علاوه پس از پایان کار، یکبار به طور کامل ویژوال استودیو را بسته و مجددا باز کنید. سپس این دستور را یکبار دیگر هم صادر کنید.


به روز رسانی بسته‌های نیوگت آزمایشی

یکسری از بسته‌ها مانند Microsoft.AspNetCore.Razor.Tools تنها با انتخاب حالت include prereleases ظاهر می‌شوند که آن‌ها را نیز باید به روز کرد:



تغییر مهم ابزارهای EF Core

در کل Solution عبارت Microsoft.EntityFrameworkCore.Tools را جستجو کرده و با نام جدید Microsoft.EntityFrameworkCore.Tools.DotNet جایگزین کنید.


در آخر یک نمونه فایل project.json به روز شده‌ی یک برنامه‌ی ASP.NET Core 1.1 را در ذیل مشاهده می‌کنید:
{
    "dependencies": {
        "Microsoft.AspNetCore.Diagnostics": "1.1.0",
        "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.1.0",
        "Microsoft.AspNetCore.Http.Extensions": "1.1.0",
        "Microsoft.AspNetCore.Mvc": "1.1.0",
        "Microsoft.AspNetCore.Mvc.Core": "1.1.0",
        "Microsoft.AspNetCore.Mvc.TagHelpers": "1.1.0",
        "Microsoft.AspNetCore.Razor.Runtime": "1.1.0",
        "Microsoft.AspNetCore.Razor.Tools": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        },
        "Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
        "Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
        "Microsoft.AspNetCore.Session": "1.1.0",
        "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000019",
        "Microsoft.AspNetCore.StaticFiles": "1.1.0",
        "Microsoft.EntityFrameworkCore": "1.1.0",
        "Microsoft.EntityFrameworkCore.InMemory": "1.1.0",
        "Microsoft.EntityFrameworkCore.Tools.DotNet": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        },
        "Microsoft.Extensions.Configuration.Binder": "1.1.0",
        "Microsoft.Extensions.Configuration.Json": "1.1.0",
        "Microsoft.Extensions.Logging.Console": "1.1.0",
        "Microsoft.Extensions.Logging.Debug": "1.1.0",
        "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        },
        "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
            "version": "1.1.0-preview4-final",
            "type": "build"
        }
    },
 
    "tools": {
        "BundlerMinifier.Core": "2.2.301",
        "Microsoft.AspNetCore.Razor.Tools": "1.1.0-preview4-final",
        "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
            "version": "1.1.0-preview4-final",
            "imports": [
                "portable-net45+win8"
            ]
        },
        "Microsoft.EntityFrameworkCore.Tools.DotNet": {
            "version": "1.1.0-preview4-final",
            "imports": [
                "portable-net45+win8"
            ]
        },
        "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.1.0-preview4-final"
    },
 
    "frameworks": {
        "netcoreapp1.1": {
            "dependencies": {
                "Microsoft.NETCore.App": {
                    "type": "platform",
                    "version": "1.1.0"
                }
            },
            "imports": [
                "dnxcore50",
                "portable-net45+win8"
            ]
        }
    },
 
    "buildOptions": {
        "emitEntryPoint": true,
        "preserveCompilationContext": true
    },
 
    "runtimeOptions": {
        "configProperties": {
            "System.GC.Server": true
        }
    },
 
    "publishOptions": {
        "include": [
            "wwwroot",
            "Features",
            "appsettings.json",
            "web.config"
        ]
    },
 
    "configurations": {
        "Release": {
            "buildOptions": {
                "optimize": true,
                "platform": "anycpu"
            }
        }
    },
 
    "scripts": {
        "precompile": [
            "dotnet bundle"
        ],
        "prepublish": [
            //"bower install"
        ],
        "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
    }
}


به روز رسانی پروژه‌ی Test

اگر از MSTest برای انجام آزمون‌های واحد استفاده می‌کنید، تغییرات فایل project.json آن نیز شامل تغییر شماره نگارش NETStandard.Library به 1.6.1 است و همچنین خود بسته‌های mstest نیز به روز شده‌اند. به علاوه قسمت frameworks آن نیز باید همانند مطالبی که عنوان شد، به روز شود:
{
    "version": "1.0.0-*",
 
    "testRunner": "mstest",
    "dependencies": {
        "Microsoft.EntityFrameworkCore": "1.1.0",
        "Microsoft.EntityFrameworkCore.InMemory": "1.1.0",
        "NETStandard.Library": "1.6.1",
        "dotnet-test-mstest": "1.1.2-preview",
        "MSTest.TestFramework": "1.0.6-preview"
    },
 
    "frameworks": {
        "netcoreapp1.1": {
            "dependencies": {
                "Microsoft.NETCore.App": {
                    "type": "platform",
                    "version": "1.1.0"
                }
            },
            "imports": [
                "dnxcore50",
                "portable-net45+win8"
            ]
        }
    }
}
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 5 - فعال سازی صفحات مخصوص توسعه دهنده‌ها
اولین Middleware موجود در بسته‌ی Microsoft.AspNetCore.Diagnostics را در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 3 - Middleware چیست؟» با نمایش welcome page آن، بررسی کردیم. در این مطلب سایر صفحات مخصوص توسعه دهنده‌های موجود در این بسته را مرور خواهیم کرد.



مشاهده‌ی جزئیات اطلاعات سرور و بسته‌های نصب شده‌ی بر روی آن

در نگارش‌های قبل از RTM، با فراخوانی app.UseRuntimeInfoPage در متد Configure کلاس Startup، ریز اطلاعاتی از وضعیت سرور و بسته‌های موجود در آن با مراجعه‌ی به آدرس http://site/runtimeinfo نمایش داده می‌شدند. این مورد خاص از نگارش RTM حذف شده‌است (احتمالا به دلایل امنیتی). البته اگر علاقمند به بررسی کدهای آن باشید، هنوز تاریخچه‌ی آن در GitHub موجود است .


مدیریت خطاها در برنامه‌های ASP.NET Core 1.0

به متد Configure کلاس Startup مراجعه کرد و یک سطر استثناء را به ابتدای کدهای Middleware انتهایی آن اضافه کنید:
public void Configure(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        throw new Exception("Generic Error");
        await context.Response.WriteAsync("Hello DNT!");
    });
}
هدف این است که بررسی کنیم اگر استثنایی در یک Middleware رخ داد، برنامه چه خروجی را نمایش می‌دهد.
در این حالت اگر برنامه را اجرا کنیم، این خروجی را دریافت خواهیم کرد:


و اگر به وضعیت بازگشت داده شده‌ی از طرف سرور دقت کنیم، فقط internal server error است:


در اینجا برخلاف نگارش‌های قبلی ASP.NET، دیگر حتی صفحه‌ی زرد رنگ معروف نمایش خطاها (yellow screen of death) نیز فعال نیستند. برای فعال سازی آن نیاز است Middleware مرتبط با آن‌را به نحو ذیل به برنامه معرفی کنیم:
public void Configure(IApplicationBuilder app)
{
   app.UseDeveloperExceptionPage();
پس از این فعال سازی، اگر مجددا برنامه را اجرا کنید، این خروجی را می‌توان در مرورگر مشاهده کرد:


به دلایل امنیتی و عدم نشت اطلاعات سمت سرور و خصوصا عدم امکان دیباگ از راه دور برنامه توسط مهاجمین، این Middleware به صورت پیش فرض فعال نیست.
بنابراین این سؤال مطرح می‌شود که چگونه می‌توان این صفحه را تنها در حین توسعه‌ی برنامه نمایش داد؟
پاسخ آن به نحوه‌ی طراحی متد Configure در کلاس Startup بر می‌گردد. این متد امضای ثابتی را ندارد. هر تعداد سرویسی را که نیاز داشتید، می‌توانید به عنوان پارامتر این متد معرفی کنید و کار تزریق وابستگی‌ها و نمونه سازی آن‌ها، توسط امکانات توکار ASP.NET Core به صورت خودکار انجام می‌شود. برای مثال سرویس IApplicationBuilder، یکی از سرویس‌های توکار ASP.NET Core است و برای تنظیم آن نیازی نیست تا کار خاصی را انجام دهیم. به همین جهت است که صرفا معرفی اینترفیس آن در این متد، وهله‌ای را از سازنده‌ی برنامه در اختیار ما قرار می‌دهد. سرویس‌ها را در مطلبی جداگانه مورد بررسی قرار خواهیم داد، اما فعلا جهت تکمیل بحث باید درنظر داشت که یکی دیگر از سرویس‌های توکار ASP.NET Core، به نام IHostingEnvironment، اطلاعاتی را در مورد محیطی که برنامه را در آن اجرا می‌کنیم در اختیار ما قرار می‌دهد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
روش معرفی آن نیز همانند روش معرفی سرویس IApplicationBuilder است و تنها کافی است به عنوان یک پارامتر جدید متد Configure معرفی شود. وهله سازی و تنظیمات آن نیز به صورت خودکار توسط ASP.NET Core انجام خواهد شد. اکنون پس از تزریق این سرویس، می‌توان صفحه‌ی نمایش جزئیات خطاها را تنها محدود به محیط توسعه کرد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
در مورد انواع محیط‌های توسعه، در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 2 - بررسی ساختار جدید Solution» در انتهای بحث به «نقش فایل launchsetting.json» اشاره شد. اگر بر روی پروژه کلیک راست کرده و به صفحه‌ی properties آن مراجعه کنید و یا دوبار کلیک بر روی گره properties، یک چنین تنظیمی را می‌توان مشاهده کرد:


این متغیر محیطی می‌تواند سه مقدار Development, Staging و Production را داشته باشد و بر اساس این متغیر و مقدار آن است که یکی از سه متد ذیل مفهوم پیدا می‌کنند و true یا false را باز می‌گردانند:
if(env.IsDevelopment()){ }
if(env.IsProduction()){ }
if(env.IsStaging()){ }


نمایش و مدیریت خطاها در حالت Production

از app.UseDeveloperExceptionPage صرفا در حالت توسعه استفاده کنید؛ چون اطلاعات نمایش داده شده‌ی توسط آن، بیش از اندازه برای مهاجمین مفید است. اما در حالت توزیع نهایی بر روی سرور چه باید کرد؟
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName");
    }
در این حالت از Middleware دیگری به نام ExceptionHandler با فراخوانی app.UseExceptionHandler می‌توان کمک گرفت. کار آن هدایت کاربر به صفحه‌ای خاص از برنامه، در صورت بروز استثنایی است. در اینجا شما می‌توانید یک صفحه‌ی عمومی «خطایی رخ داده‌است» را بدون ذکر هیچ نوع جزئیاتی، به کاربر نمایش دهید.
به علاوه در اینجا (در این قسمت خاص برنامه که توسط پارامتر errorHandlingPath مشخص شده‌است) با استفاده از قطعه کد ذیل، دسترسی کاملی را به اطلاعات خطای رخ داده، جهت ثبت و لاگ آن دارید:
 var feature = HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;


بررسی میان‌افزار StatusCode

این میان افزار برای مدیریت responseهایی که status code آن‌ها بین 400 تا 600 هستند، طراحی شده‌است. بر اساس این شماره‌ها، می‌توان خطای خاصی را بازگشت داده و یا کاربر را به یک صفحه یا کنترلر خاصی در برنامه، هدایت کرد.
در حالت عادی ثبت آن
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseStatusCodePages();
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName");
    }
}
تنها یک خروجی متنی را نمایش می‌دهد.


برای نمونه در اینجا مسیری درخواست داده شده‌است که توسط برنامه پردازش نمی‌شود و وجود ندارد.
اگر خواستید تا status code واقعی، به کاربر بازگشت داده شود، اما خروجی نمایش داده شده را سفارشی سازی کنید، می‌توانید از متد UseStatusCodePagesWithReExecute استفاده نمائید:
 app.UseStatusCodePagesWithReExecute("/MyControllerName/SomeActionMethodName/{0}");
در اینجا کاربر هنوز status code مساوی 404 را دریافت می‌کند (مناسب برای موتورهای جستجو)، اما اکشن متد خاصی در برنامه، سبب بازگشت یک View سفارشی به کاربر خواهد شد (بجای نمایش یک متن ساده). پارامتر {0} آن نیز همان شماره status code بازگشتی است.