نظرات مطالب
فعال سازی عملیات CRUD در Kendo UI Grid
در وضعیت ویرایش دسته ای، فیلدهای رکوردهای تغییر یافته به کنترلر آپدیت ارسال نمی‌شود بعبارتی پارامتر IEnumerable<Product> نال هست ولی وقتی گرید در وضعیت آپدیت دسته ای قرار ندارد پارامتر Product کنترلر آپدیت دارای مقدار است و امکان دسترسی به فیلد تغییر یافته وجود دارد. آیا باید بجز تغییر خاصیت batch: true کار دیگری نیاز است انجام شود؟
نظرات اشتراک‌ها
پیاده سازی generic repository یک ضد الگو است
خلاصه این مطلب خوب به این نحو است:
الف) اگر از یک Generic Repository داخل کدهای یک مثلا کنترلر مستقیما استفاده می‌کنید، فقط خودتان را گول زده‌اید!  این طراحی نشتی دارد و همچنین حد و مرز مشخصی را نمی‌توان برای آن قائل شد.
نشتی یا وابستگی که اینجا عنوان شده نمونه‌اش یک چنین متدی است:
IEnumerable<T> Find(Expression<Func<T, bool>> query);
در اینجا کار پیاده سازی query به یک لایه بالاتر (مانند یک کنترلر) محول شده که اشتباه است. حد و مرز لایه پیاده سازی کننده منطق تجاری شما باید مشخص باشد و نه باز. یک Expression فقط یک عبارت است که می‌توان با کمک آن انواع و اقسام کوئری‌های وابسته به فناوری دسترسی به داده‌های در حال استفاده را تهیه کرد. به عبارتی اگر تصور می‌کنید که در اینجا کپسوله سازی خوبی را انجام داده‌اید، اینطور نیست. چون دسترسی مستقیم به ORM خود را به لایه‌ای دیگر واگذار کرده‌اید و این دسترسی محصور نشده است.
نمونه خوبی را که مثال زده به صورت زیر است:
public IEnumerable<Customer> FindCustomerByName(string input)
    {
        return internalRepository.FindBy(c => c.Name == input);
    }
در اینجا دقیقا حد و مرز لایه تجاری و امکان دسترسی به آن مشخص است و دارای نشتی (باز بودن برای تغییرات بی حد و حصر در لایه‌های دیگر) نیست.
ب) الزامی به تعریف یک Generic Repository نیست اما اگر آن‌را تعریف کردید، باید این کلاس خاص داخل کدهایی که منطق تجاری برنامه را با حد و مرز مشخصی کپسوله می‌کنند استفاده کنید. نه اینکه آن‌را در خط مقدم کاری (مانند استفاده مستقیم در یک کنترلر) بکار بگیرید. به آن بیشتر به شکل یک سری متد کمکی نگاه کنید و بس. نامگذاری جالبی را هم در این حالت پیشنهاد داده: internalRepository 
مطالب
توسعه سرویس‌های مبتنی بر REST در AngularJs با استفاده از RestAngular : بخش اول
برای مطالعه‌ی این مقاله شما باید به مواردی از قبیل کتابخانه‌ی AngularJs، تعاملات بین کلاینت و سرور و همچنین معماری RESTful تسلط کافی داشته باشید و ما از توضیح و تفصیلی این سرفصل‌ها اجتناب میکنیم.
خیلی خوب بپردازیم به اصل مطلب:

Restangular  چیست؟

کتابخانه RestAngular بنا به گفته ناشر در مستندات Github آن، یک سرویس توسعه داده شده AngularJs می‌باشد که کد‌های نوشته شده‌ی برای پیاده سازی فرآیند‌های Request/Response کلاینت و سرور را به حداقل رسانده است. این فرآیندها در قالب درخواست‌های GET، POST، DELETE و Update صورت می‌پذیرند. این سرویس برای وب اپلیکیشن‌هایی که که داده‌های خود را از API‌های RESTful  همانند Microsoft ASP.NET Web API 2 دریافت می‌کنند نیز بسیار کارآمد است.
کد زیر یک فرآیند درخواست GET را نمایش می‌دهد که در آن از سرویس RestAngular استفاده شده است:
// Restangular returns promises
Restangular.all('users').getList()  // GET: /users
.then(function(users) {
  // returns a list of users
  $scope.user = users[0]; // first Restangular obj in list: { id: 123 }
})

