نظرات مطالب
پیاده سازی authorization به روش AOP به کمک کتابخانه های SNAP و StructureMap
۱- متد IsActionAuthorized نام کامل متدی که قرار است اجرا شود را به عنوان پارامتر گرفته و در دیتابیس (در این پیاده سازی به وسیله‌ی EntityFramework) چک میکند که کاربری که Id اش در  AuthManager. AuditUserId است (یعنی کاربری که درخواست اجرای متد را داده است) اجازه اجرای این متد را دارد یا نه. بسته به نیازمندی برنامه شما این دسترسی میتواند به طور ساده فقط مستقیما برای کاربر ثبت شود و یا  ترکیبی از دسترسی خود کاربر و دسترسی گروه هایی که این کاربر در آن عضویت دارد باشد.

۲-  EFAuthorizationManager  کلاس ساده ایست
namespace Framework.ServiceLayer.UserManager
{
    public class EFAuthorizationManager : IAuthorizationManager
    {
        public String AuditUserId { get; set; }
        IUnitOfWork _uow;

        public EFAuthorizationManager(IUnitOfWork uow)
        {
            _uow = uow;
        }

        public bool IsActionAuthorized(string actionName)
        {
            var res = _uow.Set<User>()
            .Any(u => u.Id == AuditUserId && 
                    u.AllowedActions.Any(a => a.Name == actionName));
            return res;
        }

        public bool IsPageAuthorized(string pageURL)
        {
            //TODO: بررسی وجود دسترسی باید پیاده سازی شود
            //فقط برای تست
            return true;
        }
    }
}
:خلاصه ای از کلاسهای مدل مرتبط را هم در زیر مشاهده می‌کنید
namespace Framework.DataModel
{
    public class User : BaseEntity
    {
        public string UserName { get; set; }
        public string Password { get; set; }

        //...

        [Display(Name = "عملیات مجاز")]
        public virtual ICollection<Action> AllowedActions { get; set; }
    }

    public class Action:BaseEntity
    {
        public string Name { get; set; }
        public Entity RelatedEntity { get; set; }

        //...

        public virtual ICollection<User> AllowedUsers { get; set; }
    }

    public abstract class BaseEntity
    {
        [Key]
        public int Id { get; set; }
        //...
    }
}



نظرات مطالب
ذخیره تنظیمات متغیر مربوط به یک وب اپلیکیشن ASP.NET MVC با استفاده از EF
سلام . تشکر از این مطلب مفید و کاربردی .
آیا دو کلاس زیر درست تعریف شده اند یا باید دومی به SEOSetting تبدیل شود ؟
public class GeneralSettings : SettingsBase
   {
       public string SiteName { get; set; }
       public string AdminEmail { get; set; }
       public bool RegisterUsersEnabled { get; set; }
   }
 
public class GeneralSettings : SettingsBase
   {
       public string SiteName { get; set; }
       public string AdminEmail { get; set; }
   }
مطالب
نکته‌ای در استفاده از AutoMapper
مدل Student را به شکل زیر در نظر بگیرید
public class Student
   {
       public int Id { get; set; }
       public string Name { get; set; }
       public string Family { get; set; }
       public DateTime Birthdate { get; set; }
       public string Tel { get; set; }
       public string CellPhone { get; set; }
       [Email]
       public string Email { get; set; }
   }
: آن را به صورت زیر ViewModel 
 public class StudentViewModel
    {      
        public string Name { get; set; }
        public string Family { get; set; }
        public string Email { get; set; }
    }
برای نگاشت مجموعه‌ای از مدل Student به مجموعه‌ای از مدل StudentViewModel داریم:
public ActionResult Index()
   {
      var model = db.Students.ToList();
      AutoMapper.Mapper.CreateMap<Student,StudentViewModel>();
      var studentViewModel = AutoMapper.Mapper.Map<List<Student>, IEnumerable<StudentViewModel>>(model);
      return View(studentViewModel);
    }
که اگر دستور 
 AutoMapper.Mapper.CreateMap<Student,StudentViewModel>();
