بازخوردهای دوره
لغو Lazy Loading در حین کار با AutoMapper و Entity Framework
سلام؛ ما در پروژه هامون معمولا کار Object Mapping رو توی لایه Repository انجام نمیدیم و در لایه Service کار تبدیل Model به ViewModel رو انجام میدیم ..  
var jobViewModel = Mapper.Map<Job>(jobViewModel);
به نظرتون استفاده مستقیم از متد ProjectTo در AutoMapper در لایه ای مثل Repository کار درستیه ؟ 
مطالب دوره‌ها
ساخت یک Mini ORM با AutoMapper
Mini ORM‌ها برخلاف ORMهای کاملی مانند Entity framework یا NHibernate، کوئری‌های LINQ را تبدیل به SQL نمی‌کنند. در اینجا کار با SQL نویسی مستقیم شروع می‌شود و مهم‌ترین کار این کتابخانه‌ها، نگاشت نتیجه‌ی دریافتی از بانک اطلاعاتی به اشیاء دات نتی هستند. خوب ... AutoMapper هم دقیقا همین کار را انجام می‌دهد! بنابراین در ادامه قصد داریم یک Mini ORM را به کمک AutoMapper طراحی کنیم.


کلاس پایه AdoMapper

public abstract class AdoMapper<T> where T : class
{
    private readonly SqlConnection _connection;
 
    protected AdoMapper(string connectionString)
    {
        _connection = new SqlConnection(connectionString);
    }
 
    protected virtual IEnumerable<T> ExecuteCommand(SqlCommand command)
    {
        command.Connection = _connection;
        command.CommandType = CommandType.StoredProcedure;
        _connection.Open();
 
        try
        {
            var reader = command.ExecuteReader();
            try
            {
                return Mapper.Map<IDataReader, IEnumerable<T>>(reader);
            }
            finally
            {
                reader.Close();
            }
        }
        finally
        {
            _connection.Close();
        }
    }
 
    protected virtual T GetRecord(SqlCommand command)
    {
        command.Connection = _connection;
        _connection.Open();
        try
        {
            var reader = command.ExecuteReader();
            try
            {
                reader.Read();
                return Mapper.Map<IDataReader, T>(reader);
            }
            finally
            {
                reader.Close();
            }
        }
        finally
        {
            _connection.Close();
        }
    }
 
    protected virtual IEnumerable<T> GetRecords(SqlCommand command)
    {
        command.Connection = _connection;
        _connection.Open();
        try
        {
            var reader = command.ExecuteReader();
            try
            {
                return Mapper.Map<IDataReader, IEnumerable<T>>(reader);
            }
            finally
            {
                reader.Close();
            }
        }
        finally
        {
            _connection.Close();
        }
    }
}
در اینجا کلاس پایه Mini ORM طراحی شده را ملاحظه می‌کنید. برای نمونه قسمت GetRecords آن مانند مباحث استاندارد ADO.NET است. فقط کار خواندن و همچنین نگاشت رکوردهای دریافت شده از بانک اطلاعاتی به شیء‌ایی از نوع T توسط AutoMapper انجام خواهد شد.


نحوه‌ی استفاده از کلاس پایه AdoMapper

در کدهای ذیل نحوه‌ی ارث بری از کلاس پایه AdoMapper و سپس استفاده از متدهای آن‌را ملاحظه می‌کنید:
public class UsersService : AdoMapper<User>, IUsersService
{
    public UsersService(string connectionString)
        : base(connectionString)
    {
    }
 
    public IEnumerable<User> GetAll()
    {
        using (var command = new SqlCommand("SELECT * FROM Users"))
        {
            return GetRecords(command);
        }
    }
 
    public User GetById(int id)
    {
        using (var command = new SqlCommand("SELECT * FROM Users WHERE Id = @id"))
        {
            command.Parameters.Add(new SqlParameter("id", id));
            return GetRecord(command);
        }
    }
}
در این مثال نحوه‌ی تعریف کوئری‌های پارامتری نیز در متد GetById به نحو متداولی مشخص شده‌است. کار نگاشت حاصل این کوئری‌ها به اشیاء دات نتی را AutoMapper انجام خواهد داد. نحوه‌ی کار نیز، نگاشت فیلد f1 به خاصیت f1 است (هم نام‌ها به هم نگاشت می‌شوند).


تعریف پروفایل مخصوص AutoMapper

