مطالب
کار با کلیدهای اصلی و خارجی در EF Code first
در حین کار با ارتباطات بین اشیاء و جداول، دانستن یک سری از نکات می‌توانند در کم کردن تعداد رفت و برگشت‌های به سرور مؤثر واقع شده و نهایتا سبب بالا رفتن سرعت برنامه شوند. از این دست می‌توان به یک سری نکات ریز همراه با primary-keys و foreign-keys اشاره کرد که در ادامه به آن‌ها پرداخته خواهد شد.
در ابتدا کلا‌س‌های مدل و Context برنامه را به شکل زیر درنظر بگیرید:
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;

namespace TestKeys
{
    public class Bill
    {
        public int Id { get; set; }
        public decimal Amount { set; get; }
        public virtual Account Account { get; set; }
    }

    public class Account
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<Bill> Bills { get; set; }
        public DbSet<Account> Accounts { get; set; }
    }

    public class Configuration : DbMigrationsConfiguration<MyContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        }

        protected override void Seed(MyContext context)
        {
            var a1 = new Account { Name = "a1" };
            var a2 = new Account { Name = "a2" };

            var bill1 = new Bill { Amount = 100, Account = a1 };
            var bill2 = new Bill { Amount = 200, Account = a2 };

            context.Bills.Add(bill1);
            context.Bills.Add(bill2);
            base.Seed(context);
        }
    }

    public static class Test
    {
        public static void Start()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
            using (var ctx = new MyContext())
            {
                var bill1 = ctx.Bills.Find(1);
                Console.WriteLine(bill1.Amount);
            }
        }
    }
}

در اینجا کلاس صورتحساب و حساب مرتبط به آن تعریف شده‌اند. سپس به کمک DbContext این دو کلاس در معرض دید EF Code first قرار گرفته‌اند و در کلاس Configuration نحوه آغاز بانک اطلاعاتی به همراه تعدادی رکورد اولیه مشخص شده است.


نحوه صحیح مقدار دهی کلید خارجی در EF Code first

تا اینجا یک روال متداول را مشاهده کردیم. اکنون سؤال این است که اگر بخواهیم اولین رکورد صورتحساب ثبت شده توسط متد Seed را ویرایش کرده و مثلا حساب دوم را به آن انتساب دهیم، بهینه‌ترین روش چیست؟ بهینه‌ترین در اینجا منظور روشی است که کمترین تعداد رفت و برگشت به بانک اطلاعاتی را داشته باشد. همچنین فرض کنید در صفحه ویرایش، اطلاعات حساب‌ها در یک Drop down list شامل نام و id آ‌ن‌ها نیز وجود دارد.

روش اول:
using (var ctx = new MyContext())
{
     var bill1 = ctx.Bills.Find(1);
     var a2 = new Account { Id = 2, Name = "a2" };
     bill1.Account = a2;
     ctx.SaveChanges();
}
این روش مخصوص تازه واردهای EF Code first است و آنطور که مدنظر آن‌ها است کار نمی‌کند.
به کمک متد Find اولین رکورد یافت شده و سپس بر اساس اطلاعات drop down در دسترس، یک شیء جدید حساب را ایجاد و سپس تغییرات لازم را اعمال می‌کنیم. در نهایت اطلاعات را هم ذخیره خواهیم کرد.
این روش به ظاهر کار می‌کنه اما حاصل آن ذخیره رکورد حساب سومی با id=3 در بانک اطلاعاتی است و سپس انتساب آن به اولین صورتحساب ثبت شده.
نتیجه: Id را دستی مقدار دهی نکنید؛ تاثیری ندارد. زیرا اطلاعات شیء جدید حساب، در سیستم tracking مرتبط با Context جاری وجود ندارد. بنابراین EF آن‌را به عنوان یک شیء کاملا جدید درنظر خواهد گرفت، صرفنظر از اینکه Id را به چه مقداری تنظیم کرده‌اید.

روش دوم:
using (var ctx = new MyContext())
{
    var bill1 = ctx.Bills.Find(1);
    var a2 = ctx.Accounts.Find(2);
    bill1.Account = a2;
    ctx.SaveChanges();
}
اینبار بر اساس Id دریافت شده از Drop down list، شیء حساب دوم را یافته و به صورتحساب اول انتساب می‌دهیم. این روش درست کار می‌کند؛ اما ... بهینه نیست. فرض کنید شیء جاری دارای 5 کلید خارجی است. آیا باید به ازای هر کلید خارجی یکبار از بانک اطلاعاتی کوئری گرفت؟
مگر نه این است که اطلاعات نهایی ذخیره شده در بانک اطلاعاتی متناظر با حساب صورتحساب جاری، فقط یک عدد بیشتر نیست. بنابراین آیا نمی‌شود ما تنها همین عدد متناظر را بجای دریافت کل شیء به صورتحساب نسبت دهیم؟
پاسخ: بله. می‌شود! ادامه آن در روش سوم.

روش سوم:
در اینجا بهترین کار و یکی از best practices طراحی مدل‌های EF این است که طراحی کلاس صورتحساب را به نحو زیر تغییر دهیم:
public class Bill
{
        public int Id { get; set; }
        public decimal Amount { set; get; }