:را استفاده نکنیم، خطای زیر اتفاق می‌افتد
An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll but was not handled in user code
مطالب
استفاده از FluentValidation در ASP.NET MVC
برای هماهنگی این کتابخانه با ASP.NET MVC نیاز به نصب FluentValidation.Mvc3 یا FluentValidation.Mvc4 از طریق Nuget یا دانلود کتابخانه از سایت CodePlex می‌باشد. بعد از نصب کتابخانه، نیاز به تنظیم FluentValidationModelValidatorProvider داخل متد Application_Start (فایل Global.asax) داریم: 

protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    FluentValidationModelValidatorProvider.Configure();
}
تصور کنید دو کلاس Person و PersonValidator را به صورت زیر داریم:
[Validator(typeof(PersonValidator))]
    public class Person {
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}
 
public class PersonValidator : AbstractValidator<Person> {
    public PersonValidator() {
        RuleFor(x => x.Id).NotNull();
        RuleFor(x => x.Name).Length(0, 10);
        RuleFor(x => x.Email).EmailAddress();
        RuleFor(x => x.Age).InclusiveBetween(18, 60);
    }
}
همان طور که ملاحظه می‌کنید، در بالای تعریف کلاس Person با استفاده از ValidatorAttribute مشخص کرده ایم که از PersonValidator جهت اعتبارسنجی استفاده کند.
در آخر می‌توانیم Controller و View ی برنامه مان را درست کنیم:
public class PeopleController : Controller {
    public ActionResult Create() {
        return View();
    }
 
    [HttpPost]
    public ActionResult Create(Person person) {
 
        if(! ModelState.IsValid) { // re-render the view when validation failed.
            return View("Create", person);
        }
 
        TempData["notice"] = "Person successfully created";
        return RedirectToAction("Index");
    }
}
@Html.ValidationSummary()
 
@using (Html.BeginForm()) {
Id: @Html.TextBoxFor(x => x.Id) @Html.ValidationMessageFor(x => x.Id)
<br />
Name: @Html.TextBoxFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name) 
<br />
Email: @Html.TextBoxFor(x => x.Email) @Html.ValidationMessageFor(x => x.Email)
<br />
Age: @Html.TextBoxFor(x => x.Age) @Html.ValidationMessageFor(x => x.Age)
 
<input type="submit" value="submit" />
}
اکنون DefaultModelBinder موجود در MVC برای اعتبارسنجی شیء Person از FluentValidationModelValidatorProvider استفاده خواهد کرد.
توجه داشته باشید که FluentValidation با اعتبارسنجی سمت کاربر ASP.NET MVC به خوبی کار خواهد کرد منتها نه برای تمامی اعتبارسنجی ها. به عنوان مثال تمام قوانینی که از شرط‌های When/Unless استفاده کرده اند، Validator‌های سفارشی، و قوانینی که در آن‌ها از Must استفاده شده باشد، اعتبارسنجی سمت کاربر نخواهند داشت. در زیر لیست Validator هایی که با اعتبارسنجی سمت کاربر به خوبی کار خواهند کرد آمده است:
  • NotNull/NotEmpty
  • Matches 
  • InclusiveBetween 
  • CreditCard 
  • Email 
  • EqualTo 
  • Length 
صفت CustomizeValidator
با استفاده از CustomizeValidatorAttribute می‌توان نحوه اجرای Validator را تنظیم کرد. به عنوان مثال اگر میخواهید که Validator تنها برای یک RuleSet مخصوص انجام شود می‌توانید مانند زیر عمل کنید: 
public ActionResult Save([CustomizeValidator(RuleSet="MyRuleset")] Customer cust) {
  // ...
}