// Later in the code...

// Restangular objects are self-aware and know how to make their own RESTful requests
$scope.user.getList('cars');  // GET: /users/123/cars

// You can also use your own custom methods on Restangular objects
$scope.user.sendMessage();  // POST: /users/123/sendMessage

// Chain methods together to easily build complex requests
$scope.user.one('messages', 123).one('from', 123).getList('unread');
// GET: /users/123/messages/123/from/123/unread
همانطور که ملاحظه میکنید تمام پروسه‌ی دریافت، در یک خط خلاصه شده است. همچنین می‌بینید که restAngular دارای Promise نیز هست. در تکه کد اول، تمامی userها به صورت Get دریافت می‌شوند. در تکه کد دوم مشاهده می‌کنید که عملیات درخواست لیست ماشین‌های کاربر چگونه در یک خط خلاصه می‌گردد. حال فرض کنید قصد داشتیم تا این عملیات را با وابستگی http پیاده سازی نماییم. برای این کار باید چندین خط کد را درون یک سرویس جا می‌دادیم و آنگاه از متد سرویس در کنترلر استفاده می‌کردیم.
در اینجا همچنین قادرید درخواست‌های خود را به صورت سفارشی نیز اعمال کنید (تکه کد سوم) و در آخر مشاهده می‌کنید که پیچیده‌ترین عملیات‌های درخواست کاربر را می‌توان در یک خط خلاصه نمود. برای نمونه کد آخر یک دستور GET تو در تو را به چه سادگی پیاده سازی کرده است.

وابستگی ها

باید توجه داشته باشید که برای استفاده از RestAngular نیاز به کتابخانه‌ی Lodash نیز می‌باشد.

شروع پروژه

برای شروع پروژه و استفاده از RestAngular، پس از اضافه کردن اسکریپت رفرنس‌ها و وابستگی‌ها (lodash و AngularJs) باید وابستگی restAngular را به صورت زیر به angular.module اضافه نمایید:
// Add Restangular as a dependency to your app
angular.module('your-app', ['restangular']);

// Inject Restangular into your controller
angular.module('your-app').controller('MainCtrl', function($scope, Restangular) {
  // ...
});
توجه داشته باشید هنگامیکه یک وابستگی به module اضافه می‌گردد، با حروف کوچک یعنی restangular اضافه می‌گردد؛ اما هنگامیکه به کنترلر اضافه می‌شود، به صورت Restangular و با R بزرگ اضافه می‌گردد.

برخی از متدهای RestAngular

در این بخش قصد داریم تا برخی از پر کاربرد‌ترین متد‌های RestAngular را تشریح کنیم:
 نام متد  پارامتر‌های ارسالی  توضیحات
 one
 route, id
 این متد یک RestAngular object ایجاد میکند که از آدرسی که در route قرار داده شده با id مشخص دریافت میشود.
 all
 route
 این متد یک RestAngular object که لیستی از المنت هایی را که در آدرس route قرار دارد، دریافت می‌نماید.
 oneUrl
 route, url
 این متد یک RestAngular object ایجاد می‌کند که یک المنت از url خاصی را بازگشت میدهد.
مانند: Restangular.oneUrl( 'googlers' , 'http://www.google.com/1' ).get(); 
 allUrl
 route, url
 این متد مانند متد قبل است با این تفاوت که یک مجموعه را بازگشت میدهد.
 copy
 formElement
 این متد یک کپی از المنت‌های یک فرم را ایجاد میکند که ما میتوانیم آنها را تغییر دهیم.
 restangularizeElement
 parent,element, route, queryParams
 یک المنت جدید را به صورت Restangularize تغییر میدهد.
 restangularizeCollection
 parent, element, route, queryParams
 یک کالکشن Collection را به صورت Restangularize تغییر میدهد.