        [ForeignKey("AccountId")]
        public virtual Account Account { get; set; }
        public int AccountId { set; get; }
}
به این ترتیب هم navigation property که سبب تعریف رابطه بین دو شیء و همچنین lazy loading اطلاعات آن می‌شود پابرجا خواهد بود و هم توسط خاصیت جدید AccountId که توسط ویژگی ForeignKey معرفی شده است، ویرایش اطلاعات آن دقیقا همانند کار با یک بانک اطلاعاتی واقعی خواهد شد.
اینبار به کمک خاصیت متناظر با کلید خارجی جدول، مقدار دهی و ویرایش کلید‌های خارجی یک شیء به سادگی زیر خواهد بود؛ خصوصا بدون نیاز به رفت و برگشت اضافی به بانک اطلاعاتی جهت دریافت اطلاعات متناظر با اشیاء تعریف شده به صورت navigation property :

using (var ctx = new MyContext())
{
    var bill1 = ctx.Bills.Find(1);
    bill1.AccountId = 2;
    ctx.SaveChanges();
}


وارد کردن یک شیء به سیستم Tracking

در قسمت قبل عنوان شد که Id را دستی مقدار دهی نکنید، چون تاثیری ندارد. سؤال: آیا می‌شود این شیء ویژه تعریف شده را به سیستم Tracking وارد کرد؟
پاسخ: بلی. به نحو زیر:
using (var ctx = new MyContext())
{
     var a2 = new Account { Id = 2, Name = "a2_a2" };
     ctx.Entry(a2).State = System.Data.EntityState.Modified;
     ctx.SaveChanges();
}
در اینجا شیء حساب دوم را به صورت دستی و بدون واکشی از بانک اطلاعاتی ایجاد کرده‌ایم. بنابراین از دیدگاه Context جاری هیچ ارتباطی به بانک اطلاعاتی نداشته و یک شیء جدید درنظر گرفته می‌شود (صرفنظر از Id آن). اما می‌توان این وضعیت را تغییر داد. فقط کافی است State آن‌را به نحوی که ملاحظه می‌کنید به Modified تغییر دهیم. اکنون اگر اطلاعات این شیء را ذخیره کنیم، دقیقا حساب با id=2 در بانک اطلاعاتی ویرایش خواهد شد و نه اینکه حساب جدیدی ثبت گردد.

 
نظرات مطالب
مقایسه بین حلقه های تکرار (Lambda ForEach و for و foreach)
متد ForEach در کلاس List از حلقه for معمولی استفاده میکنه و نه foreach:
public void ForEach(Action<T> action)
{
  if (action == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
  for (int index = 0; index < this._size; ++index)
    action(this._items[index]);
}
متد Array.ForEach هم از روشی مشابه استفاده کرده:
public static void ForEach<T>(T[] array, Action<T> action)
{
  if (array == null)
    throw new ArgumentNullException("array");
  if (action == null)
    throw new ArgumentNullException("action");
  for (int index = 0; index < array.Length; ++index)
    action(array[index]);
}
foreach به دلیل استفاده از اشیای درون IEnumerable و درنتیجه اجرای دستورات بیشتر در هر حلقه کندتر عمل میکند.
اما! اگر هدف تنها بررسی سرعت اجرای حلقه‌های اشاره شده باشه متدهای بالا نتیجه درستی نشان نخواهد داد، چون عملیات انجام شده در حلقه‌های نشان داده شده با هم دقیقا یکسان نیست. بهتره که یه عملیات ثابت و مستقل از متغیرهای درگیر استفاده بشه تا نتایج دقیقتری بدست بیاد. مثلا یه چیزی مثل اکشن زیر:
() => { int a = 1; }
بهتره تو این تستها مشخصات دقیق سخت افزاری هم ارائه بشه تا مقایسه‌ها بهتر انجام بگیره.
با این شرح با روشی که در مطلب Microbenchmark آورده شده آزمایشات رو دوباره انجام دادم و برای تعداد تکرار 100 میلیون اختلاف تمام حلقه‌ها در حد چند میلی ثانیه بود که کاملا قابل صرفنظره!
نتایج برای حالات مختلف موجود تفاوتهای زیادی داشت اما درنسخه ریلیز نهایتا نتایج کلی این بود که حلقه for معمولی از همه سریعتر، سپس Array.ForEach و بعد متد ForEach در کلاس List و درنهایت ازهمه کندتر حلقه foreach بود.
من آزمایشات روی یک سیستم با پردازنده 4 هسته ای با کلاک 3.4 گیگاهرتز (AMD Phenom II 965) با ویندوز 7 و 32 بیتی با رم 4 گیگ (3.25 گیگ قایل استفاده)انجام دادم. متاسفانه تعداد تکرار بیشتر خطای OutOfMemory میداد.
نکته: اجرای تستهای این چنینی برای آزمایش کارایی و سرعت به شدت تحت تاثیر عوامل جانبی هستند. مثل میزان منابع در دسترس سخت افزاری، نوع سیستم عامل، برنامه‌ها و سرویس‌های در حال اجرا، و مهمتر از همه نوع نسخه بیلد شده از برنامه تستر (دیباگ یا ریلیز) و محل اجرای تست (منظور اجرا در محیط دیباگ ویژوال استودیو یا اجرای مستقل برنامه) و ... . (همونطور که آقای نصیری هم مطلبی مرتبط رو به اشتراک گذاشتند ^)
مطالب
C# 8.0 - Async Streams
امکان تعریف نوع‌های شمارشی async در C# 8.0

فرض کنید قصد دارید یک متد async از نوع IEnumerable را که تعدادی yield return به تاخیر افتاده را به همراه دارد (yield return‌ها فقط زمانی اجرا می‌شوند که بر روی آن‌ها متدهایی مانند ToList و یا حلقه‌ی foreach اجرا شوند) و همچنین توسط await Task.Delay، دریافت اطلاعات به صورت async را نیز شبیه سازی می‌کند، تهیه کنید:
public struct Statement
{
    public int Id { get; }
    public string Description { get; }
    public Statement(int id, string description) => (Id, Description) = (id, description);
    public override string ToString() => Description;
}

public static async Task<IEnumerable<Statement>> GetStatements(bool error)
{
    if (error)
    {
       throw new Exception("Oops, we messed up 😬");
    }

    await Task.Delay(1000); //Simulate waiting for data to come through. 

    yield return new Statement(1, "C# is cool!");
    yield return new Statement(2, "C# orginally named COOL.");
    yield return new Statement(3, "More examples...");
}
این قطعه کد حتی در C# 8.0 نیز چنین خطای کامپایلری را به همراه دارد:
The body of 'AsyncStreams.GetStatements(bool)' cannot be an iterator block because
'Task<IEnumerable<AsyncStreams.Statement>>' is not an iterator interface type (CS1624)
عنوان می‌کند که برای دریافت اطلاعات متد GetStatements باید یک iterator تشکیل شود؛ اما Task IEnumerable از این نوع نیست.

برای رفع یک چنین مشکلی، اکنون در C# 8.0 می‌توان از اینترفیس جدید IAsyncEnumerable بجای Task IEnumerable استفاده کرد. به این ترتیب تنها تغییری که در قطعه کد فوق نیاز است، تغییر امضای آن به صورت زیر است:
static async IAsyncEnumerable<Statement> GetStatements(bool error)


امکان تعریف حلقه‌های async در C# 8.0

مرحله‌ی بعد، ایجاد حلقه‌ای بر روی متد GetStatements است. اکنون مشکل دیگری وجود دارد: حلقه‌ی foreach به خودی خود، یک حلقه‌ی synchronous است و اگر از آن برای کار با یک استریم async استفاده شود، هربار که اطلاعاتی از آن بازگشت داده می‌شود، پایان یک Task نیز گزارش داده خواهد شد که می‌توان سبب خاتمه‌ی حلقه شود. بنابراین انجام اینکار نیز پیش از C# 8.0 میسر نبود که اکنون با امکان تعریف await پیش از یک حلقه‌ی foreach، ممکن شده‌است:
static async IAsyncEnumerable<Statement> GetStatementsAsync(bool error)
{
    await foreach (var statement in GetStatements(error))
    {
      await Task.Delay(1000);
      yield return statement;
    }
}
تا پیش از C# 8.0، از واژه‌ی await تنها برای دریافت یک تک مقدار استفاده می‌شد؛ اما حالا می‌توان از آن برای دریافت استریمی از نتایج (async streams) نیز استفاده کرد.


اینترفیس IAsyncEnumerable چگونه تعریف شده‌است؟

 اینترفیس IAsyncEnumerable متد GetAsyncEnumerator را تعریف می‌کند که یک IAsyncEnumerator را بازگشت می‌دهد و آن نیز به همراه متد MoveNextAsync است. اگر دقت کنید در این حالت از نگارش async اینترفیس IDisposable به نام IAsyncDisposable استفاده کرده‌است:
using System.Threading;

namespace System.Collections.Generic
{
    public interface IAsyncEnumerable<out T>
    {
        IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
    }

    public interface IAsyncEnumerator<out T> : IAsyncDisposable
    {
        T Current { get; }

        ValueTask<bool> MoveNextAsync();
    }
}

namespace System
{
    public interface IAsyncDisposable
    {
        ValueTask DisposeAsync();
    }
}
اینترفیس‌های IAsyncDisposable و IAsyncEnumerator یک ValueTask را توسط متدهای DisposeAsync و MoveNextAsync بازگشت می‌دهند و این مورد به C# 7x باز می‌گردد که امکان await را نه تنها بر روی Task، بلکه بر روی هر نوعی که متد GetAwaiter را پیاده سازی می‌کند، میسر می‌کند و ValueTask نیز یکی از آن‌ها است. ValueTask به صورت یک نوع مقدار (value type) تعریف شده‌است؛ بجای نوع ارجاعی Task که سربار کمتری را به همراه دارد.


مثالی از IAsyncDisposable و روش Dispose خودکار آن

با معرفی IAsyncDisposable، اگر یک مثال ساده از پیاده سازی آن به صورت زیر باشد:
public class AwaitUsingTest : IAsyncDisposable
{
   public async ValueTask DisposeAsync()
   {
     await Task.CompletedTask;
   }

   public void Dummy() { }
}
روش فراخوانی using declaration بر روی آن به همراه واژه‌ی کلیدی await در C# 8.0، مانند مثال زیر است:
async Task FooBar()
{
   await using var test = new AwaitUsingTest();
   test.Dummy();
}
نظرات مطالب
استفاده ازExpressionها جهت ایجاد Strongly typed view در ASP.NET MVC
در نهایت این متد به این شکل اصلاح شود:
        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string PropertyName<T>(this Expression<Func<T, object>> expression)
        {
            return new PropertyHelper().GetNestedPropertyName(expression);
        }
مطالب دوره‌ها
حذف یک ردیف از اطلاعات به همراه پویانمایی محو شدن اطلاعات آن توسط jQuery در ASP.NET MVC
فرض کنید تعدادی ردیف در گزارشی نمایش داده شده‌اند. قصد داریم برای هر ردیف یک دکمه حذف را قرار دهیم. این حذف باید Ajax ایی باشد؛ به علاوه در حین حذف ردیف، پویانمایی محو آن ردیف را نیز سبب شود.


مدل و منبع داده برنامه

namespace jQueryMvcSample06.Models
{
    public class BlogPost
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public string Body { set; get; }
    }
}