مواردی که تا اینجا گفته شد برای استفاده در یک برنامه‌ی ساده MVC کافی به نظر می‌رسد، اما از اینجا به بعد مربوط به مواقعی است که نخواهیم از Attribute‌ها استفاده کنیم و کار را به IoC بسپاریم. 
استفاده از Validator Factory با استفاده از یک IoC Container
Validator Factory چیست؟ Validator Factory یک کلاس می‌باشد که وظیفه ساخت نمونه از Validator‌‌ها را بر عهده دارد. اینترفیس IValidatorFactory به صورت زیر می‌باشد:  
public interface IValidatorFactory {
   IValidator<T> GetValidator<T>();
   IValidator GetValidator(Type type);
}
ساخت Validator Factory سفارشی:
برای ساخت یک Validator Factory شما می‌توانید به طور مستقیم IValidatorFactory را پیاده سازی نمایید یا از کلاس ValidatorFactoryBase به عنوان کلاس پایه استفاده کنید (که مقداری از کارها را برای شما انجام داده است). در این مثال نحوه ایجاد یک Validator Factory که از StructureMap استفاده می‌کند را بررسی خواهیم کرد. ابتدا نیاز به ثبت Validator‌ها در StructureMap داریم: 
ObjectFactory.Configure(cfg => cfg.AddRegistry(new MyRegistry()));
 
public class MyRegistry : Registry {
    public MyRegistry() {
        For<IValidator<Person>>()
    .Singleton()
    .Use<PersonValidator>();
    }
}
در اینجا StructureMap را طوری تنظیم کرده ایم که از یک Registry سفارشی استفاده کند. در داخل این Registry به StructureMap میگوییم که زمانی که خواسته شد تا یک نمونه از IValidator<Person> ایجاد کند، PersonValidator را بر گرداند. متد CreateInstance نوع مناسب را نمونه سازی می‌کند (CustomerValidator) و آن را بازمی گرداند ( یا Null بر می‌گرداند اگر نوع مناسبی وجود نداشته باشد) 
استفاده از AssemblyScanner 
FluentValidation دارای یک AssemblyScanner می‌باشد که کار ثبت Validator‌ها داخل یک اسمبلی را راحت‌تر می‌سازد. با استفاده از AssemblyScanner کلاس MyRegistery ما شبیه قطعه کد زیر خواهد شد: 
public class MyRegistry : Registry {
   public MyRegistry() {
 
     AssemblyScanner.FindValidatorsInAssemblyContaining<MyValidator>()
       .ForEach(result => {
            For(result.InterfaceType)
               .Singleton()
               .Use(result.ValidatorType);
       });
   }
}
حالا زمان استفاده از factory ساخته شده در متد Application_Start برنامه می‌باشد:
protected void Application_Start() {
    RegisterRoutes(RouteTable.Routes);
 
    //Configure structuremap
    ObjectFactory.Configure(cfg => cfg.AddRegistry(new MyRegistry()));
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
 
    //Configure FV to use StructureMap
    var factory = new StructureMapValidatorFactory();
 
    //Tell MVC to use FV for validation
    ModelValidatorProviders.Providers.Add(new FluentValidationModelValidatorProvider(factory));        
    DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
}
اکنون FluentValidation از StructureMap برای نمونه سازی Validatorها استفاده خواهد کرد و کار اعتبارسنجی مدل‌ها به FluentValidaion سپرده شده است.
مطالب
چگونگی تعریف خاصیتی از نوع Enum در EF Code First
فرض می‌کنیم که یک Enum بصورت زیر داریم :
[Flags]
public enum Gender : byte
{
     None=0, Male=1, Female=2,
};
حال می‌خواهیم از این Enum در یک مدل ساده استفاده کنیم. از آنجا که EF هنوز قادر به ‍‍‍‍‍‍‍‍‍‍پشتیبانی از Enum نمی‌باشد باید به روش زیر عمل کنیم:
1) توسط data Annotation
public class User
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Username { get; set; }

    [Column(Name="Gender")]
    public int InternalGender { get; set; }
    [NotMapped]
    public Gender Gender
    {
        get { return (Gender)this.InternalGender; }
        set { this.InternalGender = (int)value; }
    }

    public DateTime DateOfBirth { get; set; }
}
2) توسط Fluent API
 modelBuilder.Entity<Participant>().Ignore(p => p.Gender);
 modelBuilder.Entity<Participant>().Property(p => p.InternalGender).HasColumnName("Gender");

