مطالب
افزودن 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" نمایش داده خواهد شد.

پیشنهادها
بررسی GraphDiff
به روز رسانی گرافی از اشیاء (مثلا یک رابطه one-to-many یا many-to-many) منقطع از Context در EF کار زمانبری است؛ زیرا EF نمی‌داند که چه اشیایی در این بین تغییر کرده‌اند، کدام‌ها حذف شده‌اند و کدام‌ها جدید هستند. کتابخانه‌ی GraphDiff راه‌حلی را برای این مشکل ارائه داده است:
Introducing GraphDiff for Entity Framework Code First
GraphDiff Aggregate Mappings
GraphDiff v1.3.5
Updates to GraphDiff with new scenarios supported
How to update an expression tree with GraphDiff
Update Many-to-Many Association with GraphDiff 
اشتراک‌ها
سرفصل‌هایی برای معتبر باقی ماندن به عنوان یک توسعه دهنده‌ی NET.

First it is important to recognize that the .NET Framework is not the same as .NET Core. The .NET Framework is effectively now in maintenance mode , and all innovation is occurring in the open source .NET Core now and into the future. So step one to remaining relevant is to understand .NET Core (and the closely related .NET Standard). 

سرفصل‌هایی برای معتبر باقی ماندن به عنوان یک توسعه دهنده‌ی NET.
مطالب
شروع به کار با EF Core 1.0 - قسمت 5 - استراتژهای تعیین کلید اصلی جداول و ایندکس‌ها
پس از بررسی نحوه‌ی انجام تنظیمات اولیه‌ی کار با EF Core و همچنین آشنایی با مهاجرت‌های آن، مرحله‌ی بعد، مرحله‌ی مدلسازی داده‌ها است و اولین مرحله‌ی آن، نحوه‌ی تعیین کلید اصلی جداول است که در این زمینه، EF Core پیشرفت‌هایی قابل ملاحظه‌ای را نسبت به EF 6.x داشته‌است. در EF 6.x تنها دو حالت کلیدهای اصلی خود افزاینده که توسط بانک اطلاعاتی مدیریت می‌شوند و یا تولید کلید اصلی در سمت کلاینت و توسط برنامه، پشتیبانی می‌شوند. در EF Core، مواردی مانند Sequence و Alternate keys نیز اضافه شده‌اند.


پیش فرض‌های تعیین کلید اصلی در EF Core

به صورت پیش فرض هر خاصیتی که به نام Id و یا type name>Id> باشد، به عنوان primary key تفسیر خواهد شد؛ مانند:
public class Car
{
    public string Id { get; set; }
و یا
public class Car
{
   public string CarId { get; set; }
در مثال اول، نام خاصیت، Id است و در مثال دوم، جمع نام کلاس به همراه Id ذکر شده‌است. یک چنین مواردی، نیازی به تنظیم اضافه‌تری ندارند.


نحوه‌ی تعیین کلید اصلی به صورت صریح

اگر یکی از دو حالت فوق برقرار نباشند، باید کلید اصلی را به نحو صریحی مشخص کرد.
الف) از طریق ویژگی‌ها
public class Car
{
   [Key]
   public string LicensePlate { get; set; }
در اینجا چون LicensePlate نه Id نام دارد و نه جمع نام کلاس به همراه Id است، باید به نحو صریحی توسط ویژگی Key مشخص شود.
ب) با استفاده از روش Fluent API
public class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
         modelBuilder.Entity<Car>()
                 .HasKey(c => c.LicensePlate);
    }
 }
روش تنظیم کلید اصلی به صورت صریح، از طریق کدنویسی است که به آن Fluent API یا API روان هم گفته می‌شود. برای اینکار باید متد OnModelCreating کلاس Context برنامه را بازنویسی کرد و سپس از طریق متد HasKey، نام خاصیت کلید اصلی را ذکر نمود.


پیشنیاز کار با ویژگی‌ها در EF Core

در اسمبلی که مدل‌های موجودیت‌ها شما قرار دارند، نیاز است وابستگی System.ComponentModel.Annotations به فایل project.json پروژه اضافه شود، تا ویژگی‌هایی مانند Key، شناسایی و قابل استفاده شوند:
{
   "dependencies": {
          "System.ComponentModel.Annotations": "4.1.0"
   }
}