using System.Collections.Generic;
using jQueryMvcSample06.Models;

namespace jQueryMvcSample06.DataSource
{
    /// <summary>
    /// منبع داده فرضی جهت سهولت دموی برنامه
    /// </summary>
    public static class BlogPostDataSource
    {
        private static IList<BlogPost> _cachedItems;
        static BlogPostDataSource()
        {
            _cachedItems = createBlogPostsInMemoryDataSource();
        }

        /// <summary>
        /// هدف صرفا تهیه یک منبع داده آزمایشی ساده تشکیل شده در حافظه است
        /// </summary>        
        private static IList<BlogPost> createBlogPostsInMemoryDataSource()
        {
            var results = new List<BlogPost>();
            for (int i = 1; i < 30; i++)
            {
                results.Add(new BlogPost { Id = i, Title = "عنوان " + i, Body = "متن ... متن ... متن " + i});
            }
            return results;
        }

        public static IList<BlogPost> LatestBlogPosts
        {
            get { return _cachedItems; }
        }
    }
}
در اینجا مدل برنامه که ساختار نمایش یک سری مطلب را تهیه می‌کند، ملاحظه می‌کنید؛ به علاوه یک منبع داده فرضی تشکیل شده در حافظه جهت سهولت دموی برنامه.


کنترلر برنامه