نظرات مطالب
ModelBinder سفارشی در ASP.NET MVC
سلام؛ با تشکر از مقاله شما.
میخواستم بپرسم override کردن BindModel یا BindProperty برای زمانییه که ما به تمام دیتا هامون دسترسی داریم حالا شکل برگرداندنمون فرق میکنه؟
اگر اینطوره سوالم اینه که برای حالتی که مدل ما به شکل زیر هست چگونه Items را Bind کنم چون از هر روشی میرم null هست!
public class Model 
{
  public Model()
  {
     Items = new List<ItemModel>();
  }
 public Guid Id { get; set; }
 public Guid ProductId { get; set; }
  public List<ItemModel> Items { get; set; }
}

public class ItemModel
{
        Public Guid Id
        public string  Title{ get; set; }
        public int Value { get; set; }
}
و من در view مدل زیر را احتیاج دارم.
@model  List<Model>
در اکشن  HttpPost مربوط به این مدل ItemsProperty Is Null.
مطالب
بازسازی کد: جابجایی متد (Move method)
معمولا زمانیکه متدی از امکانات کلاس دیگری غیر از کلاسی که در آن تعریف شده است استفاده می‌کند، نیاز به چنین بازسازی کدی داریم. روش کلی این بازسازی کد، انتقال متد به کلاسی است که بیشترین تعلق را به آن دارد! 
جابجایی متد یکی از موارد پر تکرار و مهم در امر بازسازی کد است. این بازسازی در مراحل انجام دیگر بازسازی‌های کد، مانند شکستن کلاس نیز استفاده می‌شود. با این روش ساده می‌توان کلاس‌هایی با مسئولیت‌های محدود و مشخص را توسعه داد.  

مراحل انجام این بازسازی کد  

  1. تمامی امکانات کلاس مبدا را که متد مورد نظر از آنها استفاده می‌کند، بررسی نمایید که آیا آنها نیز نیاز به انتقال دارند یا خیر. 
  2. کلاس‌های پدر و فرزند کلاس مبدا را برای یافتن تعاریف دیگری از متد مورد نظر بررسی نمایید. اگر تعاریف دیگری وجود داشتند به راحتی نمی‌توان متد را جابجا کرد. در این صورت اگر قصد جابجایی داشتید، باید به فکر جابجایی رابطه چند ریختی موجود نیز باشید. 
  3. متد را در کلاس مقصد ایجاد نمایید. 
  4. بدنه متد را به متد مقصد منتقل نمایید و تمامی امکانات استفاده شده در آن را طوری تغییر دهید که در کلاس جدید کار کند. اگر متد، نیاز به اشاره‌ای به کلاس مبدا داشت، باید تعیین نمایید که به چه صورت این اشاره انجام شود. اگر مکانیزم مدیریت خطایی (exception handling) در متد مبدا پیاده سازی شده بود، تعیین کنید که آیا متد مبدا نیز کماکان امر مدیریت خطا را انجام خواهد داد، یا به متد مقصد انتقال خواهد یافت. 
  5. کد کلاس مقصد را کامپایل و تست نمایید. 
  6. اگر متد مبدا را به عنوان فراخوان متد مقصد نگه داشتید، باید تصمیم بگیرید که کلاس مقصد در آن متد به چه صورت استفاده خواهد شد. 
  7. فراخوانی متد مقصد را به بدنه متد مبدا اضافه کنید. 
  8. کد را کامپایل و تست نمایید.
  9. در مورد سرنوشت متد مبدا تصمیم گیری نمایید که آیا نیازی به وجود آن هست یا خیر. در صورتیکه از متد مبدا در مکان‌های زیادی استفاده شده یا متد در کتابخانه یا فریم ورکی است که کنترلی بر روی استفاده کنندگان آن وجود ندارد، احتمالا باقی ماندن متد به عنوان صرفا فراخوان، ایده خوبی باشد. 
  10. اگر متد مبدا را حذف کردید تمامی استفاده از آن را باید به متد مقصد تغییر دهید. توجه داشته باشید ممکن است سناریو ساختن کلاس جدید با کلاس قدیمی متفاوت باشد. 
  11. مجددا کد را کامپایل و تست نمایید.  

