بازخوردهای دوره
مدیریت استثناءها در حین استفاده از واژه‌های کلیدی async و await
همانطور که در بالا اشاره کردید "در مثال فوق، نحوه‌ی ترکیب دو Task را توسط Task.WhenAll .... "در برخی موارد استفاده از async باعث افزایش کارآیی نیز می‌شود، آیا در موردی که مثلا من در یک اکشن برای انجام کاری نیاز به 4 درخواست  مجزا به دیتابیس دارم و بعد از گرفتن نتیجه این 4 درخواست می‌توانم درخواست نهایی را به دیتابیس بفرستم، استفاده از async باعث افزایش کارایی نیز می‌شود ؟
برای تشریح بهتر من نتیجه تست خود را اضافه میکنم. من از mvc5 و EF6  database first استفاده کردم.
حالت sync :
var watch = Stopwatch.StartNew();

 int actionId = db.CF_AccessLevel.Where(a => a.Name.ToLower().Trim() == "Edit").Select(a => a.CF_AccessLevelId).Single();

 int moduleItemId = db.CF_ModuleItem.Where(m => m.Title.ToLower().Trim() == "license".ToLower().Trim()).Select(m => m.CF_ModuleItemId).Single();

 int groupRoleId = db.Users.Where(u => u.UserId == 1).Select(u => u.UserRoleId).Single();

watch.Stop();
 var elapsedMs = watch.ElapsedMilliseconds;

حالت async:
var watch = Stopwatch.StartNew();
 var something = Task<int>.Factory.StartNew(() => db.CF_AccessLevel.Where(a => a.Name.ToLower().Trim() == "Edit").Select(a => a.CF_AccessLevelId).Single());
something.Wait();
int actionId = something.Result;

var something1 = Task<int>.Factory.StartNew(() => db.CF_ModuleItem.Where(m => m.Title.ToLower().Trim() == "license".ToLower().Trim()).Select(m => m.CF_ModuleItemId).Single());
 something1.Wait();
 int moduleItemId = something1.Result;

var something2 = Task<int>.Factory.StartNew(() => db.Users.Where(u => u.UserId == 1).Select(u => u.UserRoleId).Single());
 something2.Wait();
 int groupRoleId = something2.Result;
 watch.Stop();
 var elapsedMs = watch.ElapsedMilliseconds;
در هر  حالت بعد از انجام  3 درخواست ، درخواست نهایی را به سرور میفرستم (در کدهای بالا موجود نیست)
و نتیجه با جزئیات را در آخر اضافه کرده ام :
اما خلاصه میانگین روش sync 222 ms و روش async 191.75ms می‌باشد حدود 35.25ms تفات وجود دارد.
حال آیا تفاوت معنی دار می‌باشد؟ آیا کد async نوشته شده صحیح است؟ اگر صحیح نیست چه روشی صحیح میباشد؟ اگر نباید از async استفاده شود چه روشی بهتر است؟
همانطور که از کد مشخص است برای هدف authorization  نوشتم، ولی اگر بخواهم به صورت async در فیلتر استفاده کنم امکانپذیر نیست ، آیا راهی وجود دارد برای استفاده از async در فیلتر سفارشی توی    mvc5 ؟
202  221
 179 226
 198 208
 197  219
 188  245
 195  207
 193  217
 187  220
 171  212
 227  215
 177  312
 187  222
 191.75  227
   
اشتراک‌ها
بررسی معماری داده با روش multi tenant

Data architecture is an area in which the optimal degree of isolation for a SaaS application can vary significantly depending on technical and business considerations. Experienced data architects are used to considering a broad spectrum of choices when designing an architecture to meet a specific set of challenges, and SaaS is certainly no exception. We shall examine three broad approaches, each of which lies at a different location in the continuum between isolation and sharing 

بررسی معماری داده با روش multi tenant
اشتراک‌ها
معرفی Minimal Real-Time API برای دات نت

This article provides an introduction to a lightweight API for ASP.NET web services provided by DotNetify.SignalR NuGet package that can reduce a lot of boilerplate code when implementing real-time updates, and provide an example of how to create a real-time web component with VueJS without using Node.JS build. 