در این قسمت قصد داشتیم تا شما را با این کتابخانه کمی آشنا کنیم. در قسمت بعدی سعی می‌کنیم تا با مثال‌هایی کاربردی، شما را بیشتر با این سرویس خارق العاده آشنا کنیم.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 18 - کار با ASP.NET Web API
ارتقاء به ASP.NET Core 2.1

اگر Controller شما صرفا کار Web API را انجام می‌دهد، می‌توانید آن‌را به ویژگی جدید ApiController مزین کنید:
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
پس از این تغییر، این ویژگی‌ها را به صورت خودکار و بدون کد نویسی اضافه‌تری دریافت خواهید کرد:
الف) دیگر نیازی نیست تا ModelState.IsValid را بررسی کنید و در صورت معتبر نبودن آن برای مثال BadRequest(ModelState) را بازگشت دهید. این‌کار به صورت خودکار، در صورت بروز شکست اعتبارسنجی، انجام خواهد شد.
ب) دیگر نیازی به ذکر صریح ویژگی‌های FromBody ،FromRoute و FromQuery برای model binding به پارامترهای یک اکشن متد ندارید و در این زمینه به صورت هوشمند‌تری عمل می‌شود.

البته باید دقت داشت که این ویژگی تنها با attribute routing کار می‌کند و نه حالت convention-based routes.
نظرات مطالب
ASP.NET MVC #5
اشاره‌گر رو داخل اکشن متد کنترلر قرار بدید و بعد پروژه رو در دیباگر VS.NET اجرا کنید. مراحل کاری ASP.NET MVC از View شروع نمی‌شود. مراجعه کنید به «تفاوت مهم پردازشی ASP.NET MVC با ASP.NET Web forms» در قسمت دوم این سری.
نظرات اشتراک‌ها
تبدیلگر ایران سیستم به یونیکد
من متد UnicodeFrom را به شکل زیر تغییر دادم الان درست کار میکنه: 
   public static string UnicodeFrom(TextEncoding textEncoding, string iranSystemEncodedString)
        {
            // وهله سازی از انکودینگ صحیح برای تبدیل رشته ایران سیستم به بایت
            Encoding encoding = Encoding.GetEncoding((int)textEncoding);

            // حذف فاصله‌های موجود در رشته
            iranSystemEncodedString = iranSystemEncodedString.Replace(" ", "");
            if (iranSystemEncodedString.Length <= 0)
                return "";
            // تبدیل رشته به بایت
            byte[] stringBytes = encoding.GetBytes(iranSystemEncodedString.Trim());

            // تغییر ترتیب بایت هااز آخر به اول در صورتی که رشته تماماً عدد نباشد
            if (!IsNumber(iranSystemEncodedString))
            {
                stringBytes = stringBytes.Reverse().ToArray();
            }

            // آرایه ای که بایت‌های معادل را در آن قرار می‌دهیم
            // مجموع تعداد بایت‌های رشته + بایت‌های اضافی محاسبه شده
            byte[] newStringBytes = new byte[stringBytes.Length + CountCharactersRequireTwoBytes(stringBytes)];

            int index = 0;

            // بررسی هر بایت و پیدا کردن بایت (های) معادل آن
            for (int i = 0; i < stringBytes.Length; ++i)
            {
                byte charByte = stringBytes[i];

                // اگر جز 128 بایت اول باشد که نیازی به تبدیل ندارد چون کد اسکی است
                if (charByte < 128)
                {
                    newStringBytes[index] = charByte;
                }
                else
                {
                    // اگر جز حروف یا اعداد بود معادلش رو قرار می‌دیم
                    if (CharactersMapper.ContainsKey(charByte))
                    {
                        newStringBytes[index] = CharactersMapper[charByte];
                    }
                }

                // اگر کاراکتر ایران سیستم "لا" بود چون کاراکتر متناظرش در عربی 1256 "ل" است و باید یک "ا" هم بعدش اضافه کنیم
                if (charByte == 242)
                {
                    newStringBytes[++index] = 199;
                }

                // اگر کاراکتر یکی از انواعی بود که بعدشان باید یک فاصله باشد
                // و در عین حال آخرین کاراکتر رشته نبود
                if (charactersWithSpaceAfter.Contains(charByte) && Array.IndexOf(stringBytes, charByte) != stringBytes.Length - 1)
                {
                    // یک فاصله بعد ان اضافه می‌کنیم
                    newStringBytes[++index] = 32;
                }

                index += 1;
            }

            // تبدیل به رشته و ارسال به فراخواننده
            byte[] unicodeContent = Encoding.Convert(encoding, Encoding.Unicode, newStringBytes);

            string result = Encoding.Unicode.GetString(unicodeContent).Trim();
            result = result.Replace("ڑ", "ء").Replace("ؤ", "ئ");

            //در صورتی که عدد داخل رشته نیست نیاز به ادامه کار نمی‌باشد
            if (!Regex.IsMatch(result, @"\d"))
                return result;

            bool isLastDigit = false;
            string tempForDigits = "";
            string str="";
            for (int i = 0; i < result.Length; i++)
            {
                if (Regex.IsMatch(result[i].ToString(), @"\d") || (i+1<result.Length && Regex.IsMatch(result[i].ToString() + result[i+1].ToString(), @"/\d")))
                {
                    isLastDigit = true;
                    tempForDigits += result[i];
                }
                else
                {
                    if (isLastDigit && tempForDigits.Length > 0)
                    {
                        str += new string(tempForDigits.Reverse().ToArray());
                        isLastDigit = false;
                        tempForDigits = "";
                    }
                     str += result[i];
                }
                if (!String.IsNullOrWhiteSpace(tempForDigits) && i == result.Length - 1)
                {
                    str += new string(tempForDigits.Reverse().ToArray());
                }
            }
            return str;
        }