ORMهای تمام عیار، کار نگاشت فیلدهای بانک اطلاعاتی را به خواص اشیاء دات نتی، به صورت خودکار انجام می‌دهند. در اینجا همانند روش‌های متداول کار با AutoMapper نیاز است این نگاشت را به صورت دستی یکبار تعریف کرد:
public class UsersProfile : Profile
{
    protected override void Configure()
    {
        this.CreateMap<IDataRecord, User>();
    }
 
    public override string ProfileName
    {
        get { return this.GetType().Name; }
    }
}
و سپس در ابتدای برنامه آن‌را به AutoMapper معرفی نمود:
Mapper.Initialize(cfg => // In Application_Start()
{
    cfg.AddProfile<UsersProfile>();
});


سفارشی سازی نگاشت‌های AutoMapper

فرض کنید کلاس Advertisement زیر، معادل است با جدول Advertisements بانک اطلاعاتی؛ با این تفاوت که در کلاس تعریف شده، خاصیت TitleWithOtherName تطابقی با هیچکدام از فیلدهای بانک اطلاعاتی ندارد. بنابراین اطلاعاتی نیز به آن نگاشت نخواهد شد.
public class Advertisement
{
    public int Id { set; get; }
    public string Title { get; set; }
    public string Description { get; set; }
    public int UserId { get; set; }
 
    public string TitleWithOtherName { get; set; }
}
برای رفع این مشکل می‌توان حین تعریف پروفایل مخصوص Advertisement، آن‌را سفارشی سازی نیز نمود:
public class AdvertisementsProfile : Profile
{
    protected override void Configure()
    {
        this.CreateMap<IDataRecord, Advertisement>()
            .ForMember(dest => dest.TitleWithOtherName,
                       options => options.MapFrom(src =>
                            src.GetString(src.GetOrdinal("Title"))));
    }
 
    public override string ProfileName
    {
        get { return this.GetType().Name; }
    }
}
در اینجا پس از تعریف نگاشت مخصوص کار با IDataRecordها، عنوان شده‌است که هر زمانیکه به خاصیت TitleWithOtherName رسیدی، مقدارش را از فیلد Title دریافت و جایگزین کن.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید.
اشتراک‌ها
کارآیی JavaScript بر روی گوشی‌های اندرویدی، 7 برابر کمتر از نمونه‌ی iOS ای است

This is just terrible for the web. When there's a 7x difference between the JS performance on a BRAND NEW PIXEL and a new iPhone, web app makers just have to approach the whole game differentially. I mean, the Pixel 5 is FIVE YEARS behind the performance game. 

کارآیی JavaScript بر روی گوشی‌های اندرویدی، 7 برابر کمتر از نمونه‌ی iOS ای است
مطالب
نگاشت اشیاء در AutoMapper توسط Attribute ها #1
نگاشت اشیاء امری مفید و لذت بخش است. ولی بخاطر تنظیمات خاص آن و افزایش کدها، همیشه کمی دردسر ساز بوده است. استفاده از کلاس Profile راه کار مناسبی است؛ اما در این حالت کلاس مقصد (ViewModel) از تنظیمات نگاشت‌ها بی اطلاع می‌ماند و فقط حاوی داده خواهد بود. برای ادغام کلاس و تنظیمات نگاشت در اینجا راهکاری ارائه گردید که در ادامه و با الگو گیری از همین ایده، اقدام به ارائه‌ی روشی جدید می‌کنم که با استفاده از Attribute‌ها تنظیمات نگاشت اشیاء را در AutoMapper انجام می‌دهد.
در نهایت می‌خواهیم نگاشت‌ها را اینچنین تنظیم کنیم:
 [MapFrom(typeof (Student), ignoreAllNonExistingProperty: true, alsoCopyMetadata: true)]
 public class AdminStudentViewModel
 {
     // [IgnoreMap]
     public int Id { set; get; }

     [MapForMember("Name")]
     public string FirstName { set; get; }

     [MapForMember("Family")]
     public string LastName { set; get; }

     public string Email { set; get; }

     [MapForMember("RegisterDateTime")]
     public string RegisterDateTimePersian { set; get; }

     [UseValueResolver(typeof (BookCountValueResolver))]
     public int BookCounts { set; get; }

     [UseValueResolver(typeof (BookPriceValueResolver))]
     public decimal BookPrice { set; get; }
 };
  این سبک تنظیم کردن نگاشت‌های اشیاء به نظر بهتر از روش‌های دیگر است؛ چون کلاس‌های ویوومدل را معنادار کرده و همچنین برای برنامه نویسان EF و ASP.NET MVC استفاده‌ی از ویژگی‌ها، یک شیوه‌ی کاری معمول به حساب می‌آید. 