تعیین کلید ترکیبی و یا Composite key

اگر نیاز است چندین خاصیت را به صورت کلید اصلی معرفی کرد که به آن composite key هم می‌گویند، تنها روش ممکن، استفاده از Fluent API و به صورت زیر است:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<Car>()
                       .HasKey(c => new { c.State, c.LicensePlate });
}
در قسمت HasKey می‌توان چندین خاصیت را نیز جهت تعیین کلید ترکیبی مشخص کرد.


روش‌های مختلف تولید خودکار مقادیر خواص

حالت پیش فرض تولید مقدار فیلدهای Id عددی، همان حالت خود افزاینده‌ای است که توسط بانک اطلاعاتی کنترل می‌شود و یا کلید اصلی که از نوع Guid تعیین شود نیز به صورت خودکار توسط بانک اطلاعاتی در حین عملیات Add، مقدار دهی می‌شود (با استفاده از الگوریتم Guid سری در SQL Server).
 اگر این حالات مطلوب شما نیست، حالت‌های سه گانه‌ی ذیل را می‌توان استفاده کرد:

الف) هیچ داده‌ی خودکاری تولید نشود
برای اینکار می‌توان با استفاده از ویژگی DatabaseGenerated و تنظیم مقدار آن به None، جلوی تولید خودکار کلید اصلی را گرفت. در این حالت باید هم در حین عملیات Add و هم در حین عملیات Update، مقادیر را خودتان مقدار دهی کنید:
public class Blog
{
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int BlogId { get; set; }

    public string Url { get; set; }
}
و یا معادل این تنظیم با استفاده از Fluent API به صورت ذیل است:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
           .Property(b => b.BlogId)
           .ValueGeneratedNever();
}

ب) تولید داده‌های خودکار فقط در حالت Add
حالت Add به این معنا است که داده‌های خواص مشخصی، برای موجودیت‌های «جدید»، به صورت خودکار تولید خواهند شد. اینکه آیا واقعا این مقادیر به صورت خودکار تولید می‌شوند یا خیر، صرفا وابسته‌است به بانک اطلاعاتی در حال استفاده. برای مثال SQL Server برای نوع‌های Guid، به صورت خودکار با کمک الگوریتم SQL Server sequential GUID، کار مقدار دهی یک چنین فیلدهایی را انجام می‌دهد.
این فیلدها باید توسط ویژگی DatabaseGenerated و با مقدار Identity مشخص شوند. در اینجا Identity به معنای فیلدهایی است که به صورت خودکار توسط بانک اطلاعاتی مقدار دهی می‌شوند و الزاما به کلید اصلی اشاره نمی‌کنند. برای مثال در موجودیت ذیل، خاصیت تاریخ ثبت رکورد، از نوع Identity مشخص شده‌است. به این معنا که در حین ثبت اولیه‌ی رکورد آن، نیازی نیست تا خاصیت Inserted را مقدار دهی کرد. اما اینکه آیا SQL Server یک چنین کاری را به صورت خودکار انجام می‌دهد، پاسخ آن خیر است. SQL server فقط برای فیلدهای عددی و Guid ایی که با DatabaseGeneratedOption.Identity مزین شده باشند، مقادیر متناظری را به صورت خودکار تولید می‌کند. برای حالت DateTime نیاز است، مقدار پیش فرض فیلد را صریحا مشخص کرد که توسط ویژگی‌ها میسر نیست و فقط fluent API از آن پشتیبانی می‌کند.
public class Blog
{
   public int BlogId { get; set; }
   public string Url { get; set; }

   [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public DateTime Inserted { get; set; }
}
و یا معادل این تنظیم با استفاده از Fluent API به صورت ذیل است:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
           .Property(b => b.Inserted)
           .ValueGeneratedOnAdd();
}
برای تعیین مقدار پیش فرض خاصیت Inserted به نحوی که توسط SQL Server به صورت خودکار مقدار دهی شود، می‌توان از متد HasDefaultValueSql به نحو ذیل استفاده کرد:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Inserted)
        .HasDefaultValueSql("getdate()");
}
البته باید درنظر داشت که اگر خاصیت DateTime تعریف شده در اینجا به همین نحو بکاربرده شود، اگر مقداری برای آن در حین تعریف یک وهله جدید از کلاس Blog درکدهای برنامه درنظر گرفته نشود، یک مقدار پیش فرض حداقل به آن انتساب داده خواهد شد (چون value type است). بنابراین نیاز است این خاصیت را از نوع nullable تعریف کرد (public DateTime? Inserted).

