نظرات مطالب
ObservableCollection در Entity Framework
سلام آقای جعفری. ممنون از مطلب مفیدتون. شاید سوالم خیلی ربطی به مطلب شما نداشته باشه ولی میخواستم ازتون بپرسم که ما وقتی توی پروژه WPF از Entity Framework Code First استفاده میکنیم باید اینترفیس INotifyPropertyChanged رو برای هر Entity پیاده سازی کنیم یا نه؟! 
مطالب
شروع به کار با EF Core 1.0 - قسمت 10 - استفاده از امکانات بومی بانک‌های اطلاعاتی
در قسمت بعد، ارتباطات self referencing را بررسی خواهیم کرد و چون EF Core هیچ راه حل بهینه‌ای را برای کوئری گرفتن از این نوع روابط سلسله مراتبی ارائه نمی‌دهد (درEF 6.x نیز به همین ترتیب)، نیاز است مستقیما SQL نویسی کرد. به همین جهت در این قسمت نحوه‌ی نوشتن کوئری‌های مستقیم SQL و اجرای آن‌ها را در EF Core بررسی می‌کنیم.


اجرای کوئری‌های خام SQL بر روی بانک اطلاعاتی، توسط EF Core

گاهی از اوقات نیاز به استفاده‌ی قابلیت خاصی از بانک اطلاعاتی مدنظر وجود دارد که توسط LINQ پشتیبانی نمی‌شود و یا کوئری SQL حاصل از LINQ to Entities آنچنان بهینه نیست. در یک چنین حالاتی راهی بجز نوشتن کوئر‌ی‌های خام SQL وجود ندارد. امکان اجرای یک چنین کوئری‌هایی توسط EF Core پیش بینی شده‌است؛ اما با این محدودیت‌ها:
 - خروجی کوئری SQL، تنها باید معادل یکی از کلاس‌های موجودیت‌های شما باشد. قرار است این محدودیت در نگارش 1.1 برطرف شود.
 - کوئری SQL نوشته شده باید تمام خواص موجودیتی را که قرار است به آن نگاشت شود، بازگشت دهد.
 - نام ستون‌های بازگشت داده شده‌ی توسط کوئری SQL باید با نام خواص موجودیت در حال کار، یکی باشند و برخلاف EF 6.x، از یک چنین عدم تطابق‌هایی صرفنظر نخواهد شد.
 -  کوئری SQL نوشته شده نباید به همراه اطلاعات ارتباطات موجودیت‌ها باشد.

در اینجا برای نوشتن کوئری‌های خام SQL می‌توان از متد FromSql مرتبط با یکی از DbSetهای برنامه استفاده کرد:
var blogs = context.Blogs
    .FromSql("SELECT * FROM dbo.Blogs")
    .ToList();
و یا حتی می‌توان از رویه‌ی ذخیره شده‌ای استفاده کرد که خروجی ستون‌های آن، معادل تمام خواص کلاس Blog باشد:
var blogs = context.Blogs
  .FromSql("EXECUTE dbo.GetMostPopularBlogs")
  .ToList();

بنابراین رفتار EF Core اندکی متفاوت است با EF 6.x. در اینجا اگر می‌خواهید از عبارت SQL خود خروجی بگیرید، باید از یکی از DbSetهای خود شروع کنید و متد FromSql را بر روی آن فراخوانی نمائید. همچنین کوئری نوشته شده باید اولا تمام ستون‌های آن DbSet رابازگشت دهد و به علاوه این ستون‌ها دقیقا با نام‌های خواص آن کلاس، تطابق داشته باشند.
علت این مسایل نیز به این دلیل است که بتوان نتیجه‌ی کوئری را به صورت خودکار وارد سیستم change tracking کرد و همچنین کوئری‌های ترکیبی LINQ را نیز در اینجا فعال کرد.


ارسال پارامترها به کوئری‌های خام SQL

تنها حالتی در EF Core که مستعد به حملات تزریق SQL است، دقیقا همین مورد دور شدن از LINQ و نوشتن عبارات مستقیم SQL است. در اینجا برای نوشتن کوئری‌های پارامتری دو حالت پیش بینی شده‌است:
الف) روش parameter place holders
در اینجا متد FromSql، بسیار شبیه به متد String.Format است، اما در عمل اینطور نیست و تمام place holders آن به صورت خودکار تبدیل به پارامتر می‌شوند:
var user = "johndoe";

var blogs = context.Blogs
  .FromSql("EXECUTE dbo.GetMostPopularBlogsForUser {0}", user)
  .ToList();
ب) روش ساخت دستی DbParameterها
اگر می‌خواهید از پارامترهای نام دار استفاده کنید، با وهله‌ای از SqlParameter شروع کرده و سپس آن‌را به متد FromSql ارسال کنید:
var user = new SqlParameter("user", "johndoe");
var blogs = context.Blogs
  .FromSql("EXECUTE dbo.GetMostPopularBlogsForUser @user", user)
  .ToList();