مطالب
EF Code First #4

آشنایی با Code first migrations

ویژگی Code first migrations برای اولین بار در EF 4.3 ارائه شد و هدف آن سهولت هماهنگ سازی کلاس‌های مدل برنامه با بانک اطلاعاتی است؛ به صورت خودکار یا با تنظیمات دقیق دستی.

همانطور که در قسمت‌های قبل نیز به آن اشاره شد، تا پیش از EF 4.3، پنج روال جهت آغاز به کار با بانک اطلاعاتی در EF code first وجود داشت و دارد:
1) در اولین بار اجرای برنامه، در صورتیکه بانک اطلاعاتی اشاره شده در رشته اتصالی وجود خارجی نداشته باشد، نسبت به ایجاد خودکار آن اقدام می‌گردد. اینکار پس از وهله سازی اولین DbContext و همچنین صدور یک کوئری به بانک اطلاعاتی انجام خواهد شد.
2) DropCreateDatabaseAlways : همواره پس از شروع برنامه، ابتدا بانک اطلاعاتی را drop کرده و سپس نمونه جدیدی را ایجاد می‌کند.
3) DropCreateDatabaseIfModelChanges : اگر EF Code first تشخیص دهد که تعاریف مدل‌های شما با بانک اطلاعاتی مشخص شده توسط رشته اتصالی، هماهنگ نیست، آن‌را drop کرده و نمونه جدیدی را تولید می‌کند.
4) با مقدار دهی پارامتر متد System.Data.Entity.Database.SetInitializer به نال، می‌توان فرآیند آغاز خودکار بانک اطلاعاتی را غیرفعال کرد. در این حالت شخص می‌تواند تغییرات انجام شده در کلاس‌های مدل برنامه را به صورت دستی به بانک اطلاعاتی اعمال کند.
5) می‌توان با پیاده سازی اینترفیس IDatabaseInitializer، یک آغاز کننده بانک اطلاعاتی سفارشی را نیز تولید کرد.