یک نکته: در حالت DatabaseGeneratedOption.Identity و یا ValueGeneratedOnAdd فوق، اگر مقداری به این نوع فیلدها انتساب داده شده باشد که با مقدار پیش فرض آن‌ها (property.ClrType.GetDefaultValue) متفاوت باشد، از این مقدار جدید، بجای تولید مقداری خودکار، استفاده خواهد شد. برای مثال مقدار پیش فرض رشته‌ها، نال، مقادیر عددی، صفر و برای Guid مقدار Guid.Empty است. اگر هر مقدار دیگری بجای این‌ها به فیلدهای فوق انتساب داده شوند، از آن‌ها استفاده می‌شود.

ج) تولید داده‌های خودکار در هر دو حالت Add و Update
تولید داده‌ها در حالت‌های Add و Update به این معنا است که یک چنین خواصی، همواره با فراخوانی متد SaveChanges، دارای مقدار خودکار جدیدی خواهند شد و نیازی نیست در کدها مقدار دهی شوند. برای مشخص سازی این نوع خواص، از ویژگی DatabaseGenerated با مقدار Computed و یا متد ValueGeneratedOnAddOrUpdate در حالت Fluent API می‌توان استفاده کرد:
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime LastUpdated { get; set; }
}
و یا معادل این تنظیم با استفاده از Fluent API به صورت ذیل است:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
       .Property(b => b.LastUpdated)
       .ValueGeneratedOnAddOrUpdate();
}
همانطور که پیشتر نیز عنوان شد، تولید خودکار مقادیر فیلدها فقط در حالت‌های int و Guid انجام می‌شود (که برای مثال SQL Server از آن‌ها پشتیبانی می‌کند). در مثال فوق، خاصیت LastUpdated از نوع DateTime اینگونه تعریف شده‌است و SQL Server برای یک چنین فیلدهای خاصی، مقدار خودکاری را تولید نکرده و به دنبال مقدار پیش فرض آن می‌گردد. بنابراین در اینجا نیز باید مشخص سازی HasDefaultValueSql("getdate()") را که در قسمت قبل عنوان کردیم، صراحتا در قسمت تنظیمات Fluent API ذکر و تنظیم کرد.

تذکر: در اینجا نیز همانند حالت ValueGeneratedOnAdd، اگر این خواص مشخص شده، دارای مقدار متفاوتی با مقدار پیش فرض آن‌ها باشند، از این مقادیر جدید بجای تولید خودکار مقادیر استفاده خواهد شد.


خواص محاسباتی (Computed Columns) و تفاوت آن‌ها با DatabaseGeneratedOption.Computed

خواص محاسباتی (Computed Columns)، خواصی هستند که مقادیر آن‌ها در بانک اطلاعاتی محاسبه می‌شوند و کاملا متفاوت هستند با DatabaseGeneratedOption.Computed که مفهوم دیگری دارد. DatabaseGeneratedOption.Computed به این معنا است که این فیلد خاص، با هر بار فراخوانی SaveChanges باید مقدار محاسبه شده‌ی جدیدی را داشته باشد و روش تولید این مقدار خودکار، یا بر اساس Guidهای سری است، یا توسط فیلدهای خود افزاینده‌ی عددی و یا از طریق مقادیر پیش فرضی مانند getdate در حین ثبت یا به روز رسانی، مقدار دهی می‌شوند. اما خواص محاسباتی، یکی از امکانات «گزارشگیری سریع» SQL Server هستند و به نحو ذیل، تنها توسط Fluent API قابل تنظیم می‌باشند:
public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string DisplayName { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Person> People { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
          modelBuilder.Entity<Person>()
              .Property(p => p.DisplayName)
               .HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
     }
 }
در اینجا فیلد DisplayName یک فیلد محاسباتی بوده و از حاصل جمع دو فیلد دیگر در سمت دیتابیس تشکیل می‌شود. این نگاشت و محاسبه چون در سمت بانک اطلاعاتی انجام می‌شود، بازدهی بیشتری دارد نسبت به حالتی که ابتدا دو فیلد به کلاینت منتقل شده و سپس در این سمت جمع زده شوند.