معرفی Minimal Real-Time API برای دات نت
مطالب
امکان استفاده از یک هارد SSD بجای RAM در SQL Server 2014
Buffer Pool یکی از مصرف کنندگان اصلی حافظه در SQL Server است. برای مثال زمانیکه اطلاعاتی را از بانک اطلاعاتی دریافت می‌کنید، این داده‌ها در Buffer Pool کش می‌شوند. همچنین SQL Server اطلاعات کلیه Execution Plans را نیز در Plan Cache که جزئی از Buffer Pool است، برای استفاده‌ی مجدد نگهداری می‌کند. هر چقدر حافظه‌ی فیزیکی سرور شما بیشتر باشد، مقدار Buffer Pool نیز به همین میزان افزایش خواهد یافت که البته حداکثر آن‌را می‌توان در تنظیمات حافظه‌ی سرور محدود کرد (Max Server Memory setting).
در دنیای واقعی میزان حافظه‌ی فیزیکی سرورها محدود است. در SQL Server 2014 راه حلی برای این مشکل تحت عنوان Buffer Pool Extensions ارائه شده‌است که محل قرارگیری آن‌را در تصویر ذیل مشاهده می‌کنید:


Buffer Pool Extensions از یک فایل ساده که به آن Extension File نیز گفته می‌شود، تشکیل شده‌است و امکان ذخیره سازی آن بر روی هاردهای سریعی مانند SSD Driveها میسر است. این فایل، ساختاری را همانند page file، در سیستم عامل ویندوز دارد. در این حالت بجای اضافه کردن RAM بیشتر به سرور، یک Extension File را می‌توان بکار گرفت. هر زمان که Buffer Pool اصلی تحت فشار قرار گیرد (به میزان حافظه‌ای بیش از حافظه‌ی فیزیکی سرور نیاز باشد)، از این افزونه‌ی فایلی استفاده خواهد شد.
اطلاعات جزئیات Buffer Pool را توسط کوئری ذیل می‌توان بدست آورد:
 Select * from sys.dm_os_buffer_descriptors


نحوه‌ی فعال سازی و تنظیم Buffer Pool Extensions

قبل از هر کاری بهتر است وضعیت افزونه‌ی Buffer pool را بررسی کرد:
 select * from sys.dm_os_buffer_pool_extension_configuration


همانطور که ملاحظه می‌کنید، در حالت پیش فرض غیرفعال است.
سپس یک فایل یک گیگابایتی را به عنوان افزونه‌ی Buffer pool ایجاد می‌کنیم.
 ALTER SERVER CONFIGURATION
SET BUFFER POOL EXTENSION ON
 (FILENAME = 'd:\BufferPoolExt.BPE', SIZE = 1GB);
توصیه شده‌است که این فایل را در یک درایور پر سرعت SSD قرار دهید؛ ولی محدودیتی از لحاظ محل قرارگیری ندارد (هر چند به نظر فقط در حالتیکه از SSD Drive استفاده شود واقعا کار می‌کند).
اینبار اگر کوئری اول را اجرا کنیم، چنین خروجی قابل مشاهده است:


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


تغییر اندازه‌ی افزونه‌ی Buffer pool

اگر سعی کنیم، یک گیگابایت را مثلا به 10 گیگابایت افزایش دهیم:
 ALTER SERVER CONFIGURATION
SET BUFFER POOL EXTENSION ON
 (FILENAME = 'd:\BufferPoolExt.BPE', SIZE = 10GB);
با خطای ذیل مواجه خواهیم شد:
 Could not change the value of the 'BPoolExtensionPath' property
برای رفع این مشکل، ابتدا باید افزونه‌ی Buffer pool را غیرفعال کرد:
 ALTER SERVER CONFIGURATION
SET BUFFER POOL EXTENSION OFF
سپس می‌توان مجددا اندازه و یا مسیر دیگری را مشخص کرد. بهتر است اندازه‌ی این فایل را حدود 16 برابر حداکثر میزان حافظه‌ی سرور (Max Server Memory) تعیین کنید.
همچنین توصیه شده‌است که پس از غیرفعال کردن این افزونه، بهتر است یکبار instance جاری را ری استارت کنید.


چه زمانی بهتر است از افزونه‌ی Buffer pool استفاده شود؟
در محیط‌های read-heavy OLTP، استفاده از یک چنین افزونه‌ای می‌تواند میزان کارآیی و پاسخگویی سیستم را به شدت افزایش دهد (تا 50 درصد).