و یا این حالت را به شکل ساده شده‌ی ذیل نیز می‌توان مورد استفاده قرار داد:
 var results = _context.Contacts.FromSql(
@"SELECT Id, Name Address, City, State, Zip 
    FROM Contacts 
    WHERE Name IN (@p0, @p1)", name1, name2);
که در اینجا p0@ به name1 و p1@ به name2 نگاشت خواهد شد.
مزیت کار کردن با SqlParameter این است که می‌توان برای مثال Direction و SqlDbType را نیز صریحا ذکر کرد (بسته به نوع پارامترهای رویه‌ی ذخیره شده):
var nameParameter = new SqlParameter
{
  ParameterName = "@name",
  Value = "doc",
  Direction = ParameterDirection.Input,
  SqlDbType = SqlDbType.NVarChar
};


امکان ترکیب کوئری‌های SQL و LINQ نیز پیش بینی شده‌است

در کوئری ذیل، قسمت select از جدولی به صورت SQL و قسمت where و order by آن توسط LINQ تهیه شده‌اند که در نهایت به یک کوئری ترجمه شده و بر روی بانک اطلاعاتی اجرا می‌شوند.
یک مثال جالب آن، امکان کوئری گرفتن از Table Value Function‌ها و سپس ترکیب آن‌ها با LINQ است (این ترکیب، تنها یک کوئری SQL نهایی را تولید می‌کند):
var posts = context.Posts
  .FromSql("SELECT * FROM dbo.GetMatchingPostByTitle({0})", searchTerm)
  .Where(p => p.BlogId == 1)
  .OrderByDescending(p => p.CreateDate)
  .ToList();


واکشی ارتباطات یک موجودیت توسط SQL و LINQ

در ابتدای بحث در قسمت محدودیت‌های کوئری‌های SQL نوشته شده، ذکر شد «کوئری SQL نوشته شده نباید به همراه اطلاعات ارتباطات موجودیت‌ها باشد». برای رفع این محدودیت می‌توان از ترکیب SQL و LINQ به صورت ذیل استفاده کرد:
var searchTerm = ".NET";
var blogs = context.Blogs
  .FromSql("SELECT * FROM dbo.SearchBlogs {0}", searchTerm)
  .Include(b => b.Posts)
  .ToList();
در اینجا برای واکشی ارتباطات یک موجودیت از متد Include استفاده شده‌است.


اجرای عبارات SQL، بدون بازگشت مقداری

تا اینجا در مورد عبارات SQL از نوع Select و یا اجرای رویه‌های ذخیره شده، بحث شد. برای اجرای عبارات SQL ایی مانند update و delete می‌توان از متد ExecuteSqlCommand مربوط به  context.Database استفاده کرد:
  context.Database.ExecuteSqlCommand("UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 30");
و یا برای ارسال پارامترها به آن می‌توان به این صورت عمل کرد (اجرای یک رویه‌ی ذخیره شده با دو پارامتر ارسالی به آن):
context.Database.ExecuteSqlCommand("usp_CreateShipper @p0, @p1",
  parameters: new[] { "hello", "world" });


اجرای عبارات SQL و دریافت خروجی‌هایی به غیر از موجودیت‌های برنامه

در ابتدا بحث عنوان شد که محدودیت فعلی کوئری‌های FromSQL که می‌توانند خروجی را نیز ارائه دهند، مقید بودن آن‌ها به DbSet در حال استفاده است و محدود بودن آن‌ها به خواص کلاس متناظر تعریف شده. در این حالت اگر بخواهیم یک محاسبه‌ی عددی را بازگشت دهیم چه باید کرد؟
متد ExecuteSqlCommand تنها وضعیت نهایی اجرای عملیات را بازگشت می‌دهد و FromSQL مقید است به DbSet متناظر. برای رفع این محدودیت‌ها می‌توان مستقیما به DbConnection دسترسی یافت و سپس کوئری گرفت؛ به نحو ذیل:
using (var connection = context.Database.GetDbConnection())
{
    connection.Open();
 
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT COUNT(*) FROM Contacts";
        var result = command.ExecuteScalar().ToString();
    }
}
به عبارتی در اینجا امکان بازگشت به حالت ADO.NET خام نیز پیش بینی شده‌است.
مطالب
Blazor 5x - قسمت 27 - برنامه‌ی Blazor WASM - کار با سرویس‌های Web API
در قسمت‌های Blazor Server مثال این سری، با روش کار با سرویس‌های سمت سرور برنامه، آشنا شدیم. در این نوع برنامه‌ها، فقط کافی است اصل سرویس مدنظر را مستقیما در کامپوننت‌های Razor تزریق کرد و سپس می‌توان به نحو متداولی با آن‌ها کار کرد؛ اما در برنامه‌های Blazor WASM خیر! به این نوع برنامه‌های سمت کلاینت باید همانند برنامه‌های React ، Angular ، Vue و یا حتی برنامه‌های مبتنی بر jQuery نگاه کرد. در تمام فناوری‌های سمت کلاینت، این درخواست‌های Ajax ای هستند که با سرویس‌های یک Web API سمت سرور، ارتباط برقرار کرده، اطلاعاتی را به آن‌ها ارسال و یا دریافت می‌کنند. در برنامه‌های Blazor WASM نیز باید به همین ترتیب عمل کرد و در اینجا HttpClient دات نت، جایگزین برای مثال jQuery Ajax ، Fetch API و یا XMLHttpRequest استاندارد می‌شود (البته jQuery Ajax در اصل یک محصور کننده‌ی استاندارد XMLHttpRequest است که برای اولین بار توسط مایکروسافت در برنامه‌ی Outlook web access معرفی شد).


ایجاد سرویس سمت کلاینت دریافت اطلاعات اتاق‌ها از Web API

در قسمت 24، HotelRoomController را تکمیل کردیم که کار آن، بازگشت اطلاعات تمام اتاق‌ها و یا یک اتاق مشخص به کلاینت است. اکنون می‌خواهیم در ادامه‌ی قسمت قبل، اگر کاربری بر روی دکمه‌ی Go صفحه‌ی اول رزرو اتاقی کلیک کرد، لیست تمام اتاق‌های تعریف شده را به او نمایش دهیم. به همین جهت نیاز به سرویس سمت کلاینتی داریم که بتواند با این Web API endpoint کار کند:
namespace BlazorWasm.Client.Services
{
    public interface IClientHotelRoomService
    {
        public Task<IEnumerable<HotelRoomDTO>> GetHotelRoomsAsync(DateTime checkInDate, DateTime checkOutDate);
        public Task<HotelRoomDTO> GetHotelRoomDetailsAsync(int roomId, DateTime checkInDate, DateTime checkOutDate);
    }
}
این سرویس را در پوشه‌ی Services پروژه‌ی BlazorWasm.Client ایجاد کرده‌ایم که HotelRoomDTO خود را از پروژه‌ی BlazorServer.Models دریافت می‌کند. به این ترتیب می‌توان مدلی را بین یک Web API سمت سرور و یک سرویس سمت کلاینت، به اشتراک گذاشت. بنابراین پروژه‌ی کلاینت، باید ارجاعی را به پروژه‌ی BlazorServer.Models.csproj نیز داشته باشد.

در ادامه اینترفیس فوق را به صورت زیر پیاده سازی می‌کنیم:
namespace BlazorWasm.Client.Services
{
    public class ClientHotelRoomService : IClientHotelRoomService
    {
        private readonly HttpClient _httpClient;

        public ClientHotelRoomService(HttpClient httpClient)
        {
            _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        }

        public Task<HotelRoomDTO> GetHotelRoomDetailsAsync(int roomId, DateTime checkInDate, DateTime checkOutDate)
        {
            throw new NotImplementedException();
        }