اکثر این روش‌ها در حین توسعه یک برنامه یا خصوصا جهت سهولت انجام آزمون‌های خودکار بسیار مناسب هستند، اما به درد محیط کاری نمی‌خورند؛ زیرا drop یک بانک اطلاعاتی به معنای از دست دادن تمام اطلاعات ثبت شده در آن است. برای رفع این مشکل مهم، مفهومی به نام «Migrations» در EF 4.3 ارائه شده است تا بتوان بانک اطلاعاتی را بدون تخریب آن، بر اساس اطلاعات تغییر کرده‌ی کلاس‌های مدل برنامه، تغییر داد. البته بدیهی است زمانیکه توسط NuGet نسبت به دریافت و نصب EF اقدام می‌شود، همواره آخرین نگارش پایدار که حاوی اطلاعات و فایل‌های مورد نیاز جهت کار با «Migrations» است را نیز دریافت خواهیم کرد.


تنظیمات ابتدایی Code first migrations

در اینجا قصد داریم همان مثال قسمت قبل را ادامه دهیم. در آن مثال از یک نمونه سفارشی سازی شده DropCreateDatabaseAlways استفاده شد.
نیاز است از منوی Tools در ویژوال استودیو، گزینه‌ Library package manager آن، گزینه package manager console را انتخاب کرد تا کنسول پاورشل NuGet ظاهر شود.
اطلاعات مرتبط با پاورشل EF، به صورت خودکار توسط NuGet نصب می‌شود. برای مثال جهت مشاهده آن‌ها به مسیر packages\EntityFramework.4.3.1\tools در کنار پوشه پروژه خود مراجعه نمائید.
در ادامه در پایین صفحه، زمانیکه کنسول پاورشل NuGet ظاهر می‌شود، ابتدا باید دقت داشت که قرار است فرامین را بر روی چه پروژه‌ای اجرا کنیم. برای مثال اگر تعاریف DbContext را به یک اسمبلی و پروژه class library مجزا انتقال داده‌اید، گزینه Default project را در این قسمت باید به این پروژه مجزا، تغییر دهید.
سپس در خط فرمان پاور شل، دستور enable-migrations را وارد کرده و دکمه enter را فشار دهید.
پس از اجرای این دستور، یک سری اتفاقات رخ خواهد داد:
الف) پوشه‌ای به نام Migrations به پروژه پیش فرض مشخص شده در کنسول پاورشل، اضافه می‌شود.
ب) دو کلاس جدید نیز در آن پوشه تعریف خواهند شد به نام‌های Configuration.cs و یک نام خودکار مانند number_InitialCreate.cs
ج) در کنسول پاور شل، پیغام زیر ظاهر می‌گردد:
Detected database created with a database initializer. Scaffolded migration '201205050805256_InitialCreate' 
corresponding to current database schema. To use an automatic migration instead, delete the Migrations
folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter.

با توجه به اینکه در مثال قسمت سوم، از آغاز کننده سفارشی سازی شده DropCreateDatabaseAlways استفاده شده بود، اطلاعات آن در جدول سیستمی dbo.__MigrationHistory در بانک اطلاعاتی برنامه موجود است (تصویری از آن‌را در قسمت اول این سری مشاهده کردید). سپس با توجه به ساختار بانک اطلاعاتی جاری، دو کلاس خودکار زیر را ایجاد کرده است:

namespace EF_Sample02.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;

internal sealed class Configuration : DbMigrationsConfiguration<EF_Sample02.Sample2Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}

protected override void Seed(EF_Sample02.Sample2Context context)
{
// This method will be called after migrating to the latest version.

// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
}

namespace EF_Sample02.Migrations
{
using System.Data.Entity.Migrations;

public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"Users",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
LastName = c.String(),
Email = c.String(),
Description = c.String(),
Photo = c.Binary(),
RowVersion = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
Interests_Interest1 = c.String(maxLength: 450),
Interests_Interest2 = c.String(maxLength: 450),
AddDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.Id);