سؤال: آیا غیرفعال کردن افزونه‌ی Buffer pool سبب از دست رفتن اطلاعات می‌شود؟
خیر. BPE، تنها clean pages را در خود ذخیره می‌کند؛ یعنی تنها اطلاعاتی که Commit شده‌اند در آن حضور خواهند داشت و در این حالت حذف آن یا ری استارت کردن سرور، سبب از دست رفتن اطلاعات نخواهند شد.


برای مطالعه بیشتر

Buffer Pool Extension
SQL Server 2014 Buffer Pool Extensions
Do you require a SSD to use the Buffer Pool Extension feature in SQL Server 2014
Buffer Pool Extensions in SQL Server 2014
SQL Server 2014 – Buffer Pool Extension
مطالب دوره‌ها
بررسی سرعت و کارآیی AutoMapper
AutoMapper تنها کتابخانه‌ی نگاشت اشیاء مخصوص دات نت نیست. در این مطلب قصد داریم سرعت AutoMapper را با حالت نگاشت دستی، نگاشت توسط EmitMapper و نگاشت به کمک ValueInjecter، مقایسه کنیم.


مدل مورد استفاده

در اینجا قصد داریم، شیء User را یک میلیون بار توسط روش‌های مختلف، به خودش نگاشت کنیم و سرعت انجام این‌کار را در حالت‌های مختلف اندازه گیری نمائیم:
public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public DateTime LastLogin { get; set; }
}


روش بررسی سرعت انجام هر روش

برای کاهش کدهای تکراری، می‌توان قسمت تکرار شونده را به صورت یک Action، در بین سایر کدهایی که هر بار نیاز است به یک شکل فراخوانی شوند، قرار داد:
public static void RunActionMeasurePerformance(Action action)
{
    GC.Collect();
    var initMemUsage = Process.GetCurrentProcess().WorkingSet64;
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    action();
    stopwatch.Stop();
    var currentMemUsage = Process.GetCurrentProcess().WorkingSet64;
    var memUsage = currentMemUsage - initMemUsage;
    if (memUsage < 0) memUsage = 0;
    Console.WriteLine("Elapsed time: {0}, Memory Usage: {1:N2} KB", stopwatch.Elapsed, memUsage / 1024);
}


انجام آزمایش

در مثال زیر، ابتدا یک میلیون شیء User ایجاد می‌شوند و سپس هربار توسط روش‌های مختلفی به شیء User دیگری نگاشت می‌شوند:
static void Main(string[] args)
{
    var length = 1000000;
    var users = new List<User>(length);
    for (var i = 0; i < length; i++)
    {
 
        var user = new User
        {
            Id = i,
            UserName = "User" + i,
            Password = "1" + i + "2" + i,
            LastLogin = DateTime.Now
        };
        users.Add(user);
    }
 
    Console.WriteLine("Custom mapping");
    RunActionMeasurePerformance(() =>
    {
        var userList =
            users.Select(
                o =>
                    new User
                    {
                        Id = o.Id,
                        UserName = o.UserName,
                        Password = o.Password,
                        LastLogin = o.LastLogin
                    }).ToList();
    });
 
    Console.WriteLine("EmitMapper mapping");
    RunActionMeasurePerformance(() =>
    {
        var map = EmitMapper.ObjectMapperManager.DefaultInstance.GetMapper<User, User>();
        var emitUsers = users.Select(o => map.Map(o)).ToList();
    });
 
    Console.WriteLine("ValueInjecter mapping");
    RunActionMeasurePerformance(() =>
    {
        var valueUsers = users.Select(o => (User)new User().InjectFrom(o)).ToList();
    });
 
    Console.WriteLine("AutoMapper mapping, DynamicMap using List");
    RunActionMeasurePerformance(() =>
    {
        var userMap = Mapper.DynamicMap<List<User>>(users).ToList();
    });
 
    Console.WriteLine("AutoMapper mapping, Map using List");
    RunActionMeasurePerformance(() =>
    {
        var userMap = Mapper.Map<List<User>>(users).ToList();
    });
 
    Console.WriteLine("AutoMapper mapping, Map using IEnumerable");
    RunActionMeasurePerformance(() =>
    {
        var userMap = Mapper.Map<IEnumerable<User>>(users).ToList();
    });
 
 
    Console.ReadKey();
}