مثال: فرض کنید نرم افزاری برای مدیریت رویدادها و شرکت کنندگان آن‌ها تهیه کرده‌ایم. در این نرم افزار، کلاسی با نام Event وجود دارد و کلاسی نیز با نام Person که نام آنها کاملا نمایانگر استفاده آنها است.
بخشی از بدنه این کلاس‌ها به صورت زیر است:  
public class Event 
{ 
    public List<Person> Participants { get; internal set; } 
} 
public class Person 
{ 
    public int Id { get; private set; } 
    public void Participate(Event ev) 
    { 
        var isParticipatedAlready = ev.Participants.Any(ff => ff.Id == Id); 
        if (isParticipatedAlready) 
            return; 
        ev.Participants.Add(this); 
    } 
}
در کد مربوط به کلاس Person، شاهد متدی هستیم که عمل ثبت‌نام فرد را در یک رویداد انجام می‌دهد. اما با دقت به این متد مشاهده می‌کنیم که بدنه این متد بیشتر از اعضای کلاس Event استفاده می‌کند. حتی این استفاده باعث شده است که خصوصیت Participants از کلاس Event به صورت public تعریف شود که خود مشکل دیگری در این طراحی است.
در چنین شرایطی، بازسازی کد جابجایی متد می‌تواند در راستای انتقال مسئولیت‌های مناسب هر کلاس به بدنه آن و بهبود طراحی کمک کند. بعد از بازسازی کد شاهد چنین طراحی‌ای هستیم:  
public class Event 
{ 
    protected List<Person> Participants { get; set; } 
    public void Participate(Person person) 
    { 
        var isParticipatedAlready = Participants.Any(ff => ff.Id == person.Id); 
        if (isParticipatedAlready) 
            return; 
        Participants.Add(person); 
    } 
} 
public class Person 
{ 
    public int Id { get; private set; } 
}
بازسازی‌ای که انجام شد، دو تاثیر را بر روی طراحی این کلاس‌ها داشته است:
   اول: جایگذاری بهتر و منطقی‌تر مسئولیت‌های یک کلاس 
   دوم: کپسوله سازی آسان‌تر کلاس ها 

بازسازی کد جابجایی متد، سنگ بنای بیشتر بازسازی‌های مورد نیاز در فعالیت‌های روزمره تولید یا نگهداری نرم افزار است. علارغم این که این بازسازی ساده به نظر می‌رسد، در مجموعه کدهای پیچیده، انجام این بازسازی ممکن است امری طاقت فرسا شود.  
مطالب
بررسی تفاوت بین DTO و POCO
در ابتدا اجازه بدهید تعریف درستی از این دو واژه، ارائه کنیم.

DTO (Data Transfer Object)
به بیان خیلی ساده، DTO‌ها برای انتقال اطلاعات استفاده می‌شوند؛ پس هیچ منطق و رفتاری در این اشیاء تعریف نمی‌شود .اگر در DTO منطقی پیاده سازی شود، دیگر به آن DTO گفته نمی‌شود. اجازه بدید منظورمان را از منطق یا رفتار مشخص کنیم. منطق یا رفتار، همان متدهایی هستند که در نوع داده خود تعریف میکنیم. در #C، یک DTO تنها از خصوصیت‌ها (Properties) که از بلوک‌های Get و Set تشکیل شده‌اند، ساخته می‌شود. البته بدون کدهایی جهت اعتبار سنجی (Validation) مقادیر.

سؤال: وضعیت attribute ‌ها و Metadata‌ها چه می‌شود؟
خیلی غیر معمول نیست که از metadata‌ها در DTO، به‌منظور اعتبار سنجی یا اهداف خاص، استفاده کنیم. بعضی از attribute‌ها هیچ رفتاری را به DTO‌ها اضافه نمی‌کنند؛ ولی استفاده از DTO‌ها را در بخش‌های دیگر سیستم، ساده‌تر می‌کنند. در نتیجه هیچکدام از attribute ‌ها و metadata‌ها، شرایط DTO بودن را نقض نمی‌کنند.