CreateTable(
"Projects",
c => new
{
Id = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 50),
Description = c.String(),
RowVesrion = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
AddDate = c.DateTime(nullable: false),
AdminUser_Id = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("Users", t => t.AdminUser_Id)
.Index(t => t.AdminUser_Id);

}

public override void Down()
{
DropIndex("Projects", new[] { "AdminUser_Id" });
DropForeignKey("Projects", "AdminUser_Id", "Users");
DropTable("Projects");
DropTable("Users");
}
}
}


در این کلاس خودکار، نحوه ایجاد جداول بانک اطلاعاتی تعریف شده‌اند. در متد تحریف شده Up، کار ایجاد بانک اطلاعاتی و در متد تحریف شده Down، دستورات حذف جداول و قیود ذکر شده‌اند.
به علاوه اینبار متد Seed را در کلاس مشتق شده از DbMigrationsConfiguration، می‌توان تحریف و مقدار دهی کرد.
علاوه بر این‌ها جدول سیستمی dbo.__MigrationHistory نیز با اطلاعات جاری مقدار دهی می‌گردد.


فعال سازی گزینه‌های مهاجرت خودکار

برای استفاده از این کلاس‌ها، ابتدا به فایل Configuration.cs مراجعه کرده و خاصیت AutomaticMigrationsEnabled را true‌ کنید:

internal sealed class Configuration : DbMigrationsConfiguration<EF_Sample02.Sample2Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}

پس از آن EF به صورت خودکار کار استفاده و مدیریت «Migrations» را عهده‌دار خواهد شد. البته برای این منظور باید نوع آغاز کننده بانک اطلاعاتی را از DropCreateDatabaseAlways قبلی به نمونه جدید MigrateDatabaseToLatestVersion نیز تغییر دهیم:
//Database.SetInitializer(new Sample2DbInitializer());
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Sample2Context, Migrations.Configuration>());

یک نکته:
کلاس Migrations.Configuration که باید در حین وهله سازی از MigrateDatabaseToLatestVersion قید شود (همانند کدهای فوق)، از نوع internal sealed معرفی شده است. بنابراین اگر این کلاس را در یک اسمبلی جداگانه قرار داده‌اید، نیاز است فایل را ویرایش کرده و internal sealed آن‌را به public تغییر دهید.