خروجی آزمایش

در ادامه یک نمونه‌ی خروجی نهایی را مشاهده می‌کنید:
 Custom mapping
Elapsed time: 00:00:00.4869463, Memory Usage: 58,848.00 KB

EmitMapper mapping
Elapsed time: 00:00:00.6068193, Memory Usage: 62,784.00 KB

ValueInjecter mapping
Elapsed time: 00:00:15.6935578, Memory Usage: 21,140.00 KB

AutoMapper mapping, DynamicMap using List
Elapsed time: 00:00:00.6028971, Memory Usage: 7,164.00 KB

AutoMapper mapping, Map using List
Elapsed time: 00:00:00.0106244, Memory Usage: 680.00 KB

AutoMapper mapping, Map using IEnumerable
Elapsed time: 00:00:01.5954456, Memory Usage: 40,248.00 KB

ValueInjecter از همه کندتر است.
EmitMapper از AutoMapper سریعتر است (البته فقط در بعضی از حالت‌ها).
سرعت AutoMapper زمانیکه نوع آرگومان ورودی به آن به IEnumerable تنظیم شود، نسبت به حالت استفاده از List معمولی، به مقدار قابل توجهی کندتر است. زمانیکه از List استفاده شده، سرعت آن از سرعت حالت نگاشت دستی (مورد اول) هم بیشتر است.
متد DynamicMap اندکی کندتر است از متد Map.

در این بین اگر ValueInjecter را از لیست حذف کنیم، به نمودار ذیل خواهیم رسید (اعداد آن برحسب ثانیه هستند):



البته حین انتخاب یک کتابخانه، باید به آخرین تاریخ به روز شدن آن نیز دقت داشت و همچنین میزان استقبال جامعه‌ی برنامه نویس‌ها و از این لحاظ، AutoMapper نسبت به سایر کتابخانه‌های مشابه در صدر قرار می‌گیرد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید:
AM_Sample06.zip
مطالب
نحوه کاهش مصرف حافظه EF Code first حین گزارشگیری از اطلاعات
تمام ORMهای خوب، دارای سطح اول کش هستند. از این سطح جهت نگهداری اطلاعات تغییرات صورت گرفته روی اشیاء و سپس اعمال نهایی آن‌ها در پایان یک تراکنش استفاده می‌شود. بدیهی است جمع آوری این اطلاعات اندکی بر روی سرعت انجام کار و همچنین بر روی میزان مصرف حافظه برنامه تاثیرگذار است. به علاوه یک سری از اعمال مانند گزارشگیری نیازی به این سطح اول کش ندارند. اطلاعات مورد استفاده در آن‌ها مانند نمایش لیستی از اطلاعات در یک گرید، حالت فقط خواندنی دارد. در EF Code first برای یک چنین مواردی استفاده از متد الحاقی AsNoTracking تدارک دیده شده است که سبب خاموش شدن سطح اول کش می‌شود. در ادامه در طی یک مثال، اثر این متد را بر روی سرعت و میزان مصرف حافظه برنامه بررسی خواهیم کرد.

کدهای کامل این مثال را در ذیل ملاحظه می‌کنید:

using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Diagnostics;
using System.Linq;