امکان تعریف Sequence در EF Core 1.0

Sequence قابلیتی است که به SQL Server 2012 اضافه شده‌است و توضیحات بیشتر آن‌را در مطلب «نحوه ایجاد Sequence و استفاده آن در Sql Server 2012» می‌توانید مطالعه کنید.
در EF Core، امکان مدلسازی Sequence نیز پیش بینی شده‌است. آن‌ها به صورت پیش فرض در مدل‌ها ذکر نمی‌شوند و همچنین وابستگی به جدول خاصی ندارند. به همین جهت امکان تعریف آن‌ها صرفا توسط Fluent API وجود دارد:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     modelBuilder.HasSequence<int>("OrderNumbers", schema: "shared") 
           .StartsAt(1000).IncrementsBy(5);

     modelBuilder.Entity<Order>()
         .Property(o => o.OrderNo)
         .HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
}
پس از اینکه یک Sequence  تعریف شد، می‌توان برای نمونه از آن جهت تولید مقادیر پیش فرض ستون‌ها استفاده کرد.
در مثال فوق، ابتدا یک Sequence نمونه به نام OrderNumbers تعریف شده‌است که از عدد 1000 شروع شده و واحد افزایش آن 5 است. سپس از این نام در قسمت مقدار پیش فرض ستون OrderNo استفاده شده‌است.

و یا از Sequence ‌ها می‌توان برای تعیین مقدار پیش فرض Primary key بجای حالت identity خود افزایش یابنده استفاده کرد:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasSequence<int>("PrimaryKeyWithSequenceSequence");
    modelBuilder.Entity<PrimaryKeyWithSequence>(entity =>
     {
       entity.Property(e => e.PrimaryKeyWithSequenceId).HasDefaultValueSql("NEXT VALUE FOR [PrimaryKeyWithSequenceSequence]");
     });
}
در اینجا یک توالی از نوع int تعریف شده و سپس هربار که قرار است رکوردی درج شود، مقدار id آن به صورت خودکار از طریق کوئری Select NEXT VALUE FOR
[PrimaryKeyWithSequenceSequence] دریافت و سپس بجای فیلد id درج می‌شود.

به این روش الگوریتم Hi-Low هم می‌گویند که یکی از مهم‌ترین اهداف آن داشتن یک سری Id منحصربفرد، جهت بالابردن سرعت insertها در یک batch است. در حالت عادی insertها، ابتدا یک insert انجام می‌شود، سپس کوئری گرفته شده و آخرین Id درج شده به کلاینت بازگشت داده می‌شود. این روش، برای انجام تنها یک insert، سریع است. اما برای batch insert، به شدت کارآیی پایینی دارد. به همین جهت دسترسی به بازه‌ای از اعداد منحصربفرد، پیش از شروع به insert تعداد زیادی رکورد، سرعت نهایی کار را بالا می‌برد.


نحوه‌ی تعریف ایندکس‌ها در EF Core 1.0

برای افزودن ایندکس‌ها به EF Core 1.0، تنها روش میسر، استفاده از Fluent API است (و برخلاف EF 6.x از روش data annotations فعلا پشتیبانی نمی‌کند؛ هرچند API جدید آن نسبت به EF 6.x بسیار واضح‌تر است و با ابهامات کمتر).
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
      modelBuilder.Entity<Blog>()
          .HasIndex(b => b.Url)
          .HasName("Index_Url");
اگر قسمت HasName را ذکر نکنید، نام آن <IX_<type name>_<property name درنظر گرفته می‌شود و برای اینکه ایندکس منحصربفردی را تعریف کنید، می‌توان متد IsUnique را به انتهای این زنجیره اضافه کرد:
 modelBuilder.Entity<Blog>().HasIndex(b => b.Url).HasName("Index_Url").IsUnique();
همچنین می‌توان همانند composite keys، در اینجا نیز ترکیبی از خواص را به صورت یک ایندکس معرفی نمود:
modelBuilder.Entity<Person>()
   .HasIndex(idx => new { idx.FirstName, idx.LastName })
   .IsUnique();
در این حالت اگر HasName ذکر نشود، نام آن همانند الگویی است که پیشتر عنوان شد؛ با این تفاوت که قسمت property name آن، جمع نام تمام خواص ذکر شده و جدا شده‌ی با _ خواهد بود.

