نظرات مطالب
ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap
قالب پیش فرض Create از nested properties یا complex properties پشتیبانی نمی‌کند. (البته نه به این معنا که اگر دستی اضافه شوند کار نمی‌کند)
+ یک راه حل برای این نوع اشیاء توسط یکی از اعضای سابق تیم ASP.NET MVC
و یا اگر نیاز به اطلاعات بیشتری داشتید، پروژه MvcScaffolding یک چنین روابطی رو پشتیبانی می‌کند: «MvcScaffolding: One-to-Many Relationships»
نظرات مطالب
EF Code First #11
{ ORMهای با پشتیبانی از IQueryable ، پشتیبانی یکسانی از متدهای الحاقی تعریف شده ندارد.}
مثال واقعی و زنده ای که همین الان میشه زد تفاوت پیاده سازی L2E در NH و EF هست. عملا مهاجرت غیر ممکنه مگه از روشی که آقای نصیری گفتن. (دیدم که میگم ;) )
نظرات مطالب
Subversion 1.6.0
در واقع دلیل استفاده از ویژوال استودیو 6 پشتیبانی از برنامه ای هست که کاربران زیادی داره و متعهد به پشتیبانی اون هستیم و با وجود اینکه مدتی هست در حال انتقال سکو هستیم هنوز نتونستیم مشتریان رو قانع کنیم تا نرم افزار رو ارتقا بدهند.
ممنون از پاسخ و کمک شما
مطالب
EF Code First #8

ادامه بحث بررسی جزئیات نحوه نگاشت کلاس‌ها به جداول، توسط EF Code first


استفاده از Viewهای SQL Server در EF Code first

از Viewها عموما همانند یک جدول فقط خواندنی استفاده می‌شود. بنابراین نحوه نگاشت اطلاعات یک کلاس به یک View دقیقا همانند نحوه نگاشت اطلاعات یک کلاس به یک جدول است و تمام نکاتی که تا کنون بررسی شدند، در اینجا نیز صادق است. اما ...
الف) بر اساس تنظیمات توکار EF Code first، نام مفرد کلاس‌ها، حین نگاشت به جداول، تبدیل به اسم جمع می‌شوند. بنابراین اگر View ما در سمت بانک اطلاعاتی چنین تعریفی دارد:
Create VIEW EmployeesView
AS
SELECT id,
FirstName
FROM Employees

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

using System.ComponentModel.DataAnnotations;

namespace EF_Sample04.Models
{
[Table("EmployeesView")]
public class EmployeesView
{
public int Id { set; get; }
public string FirstName { set; get; }
}
}

در اینجا به کمک ویژگی Table، نام دقیق این View را در بانک اطلاعاتی مشخص کرده‌ایم. به این ترتیب تنظیمات توکار EF بازنویسی خواهد شد و دیگر به دنبال EmployeesViews نخواهد گشت؛ یا جدول متناظر با آن‌را به صورت خودکار ایجاد نخواهد کرد.
ب) View شما نیاز است دارای یک فیلد Primary key نیز باشد.
ج) اگر از مهاجرت خودکار توسط MigrateDatabaseToLatestVersion استفاده کنیم، پیغام خطای زیر را دریافت خواهیم کرد:

There is already an object named 'EmployeesView' in the database.

علت این است که هنوز جدول dbo.__MigrationHistory از وجود آن مطلع نشده است، زیرا یک View، خارج از برنامه و در سمت بانک اطلاعاتی اضافه می‌شود.
برای حل این مشکل می‌توان همانطور که در قسمت‌های قبل نیز عنوان شد، EF را طوری تنظیم کرد تا کاری با بانک اطلاعاتی نداشته باشد:

Database.SetInitializer<Sample04Context>(null);

به این ترتیب EmployeesView در همین لحظه قابل استفاده است.
و یا به حالت امن مهاجرت دستی سوئیچ کنید:
Add-Migration Init -IgnoreChanges
Update-Database

پارامتر IgnoreChanges سبب می‌شود تا متدهای Up و Down کلاس مهاجرت تولید شده، خالی باشد. یعنی زمانیکه دستور Update-Database انجام می‌شود، نه Viewایی دراپ خواهد شد و نه جدول اضافه‌ای ایجاد می‌گردد. فقط جدول dbo.__MigrationHistory به روز می‌شود که هدف اصلی ما نیز همین است.
همچنین در این حالت کنترل کاملی بر روی کلاس‌های Up و Down وجود دارد. می‌توان CreateTable اضافی را به سادگی از این کلاس‌ها حذف کرد.

ضمن اینکه باید دقت داشت یکی از اهداف کار با ORMs، فراهم شدن امکان استفاده از بانک‌های اطلاعاتی مختلف، بدون اعمال تغییری در کدهای برنامه می‌باشد (فقط تغییر کانکشن استرینگ، به علاوه تعیین Provider جدید، باید جهت این مهاجرت کفایت کند). بنابراین اگر از View استفاده می‌کنید، این برنامه به SQL Server گره خواهد خورد و دیگر از سایر بانک‌های اطلاعاتی که از این مفهوم پشتیبانی نمی‌کنند، نمی‌توان به سادگی استفاده کرد.



استفاده از فیلدهای XML اس کیوال سرور

در حال حاضر پشتیبانی توکاری توسط EF Code first از فیلدهای ویژه XML اس کیوال سرور وجود ندارد؛ اما استفاده از آن‌ها با رعایت چند نکته ساده، به نحو زیر است:

using System.ComponentModel.DataAnnotations;
using System.Xml.Linq;