        public Task<IEnumerable<HotelRoomDTO>> GetHotelRoomsAsync(DateTime checkInDate, DateTime checkOutDate)
        {
            // How to url-encode query-string parameters properly
            var uri = new UriBuilderExt(new Uri(_httpClient.BaseAddress, "/api/hotelroom"))
                            .AddParameter("checkInDate", $"{checkInDate:yyyy'-'MM'-'dd}")
                            .AddParameter("checkOutDate", $"{checkOutDate:yyyy'-'MM'-'dd}")
                            .Uri;
            return _httpClient.GetFromJsonAsync<IEnumerable<HotelRoomDTO>>(uri);
        }
    }
}
توضیحات:
- HttpClient یکی از سرویس‌های تنظیم شده‌ی در فایل Program.cs پروژه‌های سمت کلاینت است. بنابراین می‌توان آن‌را از طریق تزریق به سازنده‌ی این سرویس، به دست آورد.
- در اینجا برای دریافت اطلاعات JSON دریافتی از سمت سرور و سپس Deserialize خودکار آن به لیستی از DTO تعریف شده، از متد جدید GetFromJsonAsync استفاده شده‌است. این مورد جزو تازه‌های NET 5x. است.
- در اینجا استفاده از کلاس UriBuilderExt را نیز جهت تشکیل یک URL دارای کوئری استرینگ، مشاهده می‌کنید. هیچگاه نباید URL نهایی را از طریق جمع زدن اجزای آن به سمت سرور ارسال کرد؛ از این جهت که اجزای آن باید URL-encoded شوند؛ وگرنه در سمت سرور قابلیت پردازش نخواهند داشت. در ادامه تعریف کلاس جدید UriBuilderExt را مشاهده می‌کنید:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using BlazorServer.Models;
using BlazorWasm.Client.Utils;

using System;
using System.Collections.Specialized;
using System.Web;

namespace BlazorWasm.Client.Utils
{
    public class UriBuilderExt
    {
        private readonly NameValueCollection _collection;
        private readonly UriBuilder _builder;

        public UriBuilderExt(Uri uri)
        {
            _builder = new UriBuilder(uri);
            _collection = HttpUtility.ParseQueryString(string.Empty);
        }

        public UriBuilderExt AddParameter(string key, string value)
        {
            _collection.Add(key, value);
            return this;
        }

        public Uri Uri
        {
            get
            {
                _builder.Query = _collection.ToString();
                return _builder.Uri;
            }
        }
    }
}
- در اینجا توسط متد AddParameter، کار افزودن کوئری استرینگ‌ها به یک Url از پیش مشخص، انجام می‌شود. کار encoding نهایی به صورت خودکار توسط HttpUtility استاندارد دات نت، انجام خواهد شد.
- تاریخ‌های ارسالی به سمت سرور را با فرمت yyyy'-'MM'-'dd تبدیل رشته کردیم. این قالب، یکی از قالب‌های پذیرفته شده‌است.
- جهت سهولت استفاده‌ی از سرویس فوق و همچنین مدل‌های برنامه، فضای نام آن‌ها را به فایل BlazorWasm.Client\_Imports.razor اضافه می‌کنیم تا در تمام کامپوننت‌های برنامه‌ی سمت کلاینت، قابل دسترسی شوند:
@using BlazorWasm.Client.Services
@using BlazorServer.Models
- در آخر این سرویس جدید را باید به لیست سرویس‌های برنامه معرفی کرد تا قابلیت تزریق در کامپوننت‌ها را پیدا کند:
namespace BlazorWasm.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            // ...

            builder.Services.AddScoped<IClientHotelRoomService, ClientHotelRoomService>();

            // ...
        }
    }
}

چند اصلاح جزئی در کنترلرها و سرویس‌های سمت سرور

در Url نهایی فوق، دو پارامتر جدید checkInDate و checkOutDate هم وجود دارند. به همین جهت این دو را به اکشن متدهای کنترلر HotelRoom:
namespace BlazorWasm.WebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class HotelRoomController : ControllerBase
    {
        // ...

        [HttpGet]
        public async Task<IActionResult> GetHotelRooms(DateTime? checkInDate, DateTime? checkOutDate)
        {
          // ...
        }

        [HttpGet("{roomId}")]
        public async Task<IActionResult> GetHotelRoom(int? roomId, DateTime? checkInDate, DateTime? checkOutDate)
        {
           // ...
        }
    }
}
و همچنین سرویس سمت سرور IHotelRoomService نیز اضافه می‌کنیم:
namespace BlazorServer.Services
{
    public interface IHotelRoomService : IDisposable
    {
        Task<List<HotelRoomDTO>> GetAllHotelRoomsAsync(DateTime? checkInDate, DateTime? checkOutDate);
        Task<HotelRoomDTO> GetHotelRoomAsync(int roomId, DateTime? checkInDate, DateTime? checkOutDate);
        // ...
    }
}
البته فعلا پیاده سازی خاصی ندارند و آن‌ها را در قسمت‌های بعد مورد استفاده قرار خواهیم داد.


تنظیمات ویژه‌ی HttpClient برنامه‌ی سمت کلاینت

سرویس ClientHotelRoomService فوق، از HttpClient تزریق شده‌ی در سازنده‌ی خود استفاده می‌کند که BaseAddress خود را مطابق تنظیمات ابتدایی برنامه، از HostEnvironment دریافت می‌کند. در اینجا علاقمندیم تا بجای این تنظیم پیش‌فرض، فایل جدید appsettings.json را به پوشه‌ی BlazorWasm.Client\wwwroot\appsettings.json کلاینت اضافه کرده (محل قرارگیری آن در برنامه‌های سمت کلاینت، داخل پوشه‌ی wwwroot است و نه در داخل پوشه‌ی ریشه‌ی اصلی پروژه):
{
    "BaseAPIUrl": "https://localhost:5001/"
}
و از این تنظیم جدید به عنوان BaseAddress برنامه‌ی Web API استفاده کنیم که روش آن‌را در کدهای ذیل مشاهده می‌کنید:
namespace BlazorWasm.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            // ... 

            // builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
            builder.Services.AddScoped(sp => new HttpClient
            {
                BaseAddress = new Uri(builder.Configuration.GetValue<string>("BaseAPIUrl"))
            });

            // ... 
        }
    }
}

تکمیل کامپوننت دریافت لیست تمام اتاق‌ها

در قسمت قبل، کامپوننت خالی HotelRooms.razor را تعریف کردیم. کاربران پس از کلیک بر روی دکمه‌ی Go صفحه‌ی اول، به این کامپوننت هدایت می‌شوند. اکنون می‌خواهیم، لیست تمام اتاق‌ها را در این کامپوننت، از Web API برنامه دریافت کنیم:
@page "/hotel/rooms"