using System.Web.Mvc;
using System.Web.UI;
using jQueryMvcSample06.DataSource;
using jQueryMvcSample06.Security;

namespace jQueryMvcSample06.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index()
        {
            var postsList = BlogPostDataSource.LatestBlogPosts;
            return View(postsList);
        }

        [AjaxOnly]
        [HttpPost]
        [OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
        public ActionResult DeleteRow(int? postId)
        {
            if (postId == null)
                return Content(null);

            //todo: delete post from db

            return Content("ok");
        }
    }
}
کنترلر برنامه بسیار ساده بوده و نکته خاصی ندارد. در حین اولین بار نمایش صفحه، لیست مطالب را به View مرتبط ارسال می‌کند. همچنین یک اکشن متد حذف ردیف‌های نمایش داده شده را نیز در اینجا تدارک دیده‌ایم. این اکشن متد از طریق ارسال اطلاعات به صورت Ajax، شماره مطلب را در اختیار برنامه قرار می‌دهد که توسط آن در ادامه برای مثال می‌توان این رکورد را از بانک اطلاعاتی حذف کرد. امضای متد DeleteRow بر اساس پارامترهای ارسالی توسط jQuery Ajax مشخص و تنظیم شده‌اند:
 data: JSON.stringify({ postId: postId }),

View برنامه

@model IEnumerable<jQueryMvcSample06.Models.BlogPost>
@{
    ViewBag.Title = "Index";
    var postUrl = Url.Action(actionName: "DeleteRow", controllerName: "Home");
}
<h2>
    حذف یک ردیف از اطلاعات به همراه پویانمایی محو شدن اطلاعات آن</h2>
<table>
    <tr>
        <th>
            عملیات
        </th>
        <th>
            عنوان
        </th>
    </tr>
    @foreach (var item in Model)
    {
        <tr>
            <td>
                <span id="row-@item.Id">حذف</span>
            </td>
            <td>
                @item.Title
            </td>
        </tr>
    }