namespace EF_Sample04.Models
{
public class MyXMLTable
{
public int Id { get; set; }

[Column(TypeName = "xml")]
public string XmlValue { get; set; }

[NotMapped]
public XElement XmlValueWrapper
{
get { return XElement.Parse(XmlValue); }
set { XmlValue = value.ToString(); }
}
}
}


در اینجا توسط TypeName ویژگی Column، نوع توکار xml مشخص شده است. این فیلد در طرف کدهای کلاس‌های برنامه، به صورت string تعریف می‌شود. سپس اگر نیاز بود به این خاصیت توسط LINQ to XML دسترسی یافت، می‌توان یک فیلد محاسباتی را همانند خاصیت XmlValueWrapper فوق تعریف کرد. نکته‌ دیگری را که باید به آن دقت داشت، استفاده از ویژگی NotMapped می‌باشد، تا EF سعی نکند خاصیتی از نوع XElement را (یک CLR Property) به بانک اطلاعاتی نگاشت کند.

و همچنین اگر علاقمند هستید که این قابلیت به صورت توکار اضافه شود، می‌توانید اینجا رای دهید!



نحوه تعریف Composite keys در EF Code first

کلاس نوع فعالیت زیر را درنظر بگیرید:

namespace EF_Sample04.Models
{
public class ActivityType
{
public int UserId { set; get; }
public int ActivityID { get; set; }
}
}

در جدول متناظر با این کلاس، نباید دو رکورد تکراری حاوی شماره کاربری و شماره فعالیت یکسانی باهم وجود داشته باشند. بنابراین بهتر است بر روی این دو فیلد، یک کلید ترکیبی تعریف کرد:

using System.Data.Entity.ModelConfiguration;
using EF_Sample04.Models;

namespace EF_Sample04.Mappings
{
public class ActivityTypeConfig : EntityTypeConfiguration<ActivityType>
{
public ActivityTypeConfig()
{
this.HasKey(x => new { x.ActivityID, x.UserId });
}
}
}

در اینجا نحوه معرفی بیش از یک کلید را در متد HasKey ملاحظه می‌کنید.

یک نکته:
اینبار اگر سعی کنیم مثلا از متد db.ActivityTypes.Find با یک پارامتر استفاده کنیم، پیغام خطای «The number of primary key values passed must match number of primary key values defined on the entity» را دریافت خواهیم کرد. برای رفع آن باید هر دو کلید، در این متد قید شوند:

var activity1 = db.ActivityTypes.Find(4, 1);

ترتیب آن‌ها هم بر اساس ترتیبی که در کلاس ActivityTypeConfig، ذکر شده است، مشخص می‌گردد. بنابراین در این مثال، اولین پارامتر متد Find، به ActivityID اشاره می‌کند و دومین پارامتر به UserId.


بررسی نحوه تعریف نگاشت جداول خود ارجاع دهنده (Self Referencing Entity)

سناریوهای کاربردی بسیاری را جهت جداول خود ارجاع دهنده می‌توان متصور شد و عموما تمام آن‌ها برای مدل سازی اطلاعات چند سطحی کاربرد دارند. برای مثال یک کارمند را درنظر بگیرید. مدیر این شخص هم یک کارمند است. مسئول این مدیر هم یک کارمند است و الی آخر. نمونه دیگر آن، طراحی منوهای چند سطحی هستند و یا یک مشتری را درنظر بگیرید. مشتری دیگری که توسط این مشتری معرفی شده است نیز یک مشتری است. این مشتری نیز می‌تواند یک مشتری دیگر را به شما معرفی کند و این سلسله مراتب به همین ترتیب می‌تواند ادامه پیدا کند.
در طراحی بانک‌های اطلاعاتی، برای ایجاد یک چنین جداولی، یک کلید خارجی را که به کلید اصلی همان جدول اشاره می‌کند، ایجاد خواهند کرد؛ اما در EF Code first چطور؟

using System.Collections.Generic;

namespace EF_Sample04.Models
{
public class Employee
{
public int Id { set; get; }
public string FirstName { get; set; }
public string LastName { get; set; }

//public int? ManagerID { get; set; }
public virtual Employee Manager { get; set; }
}
}

در این کلاس، خاصیت Manager دارای ارجاعی است به همان کلاس؛ یعنی یک کارمند می‌تواند مسئول کارمند دیگری باشد. برای تعریف نگاشت‌ این کلاس به بانک اطلاعاتی می‌توان از روش زیر استفاده کرد:

using System.Data.Entity.ModelConfiguration;
using EF_Sample04.Models;

namespace EF_Sample04.Mappings
{
public class EmployeeConfig : EntityTypeConfiguration<Employee>
{
public EmployeeConfig()
{
this.HasOptional(x => x.Manager)
.WithMany()
//.HasForeignKey(x => x.ManagerID)
.WillCascadeOnDelete(false);
}
}
}

با توجه به اینکه یک کارمند می‌تواند مسئولی نداشته باشد (خودش مدیر ارشد است)، به کمک متد HasOptional مشخص کرده‌ایم که فیلد Manager_Id را که می‌خواهی به این کلاس اضافه کنی باید نال پذیر باشد. توسط متد WithMany طرف دیگر رابطه مشخص شده است.
اگر نیاز بود فیلد Manager_Id اضافه شده نام دیگری داشته باشد، یک خاصیت nullable مانند ManagerID را که در کلاس Employee مشاهده می‌کنید،‌ اضافه نمائید. سپس در طرف تعاریف نگاشت‌ها به کمک متد HasForeignKey، باید صریحا عنوان کرد که این خاصیت، همان کلید خارجی است. از این نکته در سایر حالات تعاریف نگاشت‌ها نیز می‌توان استفاده کرد، خصوصا اگر از یک بانک اطلاعاتی موجود قرار است استفاده شود و از نام‌های دیگری بجز نام‌های پیش فرض EF استفاده کرده است.