@inject ILocalStorageService LocalStorage
@inject IJSRuntime JsRuntime
@inject IClientHotelRoomService HotelRoomService

<h3>HotelRooms</h3>

@code {
    HomeVM HomeModel = new HomeVM();
    IEnumerable<HotelRoomDTO> Rooms = new List<HotelRoomDTO>();

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var model = await LocalStorage.GetItemAsync<HomeVM>(ConstantKeys.LocalInitialBooking);
            if (model is not null)
            {
                HomeModel = model;
            }
            else
            {
                HomeModel.NoOfNights = 1;
            }
            await LoadRooms();
        }
        catch (Exception e)
        {
            await JsRuntime.ToastrError(e.Message);
        }
    }

    private async Task LoadRooms()
    {
        Rooms = await HotelRoomService.GetHotelRoomsAsync(HomeModel.StartDate, HomeModel.EndDate);
    }
}
در اینجا در ابتدا سعی می‌شود تا HomeModel، از Local Storage که در قسمت قبل آن‌را تنظیم کردیم، خوانده شود. سپس با استفاده از متد GetHotelRoomsAsync، لیست اتاق‌ها را از Web API دریافت می‌کنیم. تمام این عملیات آغازین نیز باید در روال رویدادگران OnInitializedAsync صورت گیرند.


روش اجرای پروژه‌های Blazor WASM

تا اینجا اگر برنامه‌ی سمت کلاینت را توسط دستور dotnet watch run اجرا کنیم، هرچند صفحه‌ی خالی نمایش لیست اتاق‌ها ظاهر می‌شود، اما یک خطای fetch error را هم دریافت خواهیم کرد؛ چون نیاز است ابتدا پروژه‌ی Web API را اجرا کرد و سپس پروژه‌ی WASM را.
برای ساده سازی اجرای همزمان این دو پروژه، اگر از ویژوال استودیوی کامل استفاده می‌کنید، بر روی نام Solution کلیک راست کرده و از منوی ظاهر شده، گزینه‌ی «Set Startup projects» را انتخاب کنید. در صفحه دیالوگ ظاهر شده، گزینه‌ی «multiple startup projects» را انتخاب کرده و از لیست پروژه‌های موجود، دو پروژه‌ی Web API و WASM را انتخاب کنید و Action مقابل آن‌ها را به Start تنظیم کنید. در اینجا حتی می‌توان ترتیب اجرای این پروژه‌ها را هم تغییر داد. در این حالت زمانیکه بر روی دکمه‌ی Run، در ویژوال استودیو کلیک می‌کنید، هر دو پروژه را با هم برای شما اجرا خواهد کرد.

نکته‌ی مهم! در این حالت هم برنامه‌ی کلاینت نمی‌تواند با برنامه‌ی Web API ارتباط برقرار کند! چون شماره پورت iisExpress درج شده‌ی در فایل appsettings.json آن، باید به شماره sslPort مندرج در فایل Properties\launchSettings.json پروژه‌ی Web API تغییر داده شود که برای نمونه در اینجا این عدد 44314 است:
{
  "iisSettings": {
    "iisExpress": {
      "applicationUrl": "http://localhost:62930",
      "sslPort": 44314
    }
  }
}
و یا اگر می‌خواهید پروژه را از طریق NET Core CLI. با اجرای دستور dotnet watch run اجرا کنید ... به صورت پیش‌فرض نمی‌شود! چون برای اینکار باید به پوشه‌ی ریشه‌ی پروژه‌های Web API و WASM وارد شد و دوبار دستور یاد شده را به صورت مجزا اجرا کرد. در این حالت، هر دو پروژه، بر روی پورت پیش‌فرض 5001 اجرا می‌شوند. روش تغییر این پورت، مراجعه به فایل Properties\launchSettings.json این پروژه‌ها است. برای مثال همان پورت پیش‌فرض 5001 را که در فایل appsettings.json انتخاب کردیم، ثابت نگه می‌داریم. یعنی فایل launchSettings.json پروژه‌ی Web API را ویرایش نمی‌کنیم. اما این پورت را در پروژه‌ی کلاینت برای مثال به عدد 5002 تغییر می‌دهیم تا برنامه‌ی کلاینت، بر روی پورت پیش‌فرض برنامه‌ی Web API اجرا نشود:
{
    "BlazorWasm.Client": {
      "applicationUrl": "https://localhost:5002;http://localhost:5003",
    }  
}


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-27.zip
نظرات مطالب
ASP.NET MVC #20
مشکل حل شد.
دستور Install-Package Microsoft.AspNet.WebPages رو در کنسول زدم و سپس System.Web.Helpers.dll رو از پوشه bin لوکال به پوشه bin سرور بردم.
علت رو اما نفهمیدم! چون این فایل از اول هم توی bin لوکال نبود و بعد از اجرای این دستور اضافه شد، پس چطور قبلش صفحه بدرستی نمایش داده میشد؟
نظرات مطالب
بررسی متد های یک طرفه در WCF
می‌خواستم بدانم اگر مثلا به جای دستور  Thread.Sleep  در خواست در سمت سرور اجرای یک دستور روی دیتابیس باشد و به هر دلیلی ارتباط کلاینت قطع شود چگونه می‌توان ادامه کار سمت سرور را متوقف کرد.
طبق بررسی روی task manager کرده ام حافظه مصرف شده همچنان افزایش می‌یابد. 
بازخوردهای پروژه‌ها
خطا و راهنمایی
سلام جناب نصیری
در هنگام build پروژه چند خطا داشتم که نتونستم مشکلش رو برطرف کنم لطفا راهنمایی بفرمائید
اول اینکه این سه خطای زیر در حالتی رخ می‌دهد که dll مربوط در رفرنس هر کدام از پروژه‌ها add شده است
Error1The type or namespace name 'PdfReportSamples' could not be found (are you missing a using directive or an assembly reference?)...\pdfreport6797\Samples\WebAppTests\Default.aspx.cs37WebAppTests

Error2The type or namespace name 'PdfReportSamples' could not be found (are you missing a using directive or an assembly reference?)...\pdfreport26797\Samples\WpfAppTests\MainWindow.xaml.cs27WpfAppTests