به تعریف و توضیح صفت‌های (ویژگی‌ها یا Attributes) مورد نیاز می‌پردازم:

صفت MapFromAttribute

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MapFromAttribute : Attribute
{
    public Type SourceType { get; private set; }
    public bool IgnoreAllNonExistingProperty { get; private set; }
    public bool AlsoCopyMetadata { get; private set; }    //Go to: https://www.dntips.ir/courses/topic/16/cb36bc2e-4263-431e-86a5-236322cb5576

    public MapFromAttribute(Type sourceType, bool ignoreAllNonExistingProperty = false,
        bool alsoCopyMetadata = false)
    {
        SourceType = sourceType;
        IgnoreAllNonExistingProperty = ignoreAllNonExistingProperty;
        AlsoCopyMetadata = alsoCopyMetadata;
    }
};
این صفت روی کلاس‌ها می‌نشیند و توسط آرگومان sourceType آن، نوع مبدأ را برای automapper مشخص می‌کند. در واقع همه چیز از اینجا شروع می‌شود. همچنین آرگومان ignoreAllNonExistingProperty مشخص می‌کند کلیه‌ی صفاتی که در مقصد هستند ولی معادل اسمی در مبدأ ندارند، بصورت خودکار رد (Ignore) شده و از آن‌ها صرف نظر شود تا از شکست متد AutoMapper.Mapper.AssertConfigurationIsValid جلوگیری کند (پرداخته شده در اینجا). آرگومان alsoCopyMetadata پیاده سازی نمی‌شود؛ ولی می‌تواند پرچمی باشد تا اجازه دهد  Data Annotations از مدل‌های ef به ViewModel انتقال یابند.


صفت IgnoreMapAttribute
[AttributeUsage(AttributeTargets.Property)]
public class IgnoreMapAttribute : Attribute {};
از این صفت برای رد کردن خصیصه‌‌ای در نگاشت‌ها استفاده می‌کنیم. لازم به ذکر است که صفتی مشابه در Automapper.IgnoreAttribute وجود دارد که می‌تواند به جای این صفت مورد استفاده قرار گیرد. «نگارنده جهت همخوانی با سایر صفات، اقدام به استفاده‌ی از این صفت می‌کند»


صفت MapForMemberAttribute
[AttributeUsage(AttributeTargets.Property)]
public class MapForMemberAttribute : Attribute
{
    public string MemberToMap { get; private set; }
    public MapForMemberAttribute(string memberToMap)
    {
        MemberToMap = memberToMap;
    }
};
اگر نام خصیصه‌ها در مبدأ و مقصد یکی نباشند، از این صفت برای همگام سازی این دو استفاده می‌کنیم.


صفت UseValueResolverAttribute
[AttributeUsage(AttributeTargets.Property)]
public class UseValueResolverAttribute : Attribute
{
    public IValueResolver ValueResolver { get; private set; }
    public UseValueResolverAttribute(Type valueResolver)
    {
        ValueResolver = valueResolver.GetConstructors()[0].Invoke(new object[] {}) as IValueResolver;
    }
};
استفاده از ValueResolver‌ها در اینجا ذکر شده است. از این صفت برای تنظیم این مقدار برای یک خصیصه استفاده می‌شود. برای مثال فیلد FullName را در مقصد درنظر بگیرد که از دو فیلد Name و Family در مبدأ تشکیل می‌شود.

تا اینجا صفات پیش نیاز کار فراهم شدند. حال باید این صفت‌ها را به نگاشت متناسبی در automapper تبدیل کنیم.
دریافت کدها
ادامه دارد...
اشتراک‌ها
بررسی معماری Vertical Slice در ASP.NET Core

In this article, we are going to learn about Vertical Slice Architecture in ASP.NET Core and the benefits it provides to make our code more maintainable. We’re also going to learn how it differs from traditional architecture and its advantages over the traditional approach. 

بررسی معماری Vertical Slice در ASP.NET Core
اشتراک‌ها
Git for Windows 2.19.0 منتشر شد

New Features

Comes with Git v2.19.0.
There are now fast, built-in versions of git stash and git rebase, available as experimental options.
The included OpenSSH client now enables modern ciphers.
The gitweb component was removed because it is highly unlikely to be used on Windows.
The git archimport tool (which was probably used by exactly 0 users) is no longer included in Git for Windows. 

Git for Windows 2.19.0 منتشر شد