مثال‌های این سری رو از این آدرس هم می‌تونید دریافت کنید: (^)

مطالب
استفاده از EF در اپلیکیشن های N-Tier : قسمت اول
تمام اپلیکیشن‌ها را نمی‌توان در یک پروسس بسته بندی کرد، بدین معنا که تمام اپلیکیشن روی یک سرور فیزیکی قرار گیرد. در عصر حاظر معماری بسیاری از اپلیکیشن‌ها چند لایه است و هر لایه روی سرور مجزایی توزیع می‌شود. بعنوان مثال یک معماری کلاسیک شامل سه لایه نمایش (presentation)، اپلیکیشن (application) و داده (data) است. لایه بندی منطقی (logical layering) یک اپلیکیشن می‌تواند در یک App Domain واحد پیاده سازی شده و روی یک کامپیوتر میزبانی شود. در این صورت لازم نیست نگران مباحثی مانند پراکسی ها، مرتب سازی (serialization)، پروتوکل‌های شبکه و غیره باشیم. اما اپلیکیشن‌های بزرگی که چندین کلاینت دارند و در مراکز داده میزبانی می‌شوند باید تمام این مسائل را در نظر بگیرند. خوشبختانه پیاده سازی چنین اپلیکیشن هایی با استفاده از Entity Framework و دیگر تکنولوژی‌های مایکروسافت مانند WCF, Web API ساده‌تر شده است. منظور از n-Tier معماری اپلیکیشن هایی است که لایه‌های نمایش، منطق تجاری و دسترسی داده هر کدام روی سرور مجزایی میزبانی می‌شوند. این تفکیک فیزیکی لایه‌ها به بسط پذیری، مدیریت و نگهداری اپلیکیشن‌ها در دراز مدت کمک می‌کند، اما معمولا تاثیری منفی روی کارایی کلی سیستم دارد. چرا که برای انجام عملیات مختلف باید از محدوده ماشین‌های فیریکی عبور کنیم.

معماری N-Tier چالش‌های بخصوصی را برای قابلیت‌های change-tracking در EF اضافه می‌کند. در ابتدا داده‌ها توسط یک آبجکت EF Context بارگذاری می‌شوند اما این آبجکت پس از ارسال داده‌ها به کلاینت از بین می‌رود. تغییراتی که در سمت کلاینت روی داده‌ها اعمال می‌شوند ردیابی (track) نخواهند شد. هنگام بروز رسانی، آبجکت Context جدیدی برای پردازش اطلاعات ارسالی باید ایجاد شود. مسلما آبجکت جدید هیچ چیز درباره Context پیشین یا مقادیر اصلی موجودیت‌ها نمی‌داند.

در نسخه‌های قبلی Entity Framework توسعه دهندگان با استفاده از قالب ویژه ای بنام Self-Tracking Entities می‌توانستند تغییرات موجودیت‌‌ها را ردیابی کنند. این قابلیت در نسخه EF 6 از رده خارج شده است و گرچه هنوز توسط ObjectContext پشتیبانی می‌شود، آبجکت DbContext از آن پشتیبانی نمی‌کند.

در این سری از مقالات روی عملیات پایه CRUD تمرکز می‌کنیم که در اکثر اپلیکیشن‌های n-Tier استفاده می‌شوند. همچنین خواهیم دید چگونه می‌توان تغییرات موجودیت‌ها را ردیابی کرد. مباحثی مانند همزمانی (concurrency) و مرتب سازی (serialization) نیز بررسی خواهند شد. در قسمت یک این سری مقالات، به بروز رسانی موجودیت‌های منفصل (disconnected) توسط سرویس‌های Web API نگاهی خواهیم داشت.


بروز رسانی موجودیت‌های منفصل با Web API

سناریویی را فرض کنید که در آن برای انجام عملیات CRUD از یک سرویس Web API استفاده می‌شود. همچنین مدیریت داده‌ها با مدل Code-First پیاده سازی شده است. در مثال جاری یک کلاینت Console Application خواهیم داشت که یک سرویس Web API را فراخوانی می‌کند. توجه داشته باشید که هر اپلیکیشن در Solution مجزایی قرار دارد. تفکیک پروژه‌ها برای شبیه سازی یک محیط n-Tier انجام شده است.

فرض کنید مدلی مانند تصویر زیر داریم.

همانطور که می‌بینید مدل جاری، سفارشات یک اپلیکیشن فرضی را معرفی می‌کند. می‌خواهیم مدل و کد دسترسی به داده‌ها را در یک سرویس Web API پیاده سازی کنیم، تا هر کلاینتی که از HTTP استفاده می‌کند بتواند عملیات CRUD را انجام دهد. برای ساختن سرویس مورد نظر مراحل زیر را دنبال کنید.

  • در ویژوال استودیو پروژه جدیدی از نوع ASP.NET Web Application بسازید و قالب پروژه را Web API انتخاب کنید. نام پروژه را به Recipe1.Service تغییر دهید.
  • کنترلر جدیدی از نوع WebApi Controller با نام OrderController به پروژه اضافه کنید.
  • کلاس جدیدی با نام Order در پوشه مدل‌ها ایجاد کنید و کد زیر را به آن اضافه نمایید.