</table>
@section JavaScript
{
    <script type="text/javascript">
        $(function () {
            $('span[id^="row"]').click(function () {
                var span = $(this);
                var postId = span.attr('id').replace('row-', '');
                var tableRow = span.parent().parent();
                $.ajax({
                    type: "POST",
                    url: '@postUrl',
                    data: JSON.stringify({ postId: postId }),
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (xhr.status == 403) {
                            window.location = "/login";
                        }
                        else if (status === 'error' || !data || data == "nok") {
                            alert('خطایی رخ داده است');
                        }
                        else {
                            $(tableRow).fadeTo(600, 0, function () {
                                $(tableRow).remove();
                            });
                        }
                    }
                });
            });
        });
    </script>
}
کدهای View برنامه را در ادامه ملاحظه می‌کنید. اطلاعات مطالب دریافتی به صورت یک جدول در صفحه نمایش داده شده‌اند. در هر ردیف توسط یک span که با css تزئین گردیده است، یک دکمه حذف را تدارک دیده‌ایم. برای اینکه در حین کار با jQuery بتوانیم id هر ردیف را بدست بیاوریم، این id را در قسمتی از id این span اضافه شده قرار داده‌ایم.
در کدهای اسکریپتی صفحه، ابتدا کلیک بر روی کلیه spanهایی که id آن‌ها با row شروع می‌شود را مونیتور خواهیم کرد:
 $('span[id^="row"]').click(function () {
سپس هر زمان که بر روی یکی از این spanها کلیک شد، می‌توان بر اساس span جاری، id و همچنین tableRow مرتبط را استخراج کرد:
 var span = $(this);
var postId = span.attr('id').replace('row-', '');
var tableRow = span.parent().parent();
اکنون که به این اطلاعات دسترسی پیدا کرده‌ایم، تنها کافی است آن‌ها را توسط متد ajax به کنترلر برنامه برای پردازش نهایی ارسال نمائیم. همچنین در پایان کار عملیات، توسط متدهای fadeTo و remove ایی که ملاحظه می‌کنید، سبب حذف نمایشی یک ردیف به همراه پویانمایی محو آن خواهیم شد.


دریافت کدها و پروژه کامل این قسمت
jQueryMvcSample06.zip 
مطالب
نحوه‌ی صحیح فراخوانی SQL Aggregate Functions حین استفاده از LINQ

SQL Aggregate Functions که مد نظر شما هستند مانند Min ، Max ، Sum و امثال آن. بحث LINQ هم زمانیکه از الگوی Repository استفاده شود مستقل از نوع ORM مورد نظر خواهد شد؛ بنابراین در اینجا مقصود از LINQ می‌تواند LINQ to SQL ، LINQ to Entities ، LINQ to NHibernate و کلا هر نوع ORM دیگری با پشتیبانی از LINQ باشد.
صورت مساله هم این است: آیا نوشتن عبارت LINQ ایی به شکل زیر صحیح است؟
decimal amount = respository.Transactions
.Where(t=>t.TransactionDate>new DateTime(2010,10,13))
.Sum(t=>t.Amount);
پاسخ: خیر!
توضیحات:
عبارت LINQ فوق در نهایت به شکل زیر ترجمه خواهد شد:
-- Region Parameters
-- @p0: DateTime [2010/10/13 12:00:00 ق.ظ]
-- EndRegion
SELECT SUM([t0].[Amount]) AS [value]
FROM [Transactions] AS [t0]
WHERE [t0].[TransactionDate] > @p0
و اتفاقا در این سیستم پس از تاریخ 2010/10/13 هیچ تراکنشی ثبت نشده است؛ بنابراین خروجی این کوئری null خواهد بود و نه صفر. همینجا است که یکی از استثناهای زیر صادر شده و ادامه‌ی برنامه با مشکل مواجه خواهد شد:
- System.InvalidOperationException: The cast to value type 'decimal' failed because the materialized value is null.
- InvalidOperationException: The null value cannot be assigned to a member with type decimal which is a non-nullable value type.

مشکل هم از اینجا ناشی می‌شود که متغییری از نوع deciaml یا int و امثال آن، مقدار دریافتی نال را نمی‌پذیرند. برای رفع این مشکل باید عبارت LINQ فوق به صورت زیر بازنویسی شود (و اهمیتی هم ندارد که Sum است یا Max یا Avg و غیره؛ در مورد بکارگیری تمام SQL Aggregate Functions در یک عبارت LINQ ، این مورد باید لحاظ گردد):
decimal amount = respository.Transactions
.Where(t=>t.TransactionDate>new DateTime(2010,10,13))
.Sum(t=>(decimal?)t.Amount)??0;

دقیقا به همین علت است که در دات نت، nullable types تعریف شده‌اند. امکان ذخیره سازی null‌ در یک متغیر برای مثال از نوع decimal وجود ندارد اما نوع decimal? (و یا Nullable<decimal> به بیانی دیگر) این قابلیت را دارد.
شاید بگوئید که در اینجا با تغییر تعریف متغیر به decimal? amount مشکل حل می‌شود، اما خیر. تعریف extension method مربوط به sum به صورت زیر است:

public static TResult Sum<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> selector)

در این تعریف به TResult دقت نمائید؛ هم بیانگر نوع خروجی نهایی متد و هم مشخص سازنده‌ی نوع پارامتری است که خروجی Lambda Expression را تشکیل می‌دهد. به این معنا که سی شارپ، TResult را از lambda expression دریافت کرده و خروجی Sum را بر همان مبنا و نوع تشکیل می‌دهد. بنابراین برای دریافت خروجی nullable باید TResult ایی nullable را همانند مثال فوق ایجاد کنیم.

خلاصه بحث:
اگر در کدهای LINQ خود که با بانک اطلاعاتی سر و کار دارند از معادل‌های SQL Aggregate Functions استفاده کرده‌اید، آن‌ها را یافته و نکته‌ی nullable TResult فوق را به آن‌ها اعمال کنید؛ در غیر اینصورت منتظر باشید تا روزی برنامه شما به سادگی کرش کند.


مطالب
بررسی Bad code smell ها: زنجیره پیام یا Message chain
این کد بد بو در دسته «جلوگیری کنندگان از تغییر» قرار می‌گیرد. معمولا زمانیکه فراخوانی‌هایی مانند تکه کد زیر را در بخشی از کد مشاهده کردید، با چنین کد بد بویی مواجه هستید.  
MethodA().MethodB().MethodC();
فراخوانی هر یک از این متدها در خطی مجزا از کد نیز تشکیل دهنده‌ی این الگوی بد است. استفاده کننده‌ی از این زنجیره پیام، برای استفاده‌ی درست از آن، باید در جریان هریک از حلقه‌های زنجیره و ترتیب فراخوانی آنها باشد. در صورتیکه هر یک از حلقه‌های زنجیره تغییری داشتند، استفاده کننده  نیز باید تغییر کنند. 
به طور مثال:
public class RepresentativeEmployeeQuery 
{ 
    public dynamic GetById(int id) 
    { 
        throw new NotImplementedException(); 
    } 
} 
public class RepresentativeQuery 
{ 
    public dynamic GetById(int id) 
    { 
        throw new NotImplementedException(); 
    } 
} 
public class CustomerQuery 
{ 
    public dynamic GetById(int id) 
    { 
        throw new NotImplementedException(); 
    } 
}
public static class Programm 
{ 
    static void Main(string[] args) 
    { 
        var customer = new CustomerQuery().GetById(1); 
        var representativeId = customer.RepresentativeId; 
        var representative = new RepresentativeQuery().GetById(representativeId); 
        var managerId = representative.ManagerId; 
        var manager  = new RepresentativeEmployeeQuery().GetById(managerId); 
        var managerName = manager.FullName; 
    } 
}
  • کلاس CustomerQuery پرس و جوهای مربوط به مشتری را مدیریت می‌کند.
  • کلاس RepresentativeQuery پرس و جوهای مربوط به نمایندگی را مدیریت می‌کند .
  • کلاس RepresentativeEmployeeQuery پرس و جوهای مربوط به کارمندان نمایندگی را مدیریت می‌کند.
در مثال ذکر شده می‌خواهیم نام مدیریت نمایندگی ای را که یک مشتری از آن خرید کرده است، بدانیم. صرفا جهت نمایش مثال، این کار را در متد main انجام داده‌ایم.  
مشاهده می‌کنید که زنجیره‌ای از پیام‌ها از CustomerQuery تا پایین‌ترین قسمت یعنی RepresentativeEmployeeQuery ارسال شده است. هر یک از مراحل زنجیره بخشی از منطق دریافت نام را مدیریت می‌کنند. مانند دریافت مشتری، دریافت نمایندگی آن و دریافت مدیریت نمایندگی. 

مشکلات این کد بد بو 

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


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

روش‌های اصلاح این کد بد بو حول مخفی کردن زنجیره فراخوانی‌ها هستند. اما در مواردی مانند مثال ذکر شده در این مطلب امکان ادغام یک یا چند متد نیز وجود دارد. به شرطی که این کار ناقض اصل Single responsibility یا دیگر اصول شیء گرایی نباشد. دو نمونه از روش‌های اصلاح این کد بد بو به صورت زیر هستند: 
  • مخفی کردن زنجیره فراخوانی و مستقل سازی استفاده کننده از زنجیره فرخوانی (Hide delegate)  
  • ادغام بخشی از متدها در زنجیره فراخوانی و از بین بردن زنجیره فراخوانی 


چه کدهایی Message Chain نیستند؟ 

معمولا کدهایی که از الگوی domain specific language پیروی می‌کنند، ممکن است شباهت بسیاری به مثال مطرح شده داشته باشند. اما از نظر مفهومی با الگوی مطرح شده متفاوت هستند؛ به این صورت که در کد بد بوی زنجیره پیام‌ها، استفاده کننده از متد نیاز به دانستن تمامی حلقه‌ها و ترتیب آنها را دارد، ولی در domain specific language‌ها معمولا نحوه استفاده از متدها به صورت شبه زبانی گویا هدایت شده و معمولا ترتیب به صورت مخفی مدیریت می‌شود.
مطالب
آشنایی با Window Function ها در SQL Server بخش دوم
قبل از مطالعه این بخش لطفا آشنایی با Window Function‌ها در SQL Server بخش اول را مطالعه نمایید.
       دربخش اول،در مورد Syntax مربوط به Over Clause صحبت کردیم، و برای درک استفاده از Over Clause، مثالهایی را بررسی نمودیم، در این بخش نیز،به تفاوت Row Clause و Range Clause می پردازیم. 
مثال:  با ایجاد یک Script،عملیات جمع روی یک فیلد خاص، بوسیله Row Clause و Range Clause انجام می‌دهیم. تا تفاوت آنها را درک نماییم.
در ادامه Script زیر را اجرا نمایید:
DECLARE @Test TABLE
(
RowID INT IDENTITY,
FName VARCHAR(20),
Salary SMALLINT
);
INSERT INTO @Test (FName, Salary)
VALUES ('George', 800),
('Sam', 950),
('Diane', 1100),
('Nicholas', 1250),
('Samuel', 1250),
('Patricia', 1300),
('Brian', 3000),
('Thomas', 1600),
('Fran', 2450),
('Debbie', 2850),
('Mark', 2975),
('James', 3000),
('Cynthia', 3000),
('Christopher', 5000);

SELECT RowID,FName,Salary,
       SumByRows = SUM(Salary) OVER (ORDER BY Salary ROWS UNBOUNDED PRECEDING),
   SumByRange = SUM(Salary) OVER (ORDER BY Salary RANGE UNBOUNDED PRECEDING)
FROM @Test
ORDER BY RowID;
خروجی بصورت زیر خواهد بود:

با مشاهده شکل بالا، به وضوح می‌توان تفاوت Row و Range را تشخیص داد. در Script بالا از UNBOUNDED PRECEDING استفاده کردیم ، و مفهوم  قالب آن به شرح ذیل می‌باشد:
مقدار فیلد Salary سطر جاری = جمع مقادیر فیلد Salary همه سطر‌های ماقبل،سطر جاری + مقدار فیلد Salary سطر جاری
Row Clause بصورت فیزیکی به سطرها می‌نگرد و قالب بیان شده در Script را،روی تمامی سطرها،نسبت به جایگاه آنها در جدول، به ترتیب اعمال می‌نماید.و در شکل نیز قابل مشاهده می باشد، یعنی به چیدمان سطر‌ها در خروجی که بصورت فیزیکی نمایش داده شده است، توجه می کند، و حاصل جمع هر سطر برابر است با حاصل جمع سطرهای ماقبل + سطر جاری
اما Range Clause:به چیدمان فیزیکی سطرها توجه نمی‌کند، بلکه بصورت منطقی به مقدار فیلد Salary سطرها توجه می‌نماید، یعنی مقادیری که در یک محدوده(Range) قرار دارند، حاصل جمع آنها،یکی است.
مقدار فیلد Salary سطر چهار و پنج برابر است با 1250 بنابراین حاصل جمع آنها برابر هم می‌باشد. و بصورت زیر محاسبه می‌شود:
 800 + 950 + 1100 + 1250 + 1250 =5350
روش بیان شده، در مورد سطرهای 12 و 13 نیز صادق است.
امیدوارم با مثالهایی که در بخش اول و بخش دوم بررسی نمودیم، روش استفاده از Over Clause را درک کرده باشیم. 
 Window Function‌ها را به چهار بخش تقسیم بندی شده اند، که به شرح ذیل می‌باشد:
1- Ranking functions (توابع رتبه بندی)، که بررسی نمودیم.
2- NEXT VALUE FOR ، که در بحث ایجاد Sequence آن را بررسی نمودیم.
3- Aggregate Functions (توابع جمعی)، اکثرا با اینگونه توابع آشنا هستیم.
4- Analytic Functions (توابع تحلیلی) که در بخش بعدی آن را بررسی می‌نماییم.
     یکی از منابع بسیار مفید در مورد Window Function ها کتاب Microsoft SQL Server 2012 High-Performance T-SQL Using Window Functions ،  می باشد،که بطور کامل به  Window Function‌ها اختصاص دارد و تکنیک‌های بسیار مفیدی را بیان می‌کند. مطالعه آن به علاقمندان، پیشنهاد می‌گردد.
موفق باشید.
مطالب
سرعت واکشی اطلاعات در List و Dictionary
دسترسی به داده‌ها پیش شرط انجام همه‌ی منطق‌های اکثر نرم افزار‌های تجاری می‌باشد. داده‌های ممکن در حافظه ، پایگاه داده ، فایل‌های فیزیکی و هر منبع دیگری قرار گرفته باشند.
هنگامی که حجم داده‌ها کم باشد شاید روش دسترسی و الگوریتم مورد استفاده اهمیتی نداشته باشد اما با افزایش حجم داده‌ها روش‌های بهینه‌تر تاثیر مستقیم در کارایی برنامه دارند.
در این مثال سعی بر این است که در یک سناریوی خاص تفاوت بین Dictionary و List را بررسی کنیم :
فرض کنید 2 کلاس Student  و Grade موجود است که وظیفه‌ی نگهداری اطلاعات دانش آموز و نمره را بر عهده دارند.
    public class Grade
    {
        public Guid StudentId { get; set; }
        public string Value { get; set; }

        public static IEnumerable<Grade> GetData()
        {
            for (int i = 0; i < 10000; i++)
            {
                yield return new Grade
                                 {
                                     StudentId = GuidHelper.ListOfIds[i], Value = "Value " + i
                                 };
            }
        }
    }

    public class Student
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Grade { get; set; }

        public static IEnumerable<Student> GetStudents()
        {
            for (int i = 0; i < 10000; i++)
            {
                yield return new Student
                                 {
                                     Id = GuidHelper.ListOfIds[i],
                                     Name = "Name " + i
                                 };
            }
        }
    }