مدل‌های دیگری مثل ViewModels‌ها و API Model‌ها چه می‌شوند؟
واژه DTO خیلی مبهم است. تنها چیزی که بیان می‌کند این است که شیء است و فقط و فقط شامل اطلاعات است و رفتاری ندارد. در این تعریف درباره‌ی کاربرد مورد نظر یک DTO چیزی گفته نشده. در بسیاری از معماری‌ها، DTO نقش خاصی را ایفا می‌کند. بطور مثال در معماری MVC، از DTO‌ها برای انقیاد داده (Binding) و ارسال اطلاعات به یک View استفاده میکنند. به همین خاطر این DTO‌ها بعنوان ViewModel، در معماری MVC شناخته می‌شوند که رفتاری را در خود تعریف نمی‌کنند و تنها فرمت اطلاعات مورد انتظار یک View را مهیا می‌کنند.
پس در این سناریوی خاص، ViewModel نوعی DTO می‌باشد. اما باید دقت داشته باشید، همه ViewModel‌‌ها را نمی‌توان DTO محسوب کرد؛ مثلا در معماری MVVM، ویوو مدل‌های تعریف شده، شامل رفتار هم می‌باشند. حتی در معماری MVC نیز گاهی اوقات منطقی به  ViewModel‌‌ها اضافه می‌شود که دیگر به آنها DTO نمی‌گوییم.

 
در صورت امکان، نام DTO‌ها را بر اساس استفاده‌ی آنها تعیین کنید. بطور مثال کلاسی با نام FoodDTO، مشخص نمی‌کند که این نوع، کجا و چگونه قرار است در معماری برنامه شما مورد استفاده قرار  بگیرید؛ برعکس نامگذاری به صورت FoodViewModels کاربرد آن را صراحتا بیان می‌کند.
مثالی از DTO در زبان سی شارپ :
public class ProductViewModel
{
  public int ProductId { get; set; }
  public string Name { get; set; }
  public string Description { get; set; }
  public string ImageUrl { get; set; }
  public decimal UnitPrice { get; set; }
}

کپسوله سازی و DTO ها 
کپسوله سازی، یکی از اصول برنامه نویسی شیءگرا می‌باشد. اما این کپسوله سازی به DTO‌ها اعمال نمی‌شوند. به این علت که هدف کپسوله سازی، پنهان کردن فرآیند پشت صحنه‌ی ذخیره سازی اطلاعات است؛ اما در DTO هیچ فرآیندی پیاده سازی نشده و نباید هیچ State پنهانی وجود داشته باشد. پس بحث Encapsulation در DTO منتفی است. پس کار را برای خودتان سخت نکنید؛ با تعریف private setter ‌ها یا تبدیل کردن DTO به یک شیء غیرقابل تغییر (immutable). شما باید به‌راحتی بتوانید عملیات ایجاد، نوشتن و خواندن DTO‌‌ها را انجام دهید؛ همچنین باید بتوانید عملیات سریالایز کردن بر روی DTO‌‌ها را بدون فرآیند سفارشی اضافه‌ای، انجام دهید.

Field ها  یا Property ها 
سؤالی که مطرح می‌شود این است که وقتی کپسوله سازی در DTO مفهومی ندارد، چرا باید همیشه از property ‌ها استفاده کنیم؟ چرا از فیلد‌ها استفاده نکنیم (فیلد‌های public )؟
ما می‌توانیم هم از property استفاده کنیم و هم از field‌ها؛ اما بعضی از فریم ورک‌ها که کار Serialization را انجام می‌دهند، فقط با property ‌ها کار می‌کنند. بنابراین بسته به نیاز خودتان، از field‌های عمومی یا property‌ها استفاده کنید. اما عموما از Property استفاده میکنند. البته در این پیوند، پرسش و پاسخ مفصلی در این رابطه وجود دارد.

غیرقابل تغییر بودن (Immutability) و نوع رکورد ( Record Type )
غیرقابل تغییر بودن، یکی از مزیت‌های مهم در توسعه نرم افزار است. اما همانطور که در مثل بالا بیان شد، نیازی به غیرقابل تغییر کردن DTO‌‌ها نیست. با ارائه رکورد در سی شارپ 9  شرایط کمی تغییر کرد. شاید عبارت مخفف دیگری که اضافه شده Data transfer Records یا (DTRs) است. یکی از روش‌های تعریف DTR در سی شارپ 9، به شکل زیر است:
public record ProductDTO(int Id, string Name, string Description);