public class Order
{
    public int OrderId { get; set; }
    public string Product { get; set; }
    public int Quantity { get; set; }
    public string Status { get; set; }
    public byte[] TimeStamp { get; set; }
}
  • با استفاده از NuGet Package Manager کتابخانه Entity Framework 6 را به پروژه اضافه کنید.
  • حال کلاسی با نام Recipe1Context ایجاد کنید و کد زیر را به آن اضافه نمایید.
public class Recipe1Context : DbContext
{
    public Recipe1Context() : base("Recipe1ConnectionString") { }
    
    public DbSet<Order> Orders { get; set; }
    
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>().ToTable("Orders");
        // Following configuration enables timestamp to be concurrency token
        modelBuilder.Entity<Order>().Property(x => x.TimeStamp)
            .IsConcurrencyToken()
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
    }
}

  • فایل Web.config پروژه را باز کنید و رشته اتصال زیر را به قسمت ConnectionStrings اضافه نمایید.
<connectionStrings>
  <add name="Recipe1ConnectionString"
    connectionString="Data Source=.;
    Initial Catalog=EFRecipes;
    Integrated Security=True;
    MultipleActiveResultSets=True"
    providerName="System.Data.SqlClient" />
</connectionStrings>
  • فایل Global.asax را باز کنید و کد زیر را به آن اضافه نمایید. این کد بررسی Entity Framework Compatibility را غیرفعال می‌کند.
protected void Application_Start()
{
    // Disable Entity Framework Model Compatibilty
    Database.SetInitializer<Recipe1Context>(null);
    ...
}
  • در آخر کد کنترلر Order را با لیست زیر جایگزین کنید.
public class OrderController : ApiController
{
    // GET api/order
    public IEnumerable<Order> Get()
    {
        using (var context = new Recipe1Context())
        {
            return context.Orders.ToList();
        }
    }

    // GET api/order/5
    public Order Get(int id)
    {
        using (var context = new Recipe1Context())
        {
            return context.Orders.FirstOrDefault(x => x.OrderId == id);
        }
    }

    // POST api/order
    public HttpResponseMessage Post(Order order)
    {
        // Cleanup data from previous requests
        Cleanup();
        
        using (var context = new Recipe1Context())
        {
            context.Orders.Add(order);
            context.SaveChanges();
            // create HttpResponseMessage to wrap result, assigning Http Status code of 201,
            // which informs client that resource created successfully
            var response = Request.CreateResponse(HttpStatusCode.Created, order);
            // add location of newly-created resource to response header
            response.Headers.Location = new Uri(Url.Link("DefaultApi",
                new { id = order.OrderId }));
            return response;
        }
    }

    // PUT api/order/5
    public HttpResponseMessage Put(Order order)
    {
        using (var context = new Recipe1Context())
        {
            context.Entry(order).State = EntityState.Modified;
            context.SaveChanges();
            // return Http Status code of 200, informing client that resouce updated successfully
            return Request.CreateResponse(HttpStatusCode.OK, order);
        }
    }

    // DELETE api/order/5
    public HttpResponseMessage Delete(int id)
    {
        using (var context = new Recipe1Context())
        {
            var order = context.Orders.FirstOrDefault(x => x.OrderId == id);
            context.Orders.Remove(order);
            context.SaveChanges();
            // Return Http Status code of 200, informing client that resouce removed successfully
            return Request.CreateResponse(HttpStatusCode.OK);
        }
    }

    private void Cleanup()
    {
        using (var context = new Recipe1Context())
        {
            context.Database.ExecuteSqlCommand("delete from [orders]");
        }
    }
}

قابل ذکر است که هنگام استفاده از Entity Framework در MVC یا Web API، بکارگیری قابلیت Scaffolding بسیار مفید است. این فریم ورک‌های ASP.NET می‌توانند کنترلرهایی کاملا اجرایی برایتان تولید کنند که صرفه جویی چشمگیری در زمان و کار شما خواهد بود.

در قدم بعدی اپلیکیشن کلاینت را می‌سازیم که از سرویس Web API استفاده می‌کند.

  • در ویژوال استودیو پروژه جدیدی از نوع Console Application بسازید و نام آن را به Recipe1.Client تغییر دهید.
  • کلاس موجودیت Order را به پروژه اضافه کنید. همان کلاسی که در سرویس Web API ساختیم.

نکته: قسمت هایی از اپلیکیشن که باید در لایه‌های مختلف مورد استفاده قرار گیرند - مانند کلاس‌های موجودیت‌ها - بهتر است در لایه مجزایی قرار داده شده و به اشتراک گذاشته شوند. مثلا می‌توانید پروژه ای از نوع Class Library بسازید و تمام موجودیت‌ها را در آن تعریف کنید. سپس لایه‌های مختلف این پروژه را ارجاع خواهند کرد.

فایل program.cs را باز کنید و کد زیر را به آن اضافه نمایید.

private HttpClient _client;
private Order _order;

private static void Main()
{
    Task t = Run();
    t.Wait();
    
    Console.WriteLine("\nPress <enter> to continue...");
    Console.ReadLine();
}

private static async Task Run()
{
    // create instance of the program class
    var program = new Program();
    program.ServiceSetup();
    program.CreateOrder();
    // do not proceed until order is added
    await program.PostOrderAsync();
    program.ChangeOrder();
    // do not proceed until order is changed
    await program.PutOrderAsync();
    // do not proceed until order is removed
    await program.RemoveOrderAsync();
}

private void ServiceSetup()
{
    // map URL for Web API cal
    _client = new HttpClient { BaseAddress = new Uri("http://localhost:3237/") };
    // add Accept Header to request Web API content
    // negotiation to return resource in JSON format
    _client.DefaultRequestHeaders.Accept.
        Add(new MediaTypeWithQualityHeaderValue("application/json"));
}