از کلاس GuidHelper برای تولید و نگهداری شناسه‌های یکتا برای دانش آموز کمک گرفته شده است :
    public class GuidHelper
    {
        public static List<Guid> ListOfIds=new List<Guid>();

        static GuidHelper()
        {
            for (int i = 0; i < 10000; i++)
            {
                ListOfIds.Add(Guid.NewGuid());
            }
        }
    }
سپس لیستی از دانش آموزان و نمرات را درون حافظه ایجاد کرده و با یک حلقه  نمره‌ی هر دانش آموز به Property مورد نظر مقدار داده می‌شود.

ابتدا از LINQ روی لیست برای پیدا کردن نمره‌ی مورد نظر استفاده کرده و در روش دوم برای پیدا کردن نمره‌ی هر دانش آموز از Dictionary  استفاده شده :
    internal class Program
    {
        private static void Main(string[] args)
        {
            var stopwatch = new Stopwatch();
            List<Grade> grades = Grade.GetData().ToList();
            List<Student> students = Student.GetStudents().ToList();

            stopwatch.Start();
            foreach (Student student in students)
            {
                student.Grade = grades.Single(x => x.StudentId == student.Id).Value;
            }
            stopwatch.Stop();
            Console.WriteLine("Using list {0}", stopwatch.Elapsed);
            stopwatch.Reset();
            students = Student.GetStudents().ToList();
            stopwatch.Start();
            Dictionary<Guid, string> dictionary = Grade.GetData().ToDictionary(x => x.StudentId, x => x.Value);

            foreach (Student student in students)
            {
                student.Grade = dictionary[student.Id];
            }
            stopwatch.Stop();
            Console.WriteLine("Using dictionary {0}", stopwatch.Elapsed);
            Console.ReadKey();
        }
    }