Error3The type or namespace name 'PdfReportSamples' could not be found (are you missing a using directive or an assembly reference?)...\pdfreport26797\Samples\WindowsFormsAppTests\Form1.cs27WindowsFormsAppTests
خطای دوم اینکه فایل AppManifest.xml در سیلور پیدا نمیشه لذا مشکلی از بابت نبودش ایجاد نمیشه ؟ یک فایل مشابه میشه درست کرد ؟
خطای زیر رو هم متوجه نمیشم
Warning4The primary reference "...\pdfreport26797\Samples\PdfReportSamples\bin\Debug\PdfReportSamples.dll" could not be resolved because it has an indirect dependency on the framework assembly "System.Windows.Forms.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" which could not be resolved in the currently targeted framework. ".NETFramework,Version=v3.5". To resolve this problem, either remove the reference "...\pdfreport-26797\Samples\PdfReportSamples\bin\Debug\PdfReportSamples.dll" or retarget your application to a framework version which contains "System.Windows.Forms.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35".WindowsFormsAppTests
و در آخر یک سوال 
به نظر شما طراحی یک فیش بانکی با استفاده از این کتابخانه توصیه میشه یا استفاده از open office ؟
با تشکر
مطالب
Repository ها روی UnitOfWork ایده خوبی نیستند
در دنیای دات نت گرایشی برای تجزیه (abstract) کردن EF پشت الگوی Repository وجود دارد. این تمایل اساسا بد است و در ادامه سعی می‌کنم چرای آن را توضیح دهم.


پایه و اساس

عموما این باور وجود دارد که با استفاده از الگوی Repository می‌توانید (در مجموع) دسترسی به داده‌ها را از لایه دامنه (Domain) تفکیک کنید و "داده‌ها را بصورت سازگار و استوار عرضه کنید".

اگر به هر کدام از پیاده سازی‌های الگوی Repository در کنار (UnitOfWork (EF دقت کنید خواهید دید که تفکیک (decoupling) قابل ملاحظه ای وجود ندارد.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using ContosoUniversity.Models;

namespace ContosoUniversity.DAL
{
    public class StudentRepository : IStudentRepository, IDisposable
    {
        private SchoolContext context;

        public StudentRepository(SchoolContext context)
        {
            this.context = context;
        }

        public IEnumerable<Student> GetStudents()
        {
            return context.Students.ToList();
        }

        public Student GetStudentByID(int id)
        {
            return context.Students.Find(id);
        }

        //<snip>
        public void Save()
        {
            context.SaveChanges();
        }
    }
}

این کلاس بدون SchoolContext نمی‌تواند وجود داشته باشد، پس دقیقا چه چیزی را در اینجا decouple کردیم؟ هیچ چیز را!

در این قطعه کد - از MSDN - چیزی که داریم یک پیاده سازی مجدد از LINQ است که مشکل کلاسیک Repository API‌های بی انتها را بدست می‌دهد. منظور از Repository API‌های بی انتها، متدهای جالبی مانند GetStudentById, GetStudentByBirthday, GetStudentByOrderNumber و غیره است.

اما این مشکل اساسی نیست. مشکل اصلی روتین ()Save است. این متد یک دانش آموز (Student) را ذخیره می‌کند .. اینطور بنظر می‌رسد. دیگر چه چیزی را ذخیره می‌کند؟ آیا می‌توانید حدس بزنید؟ من که نمی‌توانم .. بیشتر در ادامه.


UnitOfWork تراکنشی است

یک UnitOfWork همانطور که از نامش بر می‌آید برای انجام کاری وجود دارد. این کار می‌تواند به سادگی واکشی اطلاعات و نمایش آنها، و یا به پیچیدگی پردازش یک سفارش جدید باشد. هنگامی که شما از EntityFramework استفاده می‌کنید و یک DbContext را وهله سازی می‌کنید، در واقع یک UnitOfWork می‌سازید.

در EF می‌توانید با فراخوانی ()SubmitChanges تمام تغییرات را فلاش کرده و بازنشانی کنید (flush and reset). این کار بیت‌های مقایسه change tracker را تغییر می‌دهد. افزودن رکوردهای جدید، بروز رسانی و حذف آنها. هر چیزی که تعیین کرده باشید. و تمام این دستورات در یک تراکنش یا Transaction انجام می‌شوند.


یک Repository مطلقا یک UnitOfWork نیست
هر متد در یک Repository قرار است فرمانی اتمی (Atomic) باشد - چه واکشی اطلاعات و چه ذخیره آنها. مثلا می‌توانید یک Repository داشته باشید با نام SalesRepository که اطلاعات کاتالوگ شما را واکشی می‌کند، و یا یک سفارش جدید را ثبت می‌کند. منظور از فرمان‌های اتمیک این است، که هر متد تنها یک دستور را باید اجرا کند. تراکنشی وجود ندارد و امکاناتی مانند ردیابی تغییرات و غیره هم جایی ندارند.

یکی دیگر از مشکلات استفاده از Repository‌ها این است که بزودی و به آسانی از کنترل خارج می‌شوند و نیاز به ارجاع دیگر مخازن پیدا می‌کنند. به دلیل اینکه مثلا نمی‌دانستید که SalesRepository نیاز به ارجاع ReportRepository داشته است (یا چیزی مانند این).

این مشکل به سرعت مشکل ساز می‌شود، و نیز به همین دلیل است که به UnitOfWork تمایل پیدا می‌کنیم.


بدترین کاری که می‌توانید انجام دهید: <Repository<T

این الگو دیوانه وار است. این کار عملا انتزاعی از یک انتزاع دیگر است (abstraction of an abstraction). به قطعه کد زیر دقت کنید، که به دلیلی نامشخص بسیار هم محبوب است.

public class CustomerRepository : Repository < Customer > {
  public CustomerRepository(DbContext context){
    //a property on the base class
    this.DB = context;
  }

  //base class has Add/Save/Remove/Get/Fetch
}

در نگاه اول شاید بگویید مشکل این کلاس چیست؟ همه چیز را کپسوله می‌کند و کلاس پایه Repository هم به کانتکست دسترسی دارد. پس مشکل کجاست؟

مشکلات عدیده اند .. بگذارید نگاهی بیاندازیم.

آیا می‌دانید این DbContext از کجا آمده است؟
خیر، نمی‌دانید. این آبجکت به کلاس تزریق (Inject) می‌شود، و نمی‌دانید که چه متدی آن را باز کرده و به چه دلیلی. ایده اصلی پشت الگوی Repository استفاده مجدد از کد است. بدین منظور که مثلا برای عملیات CRUD از کلاسی پایه استفاده کنید تا برای هر موجودیت و فرمی نیاز به کدنویسی مجدد نباشد. برگ برنده این الگو نیز دقیقا همین است. مثلا اگر بخواهید از کدی در چند فرم مختلف استفاده کنید از این الگو استفاده میشد.

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


شناسه مشتری جدید را نیاز داشتم
کد بالا در CustomerRepository را در نظر بگیرید - که یک مشتری جدید را به دیتابیس اضافه می‌کند. اما CustomerID جدید چه می‌شود؟ مثلا به این شناسه نیاز دارید تا یک log بسازید. چه می‌کنید؟ گزینه‌های شما اینها هستند:

  • متد ()SubmitChanges را صدا بزنید تا تغییرات ثبت شوند و بتوانید به CustomerID جدید دسترسی پیدا کنید
  • CustomerRepository خود را باز کنید و متد پایه Add را بازنویسی (override) کنید. بدین منظور که پیش از بازگشت دادن، متد ()SubmitChanges را فراخوانی کند. این راه حلی است که MSDN به آن تشویق می‌کند، و بمبی ساعتی است که در انتظار انفجار است
  • تصمیم بگیرید که تمام متدهای Add/Remove/Save در مخازن شما باید ()SubmitChanges را فراخوانی کنند

مشکل را می‌بینید؟ مشکل در خود پیاده سازی است. در نظر بگیرید که چرا New Customer ID را نیاز دارید؟ احتمالا برای استفاده از آن در ثبت یک سفارش جدید، و یا ثبت یک ActivityLog.

اگر بخواهیم از StudentRepository بالا برای ایجاد دانش آموزان جدید پس از خرید آنها از فروشگاه کتاب مان استفاده کنیم چه؟ اگر DbContext خود را به مخزن تزریق کنید و دانش آموز جدید را ذخیره کنید .. اوه .. تمام تراکنش شما فلاش شده و از بین رفته!

حالا گزینه‌های شما اینها هستند: 1) از StudentRepository استفاده نکنید (از OrderRepository یا چیز دیگری استفاده کنید). و یا 2) فراخوانی ()SubmitChanges را حذف کنید و به باگ‌های متعددی اجازه ورود به کد تان را بدهید.