private void CreateOrder()
{
    // Create new order
    _order = new Order { Product = "Camping Tent", Quantity = 3, Status = "Received" };
}

private async Task PostOrderAsync()
{
    // leverage Web API client side API to call service
    var response = await _client.PostAsJsonAsync("api/order", _order);
    Uri newOrderUri;
    
    if (response.IsSuccessStatusCode)
    {
        // Capture Uri of new resource
        newOrderUri = response.Headers.Location;
        // capture newly-created order returned from service,
        // which will now include the database-generated Id value
        _order = await response.Content.ReadAsAsync<Order>();
        Console.WriteLine("Successfully created order. Here is URL to new resource: {0}",  newOrderUri);
    }
    else
        Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}

private void ChangeOrder()
{
    // update order
    _order.Quantity = 10;
}

private async Task PutOrderAsync()
{
    // construct call to generate HttpPut verb and dispatch
    // to corresponding Put method in the Web API Service
    var response = await _client.PutAsJsonAsync("api/order", _order);
    
    if (response.IsSuccessStatusCode)
    {
        // capture updated order returned from service, which will include new quanity
        _order = await response.Content.ReadAsAsync<Order>();
        Console.WriteLine("Successfully updated order: {0}", response.StatusCode);
    }
    else
        Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}

private async Task RemoveOrderAsync()
{
    // remove order
    var uri = "api/order/" + _order.OrderId;
    var response = await _client.DeleteAsync(uri);

    if (response.IsSuccessStatusCode)
        Console.WriteLine("Sucessfully deleted order: {0}", response.StatusCode);
    else
        Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
}

اگر اپلیکیشن کلاینت را اجرا کنید باید با خروجی زیر مواجه شوید:

Successfully created order: http://localhost:3237/api/order/1054
Successfully updated order: OK
Sucessfully deleted order: OK

شرح مثال جاری

با اجرای اپلیکیشن Web API شروع کنید. این اپلیکیشن یک کنترلر Web API دارد که پس از اجرا شما را به صفحه خانه هدایت می‌کند. در این مرحله اپلیکیشن در حال اجرا است و سرویس‌های ما قابل دسترسی هستند.

حال اپلیکیشن کنسول را باز کنید. روی خط اول کد program.cs یک breakpoint تعریف کرده و اپلیکیشن را اجرا کنید. ابتدا آدرس سرویس Web API را پیکربندی کرده و خاصیت Accept Header را مقدار دهی می‌کنیم. با این کار از سرویس مورد نظر درخواست می‌کنیم که داده‌ها را با فرمت JSON بازگرداند. سپس یک آبجکت Order می‌سازیم و با فراخوانی متد PostAsJsonAsync آن را به سرویس ارسال می‌کنیم. این متد روی آبجکت HttpClient تعریف شده است. اگر به اکشن متد Post در کنترلر Order یک breakpoint اضافه کنید، خواهید دید که این متد سفارش جدید را بعنوان یک پارامتر دریافت می‌کند و آن را به لیست موجودیت‌ها در Context جاری اضافه می‌نماید. این عمل باعث می‌شود که آبجکت جدید بعنوان Added علامت گذاری شود، در این مرحله Context جاری شروع به ردیابی تغییرات می‌کند. در آخر با فراخوانی متد SaveChanges داده‌ها را ذخیره می‌کنیم. در قدم بعدی کد وضعیت 201 (Created) و آدرس منبع جدید را در یک آبجکت HttpResponseMessage قرار می‌دهیم و به کلاینت ارسال می‌کنیم. هنگام استفاده از Web API باید اطمینان حاصل کنیم که کلاینت‌ها درخواست‌های ایجاد رکورد جدید را بصورت POST ارسال می‌کنند. درخواست‌های HTTP Post بصورت خودکار به اکشن متد متناظر نگاشت می‌شوند.

در مرحله بعد عملیات بعدی را اجرا می‌کنیم، تعداد سفارش را تغییر می‌دهیم و موجودیت جاری را با فراخوانی متد PutAsJsonAsync به سرویس Web API ارسال می‌کنیم. اگر به اکشن متد Put در کنترلر سرویس یک breakpoint اضافه کنید، خواهید دید که آبجکت سفارش بصورت یک پارامتر دریافت می‌شود. سپس با فراخوانی متد Entry و پاس دادن موجودیت جاری بعنوان رفرنس، خاصیت State را به Modified تغییر می‌دهیم، که این کار موجودیت را به Context جاری می‌چسباند. حال فراخوانی متد SaveChanges یک اسکریپت بروز رسانی تولید خواهد کرد. در مثال جاری تمام فیلدهای آبجکت Order را بروز رسانی می‌کنیم. در شماره‌های بعدی این سری از مقالات، خواهیم دید چگونه می‌توان تنها فیلدهایی را بروز رسانی کرد که تغییر کرده اند. در آخر عملیات را با بازگرداندن کد وضعیت 200 (OK) به اتمام می‌رسانیم.

در مرحله بعد، عملیات نهایی را اجرا می‌کنیم که موجودیت Order را از منبع داده حذف می‌کند. برای اینکار شناسه (Id) رکورد مورد نظر را به آدرس سرویس اضافه می‌کنیم و متد DeleteAsync را فراخوانی می‌کنیم. در سرویس Web API رکورد مورد نظر را از دیتابیس دریافت کرده و متد Remove را روی Context جاری فراخوانی می‌کنیم. این کار موجودیت مورد نظر را بعنوان Deleted علامت گذاری می‌کند. فراخوانی متد SaveChanges یک اسکریپت Delete تولید خواهد کرد که نهایتا منجر به حذف شدن رکورد می‌شود.