روش دیگر معرفی کلاس‌های Context و Migrations.Configuration، حذف متد Database.SetInitializer و استفاده از فایل app.config یا web.config است به نحو زیر ( در اینجا حرف ` اصطلاحا back tick نام دارد. فشردن دکمه ~ در حین تایپ انگلیسی):

<entityFramework>
<contexts>
<context type="EF_Sample02.Sample2Context, EF_Sample02">
<databaseInitializer
type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[EF_Sample02.Sample2Context, EF_Sample02],
[EF_Sample02.Migrations.Configuration, EF_Sample02]], EntityFramework"
/>
</context>
</contexts>
</entityFramework>

آزمودن ویژگی مهاجرت خودکار

اکنون برای آزمایش این موارد، یک خاصیت دلخواه را به کلاس Project به نام public string SomeProp اضافه کنید. سپس برنامه را اجرا نمائید.
در ادامه به بانک اطلاعاتی مراجعه کرده و فیلدهای جدول Projects را بررسی کنید:

CREATE TABLE [dbo].[Projects](
---...
[SomeProp] [nvarchar](max) NULL,
---...

بله. اینبار فیلد SomeProp بدون از دست رفتن اطلاعات و drop بانک اطلاعاتی، به جدول پروژه‌ها اضافه شده است.


عکس العمل ویژگی مهاجرت خودکار در مقابل از دست رفتن اطلاعات

در ادامه، خاصیت public string SomeProp را که در قسمت قبل به کلاس پروژه اضافه کردیم، حذف کنید. اکنون مجددا برنامه را اجرا نمائید. برنامه بلافاصله با استثنای زیر متوقف خواهد شد:

Automatic migration was not applied because it would result in data loss.

از آنجائیکه حذف یک خاصیت مساوی است با حذف یک ستون در جدول بانک اطلاعاتی، امکان از دست رفتن اطلاعات در این بین بسیار زیاد است. بنابراین ویژگی مهاجرت خودکار دیگر اعمال نخواهد شد و این مورد به نوعی یک محافظت خودکار است که درنظر گرفته شده است.
البته در EF Code first این مساله را نیز می‌توان کنترل نمود. به کلاس Configuration اضافه شده توسط پاورشل مراجعه کرده و خاصیت AutomaticMigrationDataLossAllowed را به true تنظیم کنید:

internal sealed class Configuration : DbMigrationsConfiguration<EF_Sample02.Sample2Context>
{
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
this.AutomaticMigrationDataLossAllowed = true;
}

این تغییر به این معنا است که خودمان صریحا مجوز حذف یک ستون و اطلاعات مرتبط به آن‌را صادر کرده‌ایم.
پس از این تغییر، مجددا برنامه را اجرا کنید. ستون SomeProp به صورت خودکار حذف خواهد شد، اما اطلاعات رکوردهای موجود تغییری نخواهند کرد.


استفاده از Code first migrations بر روی یک بانک اطلاعاتی موجود

تفاوت یک دیتابیس موجود با بانک اطلاعاتی تولید شده توسط EF Code first در نبود جدول سیستمی dbo.__MigrationHistory است.
به این ترتیب زمانیکه فرمان enable-migrations را در یک پروژه EF code first متصل به بانک اطلاعاتی قدیمی موجود اجرا می‌کنیم، پوشه Migration در آن ایجاد خواهد شد اما تنها حاوی فایل Configuration.cs است و نه فایلی شبیه به number_InitialCreate.cs .
بنابراین نیاز است به صورت صریح به EF اعلام کنیم که نیاز است تا جدول سیستمی dbo.__MigrationHistory و فایل number_InitialCreate.cs را نیز تولید کند. برای این منظور کافی است دستور زیر را در خط فرمان پاورشل NuGet پس از فراخوانی enable-migrations اولیه، اجرا کنیم:
add-migration Initial -IgnoreChanges

با بکارگیری پارامتر IgnoreChanges، متد Up در فایل number_InitialCreate.cs تولید نخواهد شد. به این ترتیب نگران نخواهیم بود که در اولین بار اجرای برنامه، تعاریف دیتابیس موجود ممکن است اندکی تغییر کند.
سپس دستور زیر را جهت به روز رسانی جدول سیستمی dbo.__MigrationHistory اجرا کنید:
update-database

پس از آن جهت سوئیچ به مهاجرت خودکار، خاصیت AutomaticMigrationsEnabled = true را در فایل Configuration.cs همانند قبل مقدار دهی کنید.


مشاهده دستوارت SQL به روز رسانی بانک اطلاعاتی

اگر علاقمند هستید که دستورات T-SQL به روز رسانی بانک اطلاعاتی را نیز مشاهده کنید، دستور Update-Database را با پارامتر Verbose آغاز نمائید:
Update-Database -Verbose

و اگر تنها نیاز به مشاهده اسکریپت تولیدی بدون اجرای آن‌ها بر روی بانک اطلاعاتی مدنظر است، از پارامتر Script باید استفاده کرد:
update-database -Script



نکته‌ای در مورد جدول سیستمی dbo.__MigrationHistory

تنها دلیلی که این جدول در SQL Server البته (ونه برای مثال در SQL Server CE) به صورت سیستمی معرفی می‌شود این است که «جلوی چشم نباشد»! به این ترتیب در SQL Server management studio در بین سایر جداول معمولی بانک اطلاعاتی قرار نمی‌گیرد. اما برای EF تفاوتی نمی‌کند که این جدول سیستمی است یا خیر.
همین سیستمی بودن آن ممکن است بر اساس سطح دسترسی کاربر اتصالی به بانک اطلاعاتی مساله ساز شود. برای نمونه ممکن است schema کاربر متصل dbo نباشد. همینجا است که کار به روز رسانی این جدول متوقف خواهد شد.
بنابراین اگر قصد داشتید خواص سیستمی آن‌را لغو کنید، تنها کافی است دستورات T-SQL زیر را در SQL Server اجرا نمائید:

SELECT * INTO [TempMigrationHistory]
FROM [__MigrationHistory]
DROP TABLE [__MigrationHistory]
EXEC sp_rename [TempMigrationHistory], [__MigrationHistory]


ساده سازی پروسه مهاجرت خودکار

کل پروسه‌ای را که در این قسمت مشاهده کردید، به صورت ذیل نیز می‌توان خلاصه کرد:

using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Infrastructure;
using System.IO;

namespace EF_Sample02
{
public class Configuration<T> : DbMigrationsConfiguration<T> where T : DbContext
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
}

public class SimpleDbMigrations
{
public static void UpdateDatabaseSchema<T>(string SQLScriptPath = "script.sql") where T : DbContext
{
var configuration = new Configuration<T>();
var dbMigrator = new DbMigrator(configuration);
saveToFile(SQLScriptPath, dbMigrator);
dbMigrator.Update();
}

private static void saveToFile(string SQLScriptPath, DbMigrator dbMigrator)
{
if (string.IsNullOrWhiteSpace(SQLScriptPath)) return;

var scriptor = new MigratorScriptingDecorator(dbMigrator);
var script = scriptor.ScriptUpdate(sourceMigration: null, targetMigration: null);
File.WriteAllText(SQLScriptPath, script);
Console.WriteLine(script);
}
}
}

سپس برای استفاده از آن خواهیم داشت:

SimpleDbMigrations.UpdateDatabaseSchema<Sample2Context>();

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



تا اینجا مهاجرت خودکار را بررسی کردیم. در قسمت بعدی Code-Based Migrations را ادامه خواهیم داد.
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 19 - بومی سازی
- کار رندر اطلاعات در ASP.NET MVC از اکشن متد یک کنترلر شروع می‌شود و به View ختم خواهد شد. سفارشی سازی آن توسط اکشن‌فیلترها میسر است:
    public class FaLanguageActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            CultureInfo.CurrentCulture = new CultureInfo("fa-IR");
            CultureInfo.CurrentUICulture = new CultureInfo("fa-IR");
            base.OnActionExecuting(context);
        }
    }
برای توضیحات بیشتر و نحو‌ه‌ی ثبت سراسری آن‌ها مراجعه کنید به مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 20 - بررسی تغییرات فیلترها»
+ برای تنظیم اطلاعات تکراری Viewها می‌توان کلاس پایه آن‌ها را نیز سفارشی سازی کرد:
    public abstract class MyCustomBaseView<TModel> : RazorPage<TModel>
    {
        protected MyCustomBaseView()
        {
            CultureInfo.CurrentCulture = new CultureInfo("fa-IR");
            CultureInfo.CurrentUICulture = new CultureInfo("fa-IR");
        }
یک مثال تکمیلی
نظرات مطالب
رمزنگاری و رمزگشایی خودکار خواص مدل‌ها در ASP.NET Core
- آیا برای رندر یک partial view حتما باید return PartialView نوشت؟ خیر. همان return View هم در اینجا کار می‌کند.
- اگر اصرار به نوشتن return PartialView بود، یک سطر PartialViewResult زیر را اضافه کنید:
namespace EncryptedModelBinder.Utils
{
    public class EncryptedFieldResultFilter : ResultFilterAttribute
    {
        // ...

        public override void OnResultExecuting(ResultExecutingContext context)
        {
            var model = context.Result switch
            {
                PageResult pageResult => pageResult.Model, // For Razor pages
                ViewResult viewResult => viewResult.Model, // For MVC Views
                PartialViewResult partialViewResult => partialViewResult.Model, // For `return PartialView`
                ObjectResult objectResult => objectResult.Value, // For Web API results
                _ => null
            };