اگر تصمیم بگیرید که از StudentRepository استفاده نکنید، حالا کدهای تکراری (duplicate) خواهید داشت.

شاید بگویید که برای دستیابی به شناسه رکورد جدید نیازی به ()SubmitChanges نیست، چرا که خود EF این عملیات را در قالب یک تراکنش انجام می‌دهد!

دقیقا درست است، و نکته من نیز همین است. در ادامه به این قسمت باز خواهیم گشت.

متدهای Repositories قرار است اتمیک باشند

به هر حال تئوری اش که چنین است. چیزی که در Repository‌ها داریم حتی اصلا Repository هم نیست. بلکه یک abstraction برای عملیات CRUD است که هیچ کاری مربوط به منطق تجاری اپلیکیشن را هم انجام نمی‌دهد. مخازن قرار است روی دستورات مشخصی تمرکز کنند (مثلا ثبت یک رکورد یا واکشی لیستی از اطلاعات)، اما این مثال‌ها چنین نیستند.

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

خوب، راه حل چیست؟

برای جلوگیری از این abstraction‌های غیر منطقی دو راه وجود دارد. اولین راه استفاده از Command/Query Separation است که ممکن است در ابتدا کمی عجیب و بنظر برسند اما لازم نیست کاملا CQRS را دنبال کنید. تنها از سادگی انجام کاری که مورد نیاز است لذت ببرید، و نه بیشتر.

آبجکت‌های Command/Query

Jimmy Bogard مطلب خوبی در اینباره نوشته است و با تغییراتی جزئی برای بکارگیری Properties کدی مانند لیست زیر خواهیم داشت. مثلا برای مطالعه بیشتر درباره آبجکت‌های Command/Query به این لینک سری بزنید.

public class TransactOrderCommand {
  public Customer NewCustomer {get;set;}
  public Customer ExistingCustomer {get;set;}
  public List<Product> Cart {get;set;}
  //all the parameters we need, as properties...
  //...

  //our UnitOfWork
  StoreContext _context;
  public TransactOrderCommand(StoreContext context){
    //allow it to be injected - though that's only for testing
    _context = context;
  }

  public Order Execute(){
    //allow for mocking and passing in... otherwise new it up
    _context = _context ?? new StoreContext();

    //add products to a new order, assign the customer, etc
    //then...
    _context.SubmitChanges();

    return newOrder;
  }
}
همین کار را با یک آبجکت Query نیز می‌توانید انجام دهید. می‌توانید پست Jimmy را بیشتر مطالعه کنید، اما ایده اصلی این است که آبجکت‌های Query و Command برای دلیل مشخصی وجود دارند. می‌توانید آبجکت‌ها را در صورت نیاز تغییر دهید و یا mock کنید.


DataContext خود را در آغوش بگیرید

ایده ای که در ادامه خواهید دید را شخصا بسیار می‌پسندم (که توسط Ayende معرفی شد). چیزهایی که به آنها نیاز دارید را در قالب یک فیلتر wrap کنید و یا از یک کلاس کنترلر پایه استفاده کنید (با این فرض که از اپلیکیشن‌های وب استفاده می‌کنید).

using System;
using System.Web.Mvc;

namespace Web.Controllers
{
  public class DataController : Controller
  {
    protected StoreContext _context;

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      //make sure your DB context is globally accessible
      MyApp.StoreDB = new StoreDB();
    }

    protected override void OnActionExecuted(ActionExecutedContext filterContext)
    {
      MyApp.StoreDB.SubmitChanges();
    }
  }
}

این کار به شما اجازه می‌دهد که از DataContext خود در خلال یک درخواست واحد (request) استفاده کنید. تنها کاری که باید بکنید این است که از این کلاس پایه ارث بری کنید. این بدین معنا است که هر درخواست به اپلیکیشن شما یک UnitOfWork خواهد بود. که بسیار هم منطقی و قابل قبول است. در برخی موارد هم شاید این فرض درست یا کارآمد نباشد، که در این هنگام می‌توانید از آبجکت‌های Command/Query استفاده کنید.


ایده‌های بعدی: چه چیزی بدست آوردیم؟