در یک اپلیکیشن واقعی بهتر است کد دسترسی داده‌ها از سرویس Web API تفکیک شود و در لایه مجزایی قرار گیرد.

نظرات مطالب
آشنایی با NHibernate - قسمت هفتم
بحث entity framework با NHibernate متفاوت است.
در NHibernate این متد BuildSessionFactory فوق کار بارگذاری متادیتا و نگاشت‌ها و غیره رو انجام میده؛ یعنی خودکار نیست و اگر قرار باشه به ازای هر کوئری یکبار فراخوانی شود اصلا نمی‌شود با برنامه کار کرد چون به شدت کند خواهد بود. به همین جهت کش کردن آن‌را با استفاده از الگوی singleton به صورت فوق تنها یکبار باید انجام داد. یکبار در طول عمر برنامه باید نگاشت‌ها صورت گیرد و پس از آن بارها و بارها از آن استفاده شود چون قرار نیست در طول عمر یک برنامه در حال اجرا تغییری کند.
در حالیکه در Entity framework اینکار (بارگذاری متادیتا و نگاشت‌های تعریف شده) به صورت خودکار زمانیکه برنامه برای بار اول اجرا می‌شود رخ داده و به صورت خودکار هم کش می‌شود. ماخذ: (+) ؛ بنابراین برای مدیریت آن اصلا لازم نیست شما کاری انجام دهید.
فقط در Entity framework یک لایه بسیار کم هزینه به نام ObjectContext وجود دارد که توصیه شده در برنامه‌های ASP.NET به ازای هر درخواست ایجاد و تخریب شود که می‌توانید از مقاله فوق ایده بگیرید و اصلا نباید کش شود یا هر بحث دیگری. ماخذ: (+) ؛ حتی اگر اینکار را هم انجام ندادید مهم نیست چون سربار بسیار کمی دارد.
مطالب
Fluent Linq to Sql

نگارش بعدی یا چهارم entity framework چیزی است شبیه به Fluent NHibernate . یعنی اگر مقاله‌ای را در این زمینه مطالعه کنید و عنوان آن حذف شود، نمی‌توان تشخیص داد که این مقاله مربوط به entity framework است یا Fluent NHibernate. هر چند entity framework حداقل دو نگارش دیگر لازم دارد تا NHibernate را کاملا پشت سر بگذارد.
از آن طرف محبوبیت Linq to SQL هم هنوز پابرجا است و برای مثال سایت پر ترافیکی مثل stack overflow از آن استفاده می‌کند و بسیار هم موفق بوده و کارش را به خوبی انجام می‌دهد.
پروژه مکملی به نام Fluent Linq to Sql با الهام گیری از Fluent NHibernate در سایت codeplex موجود است که این نوع نگاشت‌ها را برای Linq to Sql نیز میسر می‌سازد. به این صورت دیگر نیازی به استفاده از attributes و یا فایل‌های xml نگاشت‌های Linq to Sql نخواهد بود. همچنین مدل کاری اول کد بعد دیتابیس نیز به این صورت محقق می‌شود.



مطالب
راهنمای آموزشی رایگان entity framework

در حین وبگردی‌های روزانه به مرجع آموزشی رایگان 500 صفحه‌ای زیر برخوردم، گفتم شاید برای شما هم جالب توجه باشد:

Entity Framework learning guide

1. Introduction to Entity Framework
2. Modeling Entities
3. Eager and Lazy Loading entities and Navigation properties
4. Views
5. Inheritance
6. Working with Objects
7. Improving Entity framework performance
8. Inserting, Updating and Deleting entities and associations
9. Querying with Linq to entities
10. Concurrency and Transactions
11. Consuming Stored Procedures
12. Mapping Crud Operations to Stored Procedure

Download

مطالب
سری بررسی SQL Smell در EF Core - استفاده از مدل Entity Attribute Value - بخش دوم
در مطلب قبلی، مدل EAV را معرفی کردیم و گفتیم که این نوع پیاده‌سازی در واقع یک SQL Smell است؛ زیرا کوئری نویسی را سخت میکند و همچنین به دلیل عدم امکان تعریف constraints، کنترلی بر روی صحت دیتاهای وارده شد نخواهیم داشت. در نهایت با برنامه‌ای روبرو خواهیم شد که درک صحیحی از ماهیت دیتا ندارد. اما اگر در شرایطی مجبور به استفاده‌ی از این مدل هستید، بهتر است از فرمت JSON برای ذخیره‌سازی دیتای داینامیک استفاده کنید. بیشتر دیتابیس‌های رابطه‌ایی به صورت native از نوع داده‌ایی JSON پشتیبانی میکنند:  
CREATE TABLE EmployeeJsonAttributes (
  Id int NOT NULL AUTO_INCREMENT,
  EmployeeId int NOT NULL,
  Attributes json DEFAULT NULL,
  PRIMARY KEY (Id),
  FOREIGN KEY (EmployeeId) REFERENCES EmployeeEav (Id) ON DELETE CASCADE
)
همانطور که مشاهده می‌کنید در اینجا تایپ ستون Attributes، به JSON تنظیم شده است. بنابراین می‌توانیم از قابلیت‌های توکار دیتابیس (MySQL در مطلب جاری) برای ذخیره و بازیابی داده‌های JSON استفاده کنیم. در ادامه دو روش ذخیره JSON  را مشاهده میکنید: 
INSERT INTO EmployeeJsonAttributes VALUES (
101, 
  '{
  "name": "Jon",
    "lastName": "Doe",
    "dateOfBirth": "1989-01-01 10:10:10+05:30",
    "skills": [ "C#", "JS" ],
    "address":  {
  "country": "UK",
      "city": "London",
      "email": "jon.doe@example.com"
    }
  }'
)