البته روش دیگری هم وجود دارد که شما property‌ها را تعریف کنید و از طریق سازنده، مقدار دهی شوند. ویژگی جدید init-only این امکان را فراهم می‌کند که فقط در زمان مقدار دهی اولیه (initialization)، خصوصیات مقداردهی شوند و در ادامه‌ی چرخه حیات شیء، property ‌ها فقط خواندنی هستند. این ویژگی، record‌ها را غیر قابل تغییر می‌کند.
مثال:
public record ProductDTO
{
  public int Id { get; init; }
  public string Name { get; init; }
}
var dto = new ProductDTO { Id = 1, Name = "some name" };

کلاس‌های POCO یا همان Plain Old CLR/C# Object
شی Plain Old چیست؟ هر شیءای که Plain Old باشد، می‌تواند در هر جایی از برنامه‌ی ما مورد استفاده قرار بگیرد؛ حتی در کلاس‌های Test برنامه. این اشیاء هیچگونه وابستگی برای اجرا وظایف خود، به بانک‌های اطلاعاتی و کتابخانه‌های ثالت ندارند.
برای درک بهتر این نوع کلاس‌ها، به مثال زیر دقت کنید:
public class Product : DataObject<Product>
{
  public Product(int id)
  {
    Id = id;
    InitializeFromDatabase();
  }
  private void InitializeFromDatabase()
  {
    DataHelpers.LoadFromDatabase(this);
  }
  public int Id { get; private set; }
  // other properties and methods
}
همانطور که مشاهده میکنید، این کلاس به متد استاتیکی برای کار با دیتابیس وابسته است؛ در نتیجه باعث میشود که کل کلاس، به وجود بانک اطلاعاتی وابسته شود. همچنین با ارث بری از کلاس پایه‌ی دیگری، وابستگی به یک کتابخانه‌ی ثالث ایجاد شده‌است. اجرای آزمون واحد برای چنین کلاسی، سبب fail شدن عملیات می‌شود. به این علت که ارتباط با بانک اطلاعاتی مورد نیاز متد DataHelpers، تامین نشده‌است. این شرایط، مثالی از الگوی Active Record Pattern می‌باشند. همچنین این کلاس دسترسی به منبع داده را در درون خود گنجانده است که این به معنای نقض اصل Persistence Ignorant (اصل Persistence Ignorance به طور خلاصه بیان می‌کند که در تحلیل و طراحی Business Logic به موضوع ذخیره‌سازی (Persistence) فکر نکنید (تا جای ممکن) یا به عبارت دیگر، ذهن خود را درگیر پیچیدگی‌های ذخیره سازی نکنید. برگرفته شده از breakpoint.blog.ir : روح الله دلپاک)می باشد. یکی از ویژگی‌های POCO عدم نقض الگوی فوق است.

مثالی از POCO : 
public class Product
{
  public Product(int id)
  {
    Id = id;
  }

  private Product()
  {
    // required for EF
  }

  public int Id { get; private set; }
  // other properties and methods
}
این کلاس یک POCO است:
  • برای اجرای وظایف خود به فریم ورک ثالثی وابسته نیست.
  • به کلاس پایه‌ای ( Base class) نیاز ندارد.
  • وابستگی به متد استاتیکی ندارد.
  • می تواند در هر جایی از پروژه، نمونه سازی شود.
  • اصل Persistence Ignorant را بیشتر رعایت کرده، نه بطور کامل؛ چون یک سازنده دارد که به کتابخانه‌ی ثالثی نیازمند است (سازنده‌ی بدون پارامتر که مورد نیاز EF می‌باشد).

POCO و DTO :