چیزهای متعددی بدست آوردیم.

  • تراکنش‌های روشن و صریح: دقیقا می‌دانیم که DbContext ما از کجا آمده و در هر مرحله روی چه UnitOfWork ای کار می‌کنیم. این امر هم الان، و هم در آینده بسیار مفید خواهد بود
  • انتزاع کمتر == شفافیت بیشتر: ما Repository‌ها را از دست دادیم، که دلیلی برای وجود داشتن نداشتند. به جز اینکه یک abstraction از abstraction دیگر باشند. رویکرد آبجکت‌های Command/Query تمیز‌تر است و دلیل وجود هرکدام و مسئولیت آنها نیز روشن‌تر است
  • شانس کمتر برای باگ ها: رویکردهای مبتنی بر Repository باعث می‌شوند که با تراکنش‌های ناموفق یا پاره ای (partially-executed) مواجه شویم که نهایتا به یکپارچگی و صحت داده‌ها صدمه می‌زند. لازم به ذکر نیست که خطایابی و رفع چنین مشکلاتی شدیدا زمان بر و دردسر ساز است

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

ایجاد Repositories بر روی UnitOfWork
به الگوی Repository در لایه DAL خود نه بگویید!
پیاده سازی generic repository یک ضد الگو است 
نگاهی به generic repositories
بدون معکوس سازی وابستگی‌ها، طراحی چند لایه شما ایراد دارد  

مطالب
واکشی اطلاعات سرویس Web Api با استفاده از TypeScript و AngularJs
در پست‌های قبلی با TypeScript، AngularJs و Web Api آشنا شدید. در این پست قصد دارم از ترکیب این موارد برای پیاده سازی عملیات واکشی اطلاعات سرویس Web Api در قالب یک پروژه استفاده نمایم. برای شروع ابتدا یک پروژه Asp.Net MVC ایجاد کنید.
در قسمت مدل ابتدا یک کلاس پایه برای مدل ایجاد خواهیم کرد:
public abstract class Entity
    {
        public Guid Id { get; set; }
    }
حال کلاسی به نام Book ایجاد می‌کنیم:
public class Book : EntityBase
    {
        public string Name { get; set; }
        public decimal Author { get; set; }
    }
در پوشه مدل یک کلاسی به نام BookRepository ایجاد کنید و کد‌های زیر را در آن کپی نمایید(به جای پیاده سازی بر روی بانک اطلاعاتی، عملیات بر روی لیست درون حافظه انجام می‌گیرد):
 public class BookRepository
    {
        private readonly ConcurrentDictionary<Guid, Book> result = new ConcurrentDictionary<Guid, Book>();

        public IQueryable<Book> GetAll()
        {
            return result.Values.AsQueryable();
        }        

        public Book Add(Book entity)
        {
            if (entity.Id == Guid.Empty) entity.Id = Guid.NewGuid();

            if (result.ContainsKey(entity.Id)) return null;

            if (!result.TryAdd(entity.Id, entity)) return null;

            return entity;
        }     
    }

نوبت به کلاس کنترلر می‌رسد. یک کنترلر Api به نام BooksController ایجاد کنید و سپس کد‌های زیر را در آن کپی نمایید:
 public class BooksController : ApiController
    {
        public static BookRepository repository = new BookRepository();       

public BooksController()
        {
            repository.Add(new Book 
            {
                Id=Guid.NewGuid(),
                Name="C#",
                Author="Masoud Pakdel"
            });

            repository.Add(new Book
            {
                Id = Guid.NewGuid(),
                Name = "F#",
                Author = "Masoud Pakdel"
            });

            repository.Add(new Book
            {
                Id = Guid.NewGuid(),
                Name = "TypeScript",
                Author = "Masoud Pakdel"
            });
        }

        public IEnumerable<Book> Get()
        {
            return repository.GetAll().ToArray();
        }          
    }

در این کنترلر، اکشنی به نام Get داریم که در آن اطلاعات کتاب‌ها از Repository مربوطه برگشت داده خواهد شد. در سازنده این کنترلر ابتدا سه کتاب به صورت پیش فرض اضافه می‌شود و انتظار داریم که بعد از اجرای برنامه، لیست مورد نظر را مشاهده نماییم.

حال نویت به عملیات سمت کلاینت میرسد. برای استفاده از قابلیت‌های TypeScript و AngularJs در Vs.Net از این مقاله کمک بگیرید. بعد از آماده سازی در فولدر script، پوشه ای به نام app می‌سازیم و یک فایل TypeScript به نام  BookModel  در آن ایجاد می‌کنیم:
module Model {
    export class Book{
        Id: string;
        Name: string;
        Author: string;
    }
}
واضح است که ماژولی به نام Model داریم که در آن کلاسی به نام Book ایجاد شده است. برای انتقال اطلاعات از طریق سرویس http$ در Angular نیاز به سریالایز کردن این کلاس به فرمت Json خواهیم داشت. قصد داریم View مورد نظر را به صورت زیر ایجاد نماییم:
 <div ng-controller="Books.Controller">       
        <table class="table table-striped table-hover" style="width: 500px;">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Author</th>              
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="book in books">
                    <td>{{book.Name}}</td>
                    <td>{{book.Author}}</td>                                     
                </tr>
            </tbody>
        </table>
    </div>

توضیح کد‌های بالا:
ابتدا یک کنترلری که به نام Controller که در ماژولی به نام Book تعریف شده است باید ایجاد شود. اطلاعات تمام کتب ثبت شده باید از سرویس مورد نظر دریافت و با یک ng-repeat در جدول نمایش داده خواهند شود.
در پوشه app یک فایل TypeScript دیگر برای تعریف برخی نیازمندی‌ها به نام  AngularModule ایجاد می‌کنیم که کد آن به صورت زیر خواهد بود:
declare module AngularModule {
    export interface HttpPromise {
        success(callback: Function) : HttpPromise;       
    }
    export interface Http {
        get(url: string): HttpPromise;   
    }
}
در این ماژول دو اینترفیس تعریف شده است. اولی به نام HttpPromise است که تابعی به نام success  دارد. این تابع باید بعد از موفقیت آمیز بودن  عملیات فراخوانی شود. ورودی آن از نوع Function است. بعنی اجازه تعریف یک تابع را به عنوان ورودی برای این توابع دارید.
در اینترفیس Http نیز تابعی به نام get تعریف شده  است که  برای دریافت اطلاعات از سرویس api، مورد استفاده قرار خواهد گرفت. از آن جا که تعریف توابع در اینترفیس فاقد بدنه است در نتیجه این جا فقط امضای توابع مشخص خواهد شد. پیاده سازی توابع به عهده کنترلر‌ها خواهد بود:
مرحله بعد مربوط است به تعریف کنترلری  به نام BookController تا اینترفیس بالا را پیاده سازی نماید. کد‌های آن به صورت زیر خواهد بود:
/// <reference path='AngularModule.ts' />
/// <reference path='BookModel.ts' />