INSERT INTO efcoresample.EmployeeJsonAttributes VALUES (
101, 
  JSON_OBJECT(
"name", "Jon", 
"lastName", "Doe",
"dateOfBirth", "1989-01-01 10:10:10+05:30",
"skills", JSON_ARRAY("C#", "JS"),
    "address", JSON_OBJECT(
  "country", "UK",
      "city", "London",
  "email", "jon.doe@example.com"
    )
  )
)

به عنوان مثال در ادامه میخواهیم کشور محل تولد یک کاربر خاص را نمایش دهیم. برای اینکار می‌توانیم از JSON_EXTRACT استفاده کنیم:
SELECT JSON_EXTRACT(Attributes, '$.address.country') as Country 
FROM EmployeeJsonAttributes
WHERE EmployeeId = 101;

-- Conutry
-- "UK"

همچنین می‌توانیم از عملگر column-path نیز به جای JSON_EXTRACT استفاده کنیم:
SELECT Attributes -> '$.address.country' as Country 
FROM EmployeeJsonAttributes
WHERE EmployeeId = 101;

-- Conutry
-- "UK"

بنابراین به راحتی می‌توانیم کوئری مطلب قبل را اینگونه بازنویسی کنیم:
SELECT EmployeeId, Attributes ->> '$.DateOfBirth' AS BirthDate FROM EmployeeJsonAttributes
WHERE Attributes ->> '$.DateOfBirth' > DATE_SUB(CURRENT_DATE(), INTERVAL 25 YEAR)
همانطور که مشاهده می‌کنید در کوئری فوق یک عملگر < دیگر نیز اضافه کرده‌ایم. هدف از آن حذف “” از خروجی نهایی می‌باشد. 

استفاده از JSON در EF Core 
متاسفانه در EF Core به صورت مستقیم نمی‌توانیم از JSON درون کلاس‌های سی‌شارپ استفاده کنیم (+ )، در نتیجه در سمت کلاس‌های سی‌شارپ باید از string استفاده کنیم و به نوعی به EF Core اطلاع دهیم که تایپ ستون موردنظرمان JSON است. در نتیجه خروجی نهایی درون دیتابیس، یک فیلد با تایپ JSON خواهد بود. برای اینکار به دو شیوه می‌توانیم تایپ ستون موردنظر را تعیین کنیم: 
// Fluent API
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>(entity =>
    {
        entity.Property(e => e.Attributes).HasColumnType("json");
    });
}

// Data Annotations
[Column(TypeName = "json")]
public string Attributes { get; set; }

در نهایت برای تشکیل بانک اطلاعاتی، به مدلی با ساختار زیر نیاز خواهیم داشت:
public class EmployeeJsonAttribute
{
    public int Id { get; set; }
    public virtual EmployeeEav Employee { get; set; }
    public int EmployeeId { get; set; }
    [Column(TypeName = "json")]
    public string Attributes { get; set; }
}
در اینجا به جای تعریف ستون‌ها و مقادیر داینامیک‌شان از یک فیلد از نوع رشته‌ایی با نام Attributes استفاده شده است. از آنجائیکه نوع ستون در سمت دیتابیس به JSON تنظیم خواهد شد، در نتیجه هر نوع ساختار JSON معتبری را می‌توانیم درون آن ذخیره کنیم:
dbContext.EmployeeJsonAttributes.Add(new EmployeeJsonAttribute
{
    EmployeeId =  101,
    Attributes = JsonSerializer.Serialize(new
    {
        FirstName = "Sirwan",
        LastName = "Afifi",
        DateOfBirth = DateTime.Now.AddYears(-31)
    })
});

dbContext.SaveChanges();
همانطور که اشاره شده به دلیل عدم پشتیبانی از JSON در حال حاضر در EF Core امکان کوئری نویسی بر روی ستون JSON را نداریم. در همین حد که براساس فیلدهای دیگر جستجو را انجام داده و خروجی را Deserialize کنیم:
var employee = dbContext.EmployeeJsonAttributes.Find(201);
Console.WriteLine(JsonSerializer.Deserialize<Employee>(employee.Attributes).DateOfBirth);

برای نوشتن کوئری روی ستون JSON می‌توانید از Query Types  نیز استفاده کنید. 
مطالب
توسعه برنامه های Cross Platform با Xamarin Forms & Bit Framework - قسمت اول
یکی از دغدغه‌های جدی امروزه توسعه دهندگان نرم افزار در سمت Front end، توسعه برنامه‌های Cross Platform است. در این سری آموزشی به صورت قدم به قدم و پروژه محور می‌خواهیم برنامه‌ای را برای Android/iOS/Windows توسعه دهیم که روی کامپیوتر، تبلت و موبایل به خوبی کار کند.
انتخاب ابزار درست برای شروع به کار از اهمیت شایانی برخوردار است و بد نیست در ابتدا به بررسی دلایل انتخاب ابزارهایی بپردازیم که قرار است در این دوره از آنها استفاده شود.