یک نکته: اگر از پروایدر SQL Server استفاده می‌کنید، می‌توان متد الحاقی ویژه‌ای را به نام ForSqlServerIsClustered نیز برای تعریف clustered indexes، در این زنجیره ذکر کرد.


امکان تعریف Alternate Keys در EF Core 1.0

به Unique Constraints در EF Core، نام Alternate Keys را داده‌اند و این مورد نیز تنها از طریق Fluent API قابل تنظیم است:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<Car>()
     .HasAlternateKey(c => c.LicensePlate)
     .HasName("AlteranteKey_LicensePlate");
}
برای یک Alternate Key به صورت خودکار هم ایندکس ایجاد می‌شود و هم اینکه این ایندکس منحصربفرد خواهد بود.
اگر متد HasName در اینجا ذکر نشود، نام پیش فرض آن  <type name>_<property name> خواهد بود و اگر همانند composite keys و یا ایندکس‌های ترکیبی، چند خاصیت ذکر شوند، قسمت property name به جمع نام تمام خواص ذکر شده و جدا شده‌ی با _ تنظیم می‌شود.
برای نمونه اگر یک Alternate Key ترکیبی را به صورت ذیل تعریف کنیم:
modelBuilder.Entity<Person>()
     .HasAlternateKey(x => new { x.FirstName, x.LastName });
در قسمت مهاجرت‌هایی که قرار است به بانک اطلاعاتی اعمال شوند، به یک UniqueConstraint ترجمه می‌شود:
 table.UniqueConstraint("AK_Persons_FirstName_LastName", x => new { x.FirstName, x.LastName });


سؤال: یک Unique Constraint با Unique Index چه تفاوتی دارد؟

در پشت صحنه، پیاده سازی یک Unique Constraint با Unique Index تفاوتی ندارند. فقط از دیدگاه روشن‌تر شدن مقصود، استفاده‌ی از Unique Constraint ترجیح داده می‌شود.
البته از دیدگاه بانک اطلاعاتی پیاده سازی کننده نیز برای نمونه SQL Server، این تفاوت‌ها وجود دارند:
الف) یک Unique Constraint را نمی‌توان غیرفعال کرد؛ برخلاف Unique Indexها.
ب) Unique Constraint‌ها موارد اضافه‌تری را مانند FILLFACTOR و IGNORE_DUP_KEY نیز می‌توانند تنظیم کنند.
ج) امکان تعریف فیلترها برای Unique Indexها وجود دارد؛ برخلاف Unique Constraint ها.

که البته از دیدگاه EF، این سه مورد اهمیتی ندارند و بیشتر روشن‌تر شدن مقصود، هدف اصلی آن‌ها است.
اشتراک‌ها
Primery Key از نوع GUID بجای نوع int جدول دیتابیس در مواقع وجود چندین دیتابیس
With the increasing use of Object-Relational Mapping (ORM) frameworks such as NHibernate and the ADO.NET Entity Framework, relying on the server to generate key values adds a lot of complication that most people would prefer to avoid 
Primery Key از نوع GUID بجای نوع int جدول دیتابیس در مواقع وجود چندین دیتابیس
اشتراک‌ها
Rider 2018.1 منتشر شد

Rider 2018.1 adds support for Roslyn analyzers and Entity Framework, introduces XAML preview, takes Unity integration to a whole new level, improves debugger with Memory view and other updates, evolves F# and NuGet support. 

Rider 2018.1 منتشر شد
اشتراک‌ها
orchard core دیگر یک وب سایت مدیریت محتوا نیست !

orchard core دیگر یک وب سایت مدیریت محتوا نیست !

در نسخه Core این پروژه ( که به صورت کامل باز طراحی و بازنویسی شده ) تبدیل به یک سایت ساز شده است .

It is a completely new thing written in ASP.NET Core. The Orchard CMS was designed as a CMS, but Orchard Core was designed to be an<<  application framework >> that can be used to build a CMS, a blog or whatever you want to build 

orchard core دیگر یک وب سایت مدیریت محتوا نیست !
مطالب
تاریخچه‌ی نگارش‌های مختلف دات نت فریم ورک

حدود 8 سال از ارائه اولین نگارش دات نت فریم ورک می‌گذرد و در ادامه مرور سریعی خواهیم داشت بر عناوین کتابخانه‌های اضافه شده به این مجموعه:



دات نت فریم ورک 1.0
اولین ارائه عمومی آزمایشی آن در PDC 2000 صورت گرفت و در اوایل 2002 به عموم عرضه شد (2/13/2002).
عبارت کد مدیریت شده را به دنیا معرفی کرد (managed code) و شامل اجزای زیر بود:
• GC, JIT
• C#
• Coherent Framework
• XSP….ASP+…ASP.NET!
• WinForms

دات نت فریم ورک 1.1
در اوایل 2003 به همراه ویندوز سرور 2003 و VS 2003 ارائه شد.
• Mobile ASP.NET controls
• Built-in support for ODBC and Oracle databases.
• IPv6 support.

دات نت فریم ورک 2
در اواخر 2005 ارائه شد (11/07/2005) و شامل تازه‌های زیر بود:
• ASP.NET for the Masses
○ Application Building Blocks
§ Parts, Authentication, Role Management, etc
○ Visual Web Developer
• Client Development
• ClickOnce!

دات نت فریم ورک 3
در اواخر 2006 ارائه شد (11/06/2006) و موارد زیر را به این فریم ورک افزود:

• Windows CardSpace - Digital identity interface.
• Windows Presentation Foundation : WPF
○ Vector Graphics, Media and UI
○ Enters the age of UX
• Windows Communication Foundation : WCF
○ Unified messaging model
• Windows Workflow Foundation : WF
○ Coordinating work with durable applications

دات نت فریم ورک 3.5
در پایان 2007 ارائه شد (11/19/2007) و تازه‌های زیر را به همراه داشت:
• Linq
• Expression Trees and Lamda Methods
• Extension Methods
• Paging Support for ADO.NET
• Managed Wrappers for WMI and AD
• Enhancements to WCF and WF
• System.CodeDom namespace
• ASP.NET AJAX
• WCF/WF
○ REST Services
○ Workflow Services
• Client
○ Sync
○ Client app services

در همین زمان نیز دات نت فریم ورک سورس باز شد.

سرویس پک یک دات نت فریم ورک 3.5
در اواسط 2008 ارائه شد (8/11/2008) و به همراه تغییرات زیر بود:
• ASP.NET Dynamic Data
• ADO.NET
○ Entity Framework
○ Data Services (Astoria)
• WCF
○ AtomPub ServiceDocuments
• Client
○ Client Profile
○ Performance
§ Working set and startup time
• Silverlight 2
○ RTM end of 2008
○ Brings power of .NET to the web client
○ Media and RIA .NET platform


دات نت فریم ورک 4.0
نگارش بتای آن در دسترس است و احتمالا نگارش نهایی آن در سال آینده‌ی میلادی به همراه VS2010 ارائه می‌شود. این مجموعه تازه‌های زیر را به همراه خواهد داشت:

• Base Class Library Improvements
○ Managed Extensibility Framework
○ More Core Data Structures
○ I/O Improvements
• Parallel Computing
○ Task Parallel Library
○ Parallel Linq (PLINQ)
○ Coordination Data Structures (CDS)
• Client
○ WPF
§ Client Profile
§ Business Focused Controls
§ Win7 Advances (Multi-touch, etc)
○ ADO.NET
§ Entity Framework v2
□ Code-First Development
□ TDD Support
□ Foreign-Key Support
○ ASP.NET
§ ASP.NET Dynamic Data Improvements
§ ASP.NET MVC
§ ASP.NET Dynamic Data for MVC
§ Extensible Caching Framework
○ WF & WCF
§ Fully Declarative Services
§ Workflow Enhancements
□ New flowchart modeling
□ Workflow Rules Integration
□ … much more…
§ WCF Enhancements
□ Durable Duplex
□ WS-Discovery & UDP Channel
□ In-Process Channel
§ RIA (Silverlight)
□ Simplified N-tier development
□ Business-focused framework

اشتراک‌ها
Entity Signal یا Entity Framework بلادرنگ

Entity Signal allows you to connect to Entity Framework through SignalR and get updates on subscribed data in real time. This lowers the number of refreshes done by the user and ultimatly allows you to do more with fewer servers. It is also requires minimal changes, so you can make realtime apps about as easily as you can make standard apps.  

Entity Signal یا Entity Framework بلادرنگ