module Books {
    export interface Scope {        
        books: Model.Book[];
    }

    export class Controller {
        private httpService: any;

        constructor($scope: Scope, $http: any) {
            this.httpService = $http;

            this.getAllBooks(function (data) {
                $scope.books = data;
            });
            var controller = this;
    }

        getAllBooks(successCallback: Function): void {
            this.httpService.get('/api/books').success(function (data, status) {
                successCallback(data);
            });
        }
    }
}


توضیح کد‌های بالا:
برای دسترسی به تعاریف انجام شده در سایر ماژول‌ها باید ارجاعی به فایل تعاریف ماژول‌های مورد نظر داشته باشیم. در غیر این صورت هنگام استفاده از این ماژول‌ها با خطای کامپایلری روبرو خواهیم شد. عملیات ارجاع به صورت زیر است:
/// <reference path='AngularModule.ts' />
/// <reference path='BookModel.ts' />
در پست قبلی توضیح داده شد که برای مقید سازی عناصر بهتر است یک اینترفیس به نام Scope تعریف کنیم تا بتوانیم متغیر‌های مورد نظر برای مقید سازی را در آن تعریف نماییم در این جا تعریف آن به صورت زیر است:
export interface Scope {  
        books: Model.Book[];      
    }
در این جا فقط نیاز به لیستی از کتاب‌ها داریم تا بتوان در جدول مورد نظر در View آنرا پیمایش کرد. تابعی به نام getAllBooks در کنترلر مورد نظر نوشته شده است که ورودی آن یک تابع خواهد بود که باید بعد از واکشی اطلاعات از سرویس، فراخوانی شود. اگر به کد‌های بالا دقت کنید می‌بینید که در ابتدا سازنده کنترلر،سرویس http$ موجود در Angular به متغیری به نام httpService نسبت داده می‌شود. با فراخوانی تابع get و ارسال آدرس سرویس که با توجه به مقدار مسیر یابی پیش فرض کلاس WebApiConfig باید با api شروع شود به راحتی اطلاعات مورد نظر به دست خواهد آمد. بعد از واکشی در صورت موفقیت آمیز بودن عملیات تابع success اجرا می‌شود که نتیجه آن انتساب مقدار به دست آمده به متغیر books تعریف شده در scope$ می‌باشد.

در نهایت خروجی به صورت زیر خواهد بود:


سورس پیاده سازی مثال بالا در Visual Studio 2013
مطالب
خروجی گرفتن از برنامه‌های NET Core 3. بدون وابستگی به فریم‌ورک و در یک فایل Exe
دیشب نسخه 6 پیش‌نمایش دات نت کور 3، منتشر شد و ویژگی‌های بسیار خوبی را ارائه کرد و بهانه‌ای شد تا با برخی از ویژگی‌ها مخصوص خروجی گرفتن از برنامه در دات نت کور 3 آشنا بشویم.

PublishSingleFile
توسط این دستور میتوانید برنامه خودتان را همراه با تمام اسمبلی‌ها و فایل‌ها، در یک فایل Exe قرار دهید و اجرا کنید. برای بار اول اجرا ممکن است چند ثانیه طول بکشد ولی از دفعات بعد، تاخیری در اجرا نخواهیم داشت. در واقع این روش تمام فایل‌ها را فشرده کرده و با اجرا، در مسیر مشخصی آنپک می‌کند.
روش استفاده از آن به این صورت هست که پنجره CMD را در کنار فایل پروژه باز کنید و دستور زیر را اجرا کنید: 
 dotnet publish -r win10-x64 /p:PublishSingleFile=true

ReadyToRun 
 توسط این دستور میتوانید سرعت اجرای برنامه‌های دات نت کور را شدیدا بهبود بخشید. شما میتوانید با کامپایل کردن اسمبلی‌های برنامه به فرمت R2R، علاوه بر کاهش زمان اجرای برنامه، میزان استفاده از حافظه را هم کاهش دهید. البته حجم برنامه‌ی خروجی در این روش بیشتر است؛ چون هم شامل کدهای IL برنامه هست و هم شامل همان کدها بصورت Native تا بتواند اجرای برنامه را سرعت ببخشد.
نتایج زیر، بر روی پروژه‌ی ساده‌ی WPF اجرا شده 
 With ReadyToRun images  IL-only Application
 Startup time: 1.3 seconds   Startup time: 1.9 seconds 
 Memory usage: 55.7 MB   Memory usage: 69.1 MB 
 Application size: 156 MB   Application size: 150 MB 

برای فعالسازی آن، تگ زیر را به فایل پروژه اضافه کنید:
  <PublishReadyToRun>true</PublishReadyToRun>
و سپس با دستور زیر در cmd اقدام به گرفتن خروجی کنید:
 dotnet publish -r win-x64 -c Release
  در حال حاضر این روش فقط برای حالت Self-Contained کار می‌کند و در نسخه‌ی بعدی برای FramworkDependent هم فعال می‌شود.

Assembly linking
توسط این ابزار میتوانید حجم و سایز برنامه را کاهش دهید. روش کار به این صورت است که کدهای IL را اسکن می‌کند و فقط اسمبلی‌های استفاده شده در برنامه را در کنار برنامه قرار میدهد که باعث کاهش حجم برنامه می‌شود.
برای فعالسازی آن، تگ زیر را به فایل پروژه اضافه کنید
  <PublishTrimmed>true</PublishTrimmed>
و سپس با دستور زیر در cmd اقدام به گرفتن خروجی کنید:
 dotnet publish -r win-x64 -c Release
در یک پروژه‌ی ساده‌ی WPF، حجم برنامه از 68 مگابایت، به 28 مگابایت کاهش پیدا کرد.

نکته: شما میتوانید از ایمیج‌های ReadyToRun و Linker همزمان استفاده کنید. اولی حجم برنامه را افزایش میدهد و دومی کاهش! و این در حال حاضر باعث ایجاد مشکلاتی می‌شود که قرار هست در نسخه‌ی بعدی، یعنی پیش‌نمایش 7، رفع بشود. در حال حاضر بهتر است از هر دو دستور باهم استفاده نکنید.