۱- زبان برنامه نویسی: CSharp
CSharp با وجود امکاناتی مانند Generics‌، Lambda Expressions، Linq، Async و ... که تا حدودی در سایر زبان‌ها هم هستند، زبانی خوش ساختار و کاربردی است. همچنین اضافه شدن امکانات جدیدی مانند ref returns و ... نشان دهنده این است که این زبان رو به جلو در حرکت و در برخی موارد پیشرو است. اما در توسعه یک برنامه Cross Platform مواردی اهمیت پیدا می‌کنند که شاید توسعه دهنده نرم افزار مستقیما با آنها درگیر نشود، ولی از آن‌ها تاثیر می‌پذیرد. در زبان CSharp مواردی مانند P/Invoke ،Pointers، Extern و ... جزء این دست از موارد هستند که کمک می‌کنند CSharp به یکی از لذت بخش‌ترین زبان هایی تبدیل شود که قابلیت فراخوانی 100% امکانات زبان‌های دیگر را بدون اما و اگرهای فراوان داشته باشد.
در سایر زبان‌های Cross Platform اگر کتابخانه‌های توسعه داده شده و ترکیب زبان‌های برنامه نویسی استفاده شده در آنها را بررسی کنید، می‌بینید که اگر قرار است کتابخانه مربوطه مثلا در JavaScript استفاده شود، توسعه دهنده کد، درصدی از کد را با Java، درصدی را با Swift و درصدی را با JavaScript توسعه داده است! اگر معادل همان کتابخانه را برای CSharp پیدا کنید، می‌بینید که تمامی قسمت‌های مربوط به اندروید، iOS و ویندوز به زبان CSharp است.
برای مثال در ادامه کدهای مربوط به پروژه‌ای را می‌بینید که هدف آن، ارائه متدهایی ساده برای کار با امکانات مختلف دستگاه، به صورت Cross Platform هست. مثلا برای بررسی وضعیت باطری بنویسید:
var state = Battery.State; //  Charging, Full, Discharging, ...
که تماما با CSharp توسعه داده شده است.
اما معادل چنین پروژه‌ای در هیچ زبان دیگری به صورت 100% با خود آن زبان توسعه داده نشده‌است و بیشتر مواقع با چنین چیزی مواجه می‌شوید:

این مسئله وقتی حائز اهمیت می‌شود که در پروژه‌تان به سمت کارهایی حرکت کنید که کمی خاص باشند و نتوانید کتابخانه‌ای را پیدا کنید که نیازهای شما را پوشش دهد و یا از کیفیت خوبی برخوردار نباشد و ... و خلاصه بخواهید کمی بیشتر دست به کد شوید. در چنین مواقعی شما عملا درگیر چندین زبان و محیط توسعه و سیستم عامل و Debugger و ... می‌شوید. به هر میزان که برنامه شما خاص باشد، این هزینه افزایش پیدا می‌کند تا جایی که ممکن است ادامه توسعه نرم افزار را غیر ممکن کند.
در CSharp شما به صد در صد امکانات سیستم عامل‌ها (Android/iOS/Windows/Linux/Mac/Tizen) دسترسی دارید.

۲- اجرا کننده برنامه: NET.
انتخاب NET. و کتابخانه‌های آن مانند Task Parallel Library - Entity Framework(Sqlite) - Noda - JSON.NET که در هر زمینه‌ای بالاترین کیفیت ممکن را به شما ارائه می‌کنند به خودی خود منطقی به نظر می‌رسد. اما تمامی این‌ها در کنار سرعت اجرای NET. به صورت Native و همچنین قابلیت اجرای NET. در تمامی سیستم عامل‌ها و همچنین امکان اجرای آن در مرورگر به کمک استاندارد Web Assembly آن را به انتخابی فوق العاده بدل می‌کند. سرعت گسترش محبوبیت و استفاده از NET. در دنیا نیز دلیل دیگری است برای اطمینان خاطر از انتخاب درست.

۳- Xamarin forms
Xamarin forms همه آن چیزهای پایه‌ای است که برای نوشتن یک برنامه لازم داریم. کنترل هایی مانند ListView، Button و ...به همراه Binding - Navigation و ...
در عمل می‌توانید آن را معادل Angular & Angular Material بدانید. وقتی شما فرمی را با Xamarin Forms توسعه می‌دهید و درون آن دکمه‌ای است که از فرم اول، شما را به فرم دوم می‌برد، می‌توانید آن را در هر جایی که Xamarin forms پشتیبانی می‌کند، استفاده کنید. پشتیبانی Xamarin forms برای Android/iOS/Windows خوب و برای Linux/Mac/Tizen و Web در مراحل اولیه است.
در Xamarin forms شما UI کاملا Native خواهید داشت.

۴- Prism Patterns & practices
Prism همه آن چیزی است که برای نوشتن یک برنامه با کیفیت، با قابلیت نگهداری بالا و تست پذیر احتیاج داریم.

با نقش Bit و کمک‌های آن در طول مسیر آموزش بیشتر آشنا خواهیم شد.

در قسمت‌های بعدی به آموزش نصب و نحوه دیباگ کردن کد و ارائه پابلیش در Android-iOS-Windows خواهیم پرداخت و سپس وارد کدنویسی شده و پروژه اولیه را خواهیم ساخت و در قسمت‌های بعد از آن هم کار با دیتابیس کلاینت ساید، ارتباط با سرور و ... را آموزش می‌بینیم.
اگر قبلا Xamarin Forms را تست کرده‌اید و به علت مسائلی مانند حجم بالای خروجی برنامه و یا کندی در توسعه برنامه یا اجرای آن در دستگاه مشتری آن را کنار گذاشته‌اید، توصیه می‌کنم بار دیگر آن را با ما تست کنید و با رعایت چند نکته ساده از نوشتن برنامه Cross Platform به بهترین شکل لذت ببرید و خروجی خوبی را در نهایت به مشتریان سیستم ارائه کنید.