نتیجه‌ی مقایسه در سیستم من اینگونه می‌باشد :



همانگونه که مشاهده می‌شود در این سناریو خواندن نمره از روی Dictionary بر اساس 'کلید' بسیار سریع‌تر از انجام یک پرس و جوی LINQ روی لیست است.

زمانی که از LINQ on list
   student.Grade = grades.Single(x => x.StudentId == student.Id).Value;
برای پیدا کردن مقدار مورد نظر یک به یک روی اعضا لیست حرکت می‌کند تا به مقدار مورد نظر برسد در نتیجه پیچیدگی زمانی آن O n هست. پس هر چه میزان داده‌ها بیشتر باشد این روش کند‌تر می‌شود.

زمانی که از Dictonary
         student.Grade = dictionary[student.Id];
برای پیدا کردن مقدار استفاده می‌شود با اولین تلاش مقدار مورد نظر یافت می‌شود پس پیچیدگی زمانی آن O 1 می‌باشد.

در نتیجه اگر نیاز به پیدا کردن اطلاعات بر اساس یک مقدار یکتا یا کلید باشد تبدیل اطلاعات به Dictionary و خواندن از آن بسیار به صرفه‌تر است.

تفاوت این 2 روش وقتی مشخص می‌شود که میزان داده‌ها زیاد باشد.