شاید این دو مفهموم گیج کننده باشند، ولی DTO همان POCO هست. اگر یک کلاس، DTO باشد، حتما POCO نیز هست. (مرور ویژگی‌های دو مورد در بخش‌های قبلی) ولی برعکس این وضعیت ممکن است صادق نباشد؛ مثال قبلی که در آن وابستگی به کتابخانه‌ی ثالثی در سازنده‌ی بدون پارامتر وجود داشت، DTO بودن را نقض می‌کرد. پس اگر هر دو حالت صادق بود، میتوان گفت این دو مفهوم یکی است.
پاسخ به بازخورد‌های پروژه‌ها
ایجاد گزارش با داده های ثابت و متغیر
ممنون از جوابتون.کلاس من به فرض مثال به شکل زیر است :
public class Documents
    {
        [Key]
        public int Id { set; get; }

        [Display(Name = "تاریخ")]
        public string Date { set; get; }

        [Display(Name = "تاریخ میلادی")]
        public DateTime? EnDate { set; get; }

        [Display(Name = "روز هفته")]
        public string Day { set; get; }

        [Display(Name = "بهداشتی نقدی ")]
        public string BehdashtiNaghdi { set; get; }

        [Display(Name = "بهداشتی کارتخوان")]
        public string BehdashtiPose { set; get; }

        [Display(Name = "فیش بستری نقدی")]
        public string FishBastariNaghdi { set; get; }

        [Display(Name = "فیش بستری کارتخوان")]
        public string FishBastariPose { set; get; }

        [Display(Name = "تعداد فیش")]
        public string FishCount { set; get; }

        [Display(Name = "سرپایی نقدی")]
        public string SarepayiNaghdi { set; get; }

        [Display(Name = "سرپایی کارتخوان")]
        public string SarepayiPose { set; get; }

        [Display(Name = "تعداد نسخه")]
        public string Noskhecount { set; get; }

        [Display(Name = "صندوق شماره 1")]
        public string SandoghNumberOne { set; get; }

        [Display(Name = "صندوق شماره 2")]
        public string SandoghNumberTow { set; get; }

        [Display(Name = "تعداد فیش‌های کارتخوان")]
        public string PoseResidCount { set; get; }

        [Display(Name = "نمره دکتر")]
        public string DoctosrRate { set; get; }

        [Display(Name = "توضیحات")]
        public string Description { set; get; }
        
        [Display(Name = "توسط کاربر")]
        [ForeignKey("User")]
        public virtual Users Users { get; set; }
        public int User { get; set; }

        [Display(Name = "پزشک شیفت")]
        [ForeignKey("Doctor")]
        public virtual Doctors Doctors { get; set; }
        public int Doctor { get; set; }

        [Display(Name = "شیفت")]
        [ForeignKey("Shift")]
        public virtual Shift ShiftsShift { get; set; }
        public int Shift { get; set; }

        [Display(Name = "نام بیمارستان")]
        [ForeignKey("HospitalId")]
        public virtual Hospitals Hospitals { get; set; }
        public int? HospitalId { get; set; }

    }
طبق فرموده‌ی شما من باید برای هر فیلد، یک فیلد دیگر با عنوان Title تعریف کنم که بشود در گزارش استفاده کرد. چون داخل تک تک فیلد‌های بالا فقط مقادیر ریخته میشوند. چیزی با این شکل که عنوانی برای هر فیلد قرار داشته باشد نیست.
اما راهی نیست که این موارد که همان عناوین است به شکل تصویر بالا رو به صورت دستی در گزارش وارد کنم؟
نظرات مطالب
خودکارسازی فرآیند نگاشت اشیاء در AutoMapper
سلام؛ با چه  روشی می‌توان وقتی که اطلاعات را از دیتابیس فراخوانی می‌کنم، AddressViewModel داخل SupplierViewModel از نگاشت  <CreateMap<Address, AddressViewModel استفاده کند.
public class SupplierViewModel
{  
   public string name { get; set; }   
   public AddressViewModel AddressViewModel { get; set; }=new AddressViewModel();    }

public class AddressViewModel
{
   public string phone_mobile { get; set; }
   public string address1 { get; set; }
}

public class Supplier 
{      
   public int Id { get; set; }   
   public string name { get; set; } 
   public virtual Address Address { get; set; } 
} 

CreateMap<Supplier, SupplierViewModel>().IgnoreAllUnmapped()  });
CreateMap<Address, AddressViewModel>()
var viewModel =
         await  _suppliers.AsNoTracking()
                   .ProjectTo<SupplierViewModel>(parameters:null,configuration: _mappingEngine.ConfigurationProvider)
                   .FirstOrDefaultAsync(a => a.Id == id);