namespace EFGeneral
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class MyContext : DbContext
    {
        public DbSet<User> Users { get; set; }
    }

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

        protected override void Seed(MyContext context)
        {            
            for (int i = 0; i < 21000; i++)
            {
                context.Users.Add(new User { Name = "name " + i });
                if (i % 1000 == 0)
                    context.SaveChanges();
            }
            base.Seed(context);
        }
    }

    public class PerformanceHelper
    {
        public static string RunActionMeasurePerformance(Action action)
        {
            GC.Collect();
            long initMemUsage = Process.GetCurrentProcess().WorkingSet64;

            var stopwatch = new Stopwatch();
            stopwatch.Start();

            action();

            stopwatch.Stop();

            var currentMemUsage = Process.GetCurrentProcess().WorkingSet64;
            var memUsage = currentMemUsage - initMemUsage;
            if (memUsage < 0) memUsage = 0;

            return string.Format("Elapsed time: {0}, Memory Usage: {1:N2} KB", stopwatch.Elapsed, memUsage / 1024);
        }
    }

    public static class Test
    {
        public static void RunTests()
        {
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
            StartDb();            

            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("\nRun {0}", i + 1);

                var memUsage = PerformanceHelper.RunActionMeasurePerformance(() => LoadWithTracking());
                Console.WriteLine("LoadWithTracking:\n{0}", memUsage);

                memUsage = PerformanceHelper.RunActionMeasurePerformance(() => LoadWithoutTracking());
                Console.WriteLine("LoadWithoutTracking:\n{0}", memUsage);
            }
        }

        private static void StartDb()
        {
            using (var ctx = new MyContext())
            {
                var user = ctx.Users.Find(1);
                if (user != null)
                {
                    // keep the object in memory
                }
            }
        }

        private static void LoadWithTracking()
        {
            using (var ctx = new MyContext())
            {
                var list = ctx.Users.ToList();
                if (list.Any())
                {
                    // keep the list in memory
                }
            }
        }

        private static void LoadWithoutTracking()
        {
            using (var ctx = new MyContext())
            {
                var list = ctx.Users.AsNoTracking().ToList();
                if (list.Any())
                {
                    // keep the list in memory
                }
            }
        }
    }
}

توضیحات:
مدل برنامه یک کلاس ساده کاربر است به همراه id و نام او.
سپس این کلاس توسط Context برنامه در معرض دید EF Code first قرار می‌گیرد.
در کلاس Configuration تعدادی رکورد را در ابتدای کار برنامه در بانک اطلاعاتی ثبت خواهیم کرد. قصد داریم میزان مصرف حافظه بارگذاری این اطلاعات را بررسی کنیم.
کلاس PerformanceHelper معرفی شده، دو کار اندازه گیری میزان مصرف حافظه برنامه در طی اجرای یک فرمان خاص و همچنین مدت زمان سپری شدن آن‌را اندازه‌گیری می‌کند.
در کلاس Test فوق چندین متد به شرح زیر وجود دارند:
متد StartDb سبب می‌شود تا تنظیمات ابتدایی برنامه به بانک اطلاعاتی اعمال شوند. تا زمانیکه کوئری خاصی به بانک اطلاعاتی ارسال نگردد، EF Code first بانک اطلاعاتی را آغاز نخواهد کرد.
در متد LoadWithTracking اطلاعات تمام رکوردها به صورت متداولی بارگذاری شده است.
در متد LoadWithoutTracking نحوه استفاده از متد الحاقی AsNoTracking را مشاهده می‌کنید. در این متد سطح اول کش به این ترتیب خاموش می‌شود.
و متد RunTests، این متدها را در سه بار متوالی اجرا کرده و نتیجه عملیات را نمایش خواهد داد.

برای نمونه این نتیجه در اینجا حاصل شده است:


همانطور که ملاحظه کنید، بین این دو حالت، تفاوت بسیار قابل ملاحظه است؛ چه از لحاظ مصرف حافظه و چه از لحاظ سرعت.

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

 
اشتراک‌ها
بررسی میزان پیچیدگی کدها با Microsoft.CodeAnalysis.Metrics

This page describes how to use the Microsoft.CodeAnalysis.Metrics package to perform source code analysis of .NET assemblies from a console application. Visual Studio users can perform source code analysis by clicking the "Analyze" dropdown menu and selecting "Calculate Code Metrics", but I sought to automate this process so I can generate custom code analysis reports from console applications as part of my CI pipeline. 

بررسی میزان پیچیدگی کدها با Microsoft.CodeAnalysis.Metrics
اشتراک‌ها
NET 8 Preview 6. منتشر شد

an exciting release incorporated with plenty of library updates, a new WASM mode, more source generators, constant performance improvements, and NativeAOT support on iOS. We hope you enjoy these new features and improvements. Stay tuned for more updates as we continue our journey of making .NET better, together! 

NET 8 Preview 6. منتشر شد
اشتراک‌ها
NET 5.0 Preview 6. منتشر شد

Today, we’re releasing .NET 5.0 Preview 6. It contains a small set of new features and performance improvements. The .NET 5.0 Preview 4 post covers what we are planning to deliver with .NET 5.0. Most of the features are now in the product, but some are not yet in their final state. We expect that the release will be feature-complete with Preview 8. 

NET 5.0 Preview 6. منتشر شد