در همین رابطه (1 ، 2

DictionaryVsList.zip
نظرات مطالب
فعال سازی عملیات CRUD در Kendo UI Grid
مثال فوق را (KendoUI06) اگر برای ASP.NET MVC بازنویسی کنیم به کدهای ذیل خواهیم رسید:
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Mvc;
using Kendo.DynamicLinq;
using KendoUI06Mvc.Models;
using Newtonsoft.Json;

namespace KendoUI06Mvc.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(); // shows the page.
        }

        [HttpDelete]
        public ActionResult DeleteProduct(int id)
        {
            var item = ProductDataSource.LatestProducts.FirstOrDefault(x => x.Id == id);
            if (item == null)
                return new HttpNotFoundResult();

            ProductDataSource.LatestProducts.Remove(item);

            return Json(item);
        }

        [HttpGet]
        public ActionResult GetProducts()
        {
            var request = JsonConvert.DeserializeObject<DataSourceRequest>(
               this.Request.Url.ParseQueryString().GetKey(0)
            );

            var list = ProductDataSource.LatestProducts;
            return Json(list.AsQueryable()
                       .ToDataSourceResult(request.Take, request.Skip, request.Sort, request.Filter),
                       JsonRequestBehavior.AllowGet);
        }

        [HttpPost]
        public ActionResult PostProduct(Product product)
        {
            if (!ModelState.IsValid)
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);

            var id = 1;
            var lastItem = ProductDataSource.LatestProducts.LastOrDefault();
            if (lastItem != null)
            {
                id = lastItem.Id + 1;
            }
            product.Id = id;
            ProductDataSource.LatestProducts.Add(product);

            // گرید آی دی جدید را به این صورت دریافت می‌کند
            return Json(new DataSourceResult { Data = new[] { product } });
        }

        [HttpPut] // Add it to fix this error: The requested resource does not support http method 'PUT'
        public ActionResult UpdateProduct(int id, Product product)
        {
            var item = ProductDataSource.LatestProducts
                                        .Select(
                                            (prod, index) =>
                                                new
                                                {
                                                    Item = prod,
                                                    Index = index
                                                })
                                        .FirstOrDefault(x => x.Item.Id == id);
            if (item == null)
                return new HttpNotFoundResult();


            if (!ModelState.IsValid || id != product.Id)
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);

            ProductDataSource.LatestProducts[item.Index] = product;

            //Return HttpStatusCode.OK
            return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
    }
}
در این حالت View برنامه فقط جهت ذکر آدرس‌های جدید باید اصلاح شود و نیاز به تغییر دیگری ندارد:
        var productsDataSource = new kendo.data.DataSource({
            transport: {
                read: {
                    url: "@Url.Action("GetProducts","Home")",
                    dataType: "json",
                    contentType: 'application/json; charset=utf-8',
                    type: 'GET'
                },
                create: {
                    url: "@Url.Action("PostProduct","Home")",
                    contentType: 'application/json; charset=utf-8',
                    type: "POST"
                },
                update: {
                    url: function (product) {
                        return "@Url.Action("UpdateProduct","Home")/" + product.Id;
                        },
                        contentType: 'application/json; charset=utf-8',
                        type: "PUT"
                    },
                    destroy: {
                        url: function (product) {
                            return "@Url.Action("DeleteProduct","Home")/" + product.Id;
                        },
                        contentType: 'application/json; charset=utf-8',
                        type: "DELETE"
                    },
                    parameterMap: function (options) {
                        return kendo.stringify(options);
                    }
                },