معرفی فریمورک AUA  بخش دوم

در قسمت اول به صورت کلی فریمورک AUA را مورد برسی قرار دادیم و تا حد کمی وارد قسمت کد نویسی شدیم در این مقاله بخش های دیگیر AUA Framework را مورد بررسی قرار میدهیم. لطفا سوالات و نظرات خوتون رو در قسمت کامنت ها بنویسیدکلیه محتوای این مقاله متعلق به سایت auaframework میباشداگه ایم مقاله براتون مفید بود تو کافیته یک قهوه مهمونم کنید : ) https://www.coffeete.ir/Sobhan توابع ConvertTo و ProjectToبرای کمک در عملیات مپینگ می توان یک آبجکت یا لیستی از آبجکت ها را بدون هیچ محدودیتی به یک یا لیستی از ویو مدل ها تبدیل نمود و از IMapFrom و IHaveCustomMappings نیز برای کانفیگ عملیات مپینگ استفاده نمود. (MapperInstance یک نمونه از AutoMapper در تمام سرویس ها به صورت پیشفرض است) برای مثال ما یک ویو مدل با نام TestMappingVm ساخته ایم که برای کانفیگ عملیلات مپینگ از IHaveCustomMappings استفاده می کند که نتیجه یک کوئری از موجودیت AppUser را به این ویو مدل با کمک ConvertTo و ProjectTo مپ می کنیم .public class TestMappingVm : IHaveCustomMappings { public string Email { get; set; } public stringPersonName { get; set; } public intRoleCount { get; set; } public ICollection<Role> UserRoles { get; set; } public voidConfigureMapping(Profile configuration) { configuration.CreateMap<AppUser, TestMappingVm>() .ForMember(p => p.PersonName, p => p.MapFrom(q => q.FirstName + &quot &quot + q.LastName)) .ForMember(p => p.RoleCount, p => p.MapFrom(q => q.UserRoles.Count)) .ForMember(p => p.UserRole, p => p.MapFrom(q => q.UserRoles.Select(m => m.Role))) .ReverseMap(); } }مپینگ با استفاده از ProjectTopublic IEnumerable<TestMappingVm> GeTestMappingVms() { var query = GetAll().Where(p => p.IsActive); return MapperInstance .ProjectTo<TestMappingVm>(query) .ToList(); }مپینگ با استفاده از ConvertTopublic IEnumerable<TestMappingVm> GeTestMappingVms() { var query = GetAll().Where(p => p.IsActive); return query .ConvertTo<TestMappingVm>(MapperInstance) .ToList(); }یکی از مهمترین مشخصه های فریم ورک AUA امنیت بالای آن می باشد . امنیت و پرفرمنس همیشه در مقابل هم بوده اند. تیم امنیتی هیلتن با تلاش زیاد یک راه بسیار مناسب برای بالا بردن امنیت طراحی نموده که پرفرمنس آن نیز حفظ می شود و در فریم ورکAUA از آن استفاده نموده ایم.هر ویو مدل و DTO از کلاس جنریک BaseEncryptionVm<TPrimaryKey> ارث بری می کنند و دو فیلد EncKeyIdو DecKeyId به آن اضافه می شوند.EncKeyId معادل رمزنگاری شده ی کلید اصلی( Id) می باشد. کلید رمز نگاری برای هر کاربر متفاوت تولید می شود . در صورتی که برنامه نویس در دستور Select خود این فیلد را مشخص کرده باشد، تولید و در غیر این صورت فریم ورک آن را تولید نمی کند.DecKeyId معادل Id می باشد.علاوه بر این در BaseController تابع DecKeyId در نظر گرفته شده که EncKeyId را دریافت و به Id تبدیل می کند و برنامه نویس در هیچ شرایطی درگیر رمز نگاری و الگوریتم های Hash نمی شود.( البته می توان از این ویژگی استفاده نکرد و از همان Id استفاده کرد.)public async Task<IActionResult> _Update(string keyId) { var userId = DecKeyId<long>(keyId); var model = await_appUserService.GetAppUserVmAsync(userId); return View(model); }سرویس هاتمام بیزینس در قالب سرویس ها پیاده سازی و در لایه سرویس ایجاد می شود. لایه سرویس از لایه Service Infrastructure استفاده می کند و به هر سرویس به صورت خودکار Repository خودش وصل می شود. مزیت این روش این است که برنامه نویس درگیر دو مفهوم Repository و سرویس نشده و فقط روی سرویس خود متمرکز می شود. سرویس به صورت built in ( توکار) Repository خود را دارد و این یکی از مهمترین مشخصه های معماری فریم ورک AUA است. برای مثال اگر بخواهیم برای موجودیت Student یک سرویس بنویسیم ، ابتدا باید یک اینترفیس برای موجودیت Student ساخته شود که از کلاس IGenericEntityService ارث بری می کند.public interface IStudentService : IGenericEntityService<Student, StudentDto> { }بعد از اینتر فیس می توان سرویس مورد نظر را ایجاد نمود. سرویس باید از کلاس GenericEntityService ارث بری کند و اینترفیس IStudentService که در گام قبل ساخته شد را پیاده سازی نماید.public class StudentService: GenericEntityService<Student,StudentDto>,IStudentService { public StudentService(IUnitOfWork unitOfWork) : base(unitOfWork) { } }به صورت پیش فرض سرویس ایجاد شده شامل تمام توابع مورد نیاز برای کار با Repository می باشد.لیست توابع Repository که به صورت خودکار به هر سرویس اضافه می شوند.؛ GetAll : کل موجودیت ها را برگشت می دهد و قابلیت فیلتر شدن دارد. پشتیبانی از Async؛ :GetAllDto : کل موجودیت ها را در قالب DTO برگشت می دهد و قابلیت فیلتر شدن دارد . پشتیبانی از Async؛ GetCount : تعداد موجودیت ها - قابلیت فیلتر شدن دارد.؛ GetFirst : اولین موجودیت را برگشت میدهد - قابلیت فیلتر شدن دارد.؛ GetLast : آخرین موجودیت را برگشت میدهد- قابلیت فیلتر شدن دارد.؛ GetCountAsync : تعداد موجودیت ها - قابلیت فیلتر شدن دارد. پشتیبانی از Async؛ GetFirstAsync : اولین موجودیت را برگشت میدهد- قابلیت فیلتر شدن دارد. پشتیبانی از Async؛ GetLastAsync : آخرین موجودیت را برگشت میدهد - قابلیت فیلتر شدن دارد. پشتیبانی از Async؛ GetDtoById : گرفتن موجودیت و مپ کردن در قالب DTO؛ GetByIdAsync : گرفتن موجودیت با کلید اصلی. پشتیبانی از Async؛ GetDtoByIdAsync : گرفتن موجودیت و مپ کردن در قالب DTO پشتیبانی از Async؛ Delete : حذف موجودیت –) با کلید اصلی یا Entity یا (DTO؛ DeleteAsync : حذف موجودیت –) با کلید اصلی یا Entity یا (DTO پشتیبانی از Async؛ Insert : درج موجودیت جدید ) با Entity یا DTO )؛ InsertAsync : درج موجودیت جدید پشتیبانی از Async؛ InsertMany : درج چندین موجودیت به صورت همزمان؛ InsertManyAsync : درج چندین موجودیت به صورت همزمان پشتیبانی از Async؛ InsertCustomVm : درج موجودیت با ویو مدل سفارشی(هنگامی که بخشی از فیلد های موجودیت از ویو ارسال می شود)؛ InsertCustomVmAsync : درج موجودیت با ویو مدل سفارشی- پشتیبانی از Async؛ PartialInsert : درج موجودیت در ریپوزیتوری بدون ارسال به سمت دیتابیس - پشتیبانی از Async؛ Update : ویرایش موجودیت جدید ) با Entity یا DTO )؛ UpdateAsync : ویرایش موجودیت جدید ) با Entity یا DTO ) پشتیبانی از Async؛ UpdateCustomVm :ویرایش موجودیت با ویو مدل سفارشی(هنگامی که بخشی از فیلد های موجودیت از ویو ارسال می شود)؛ UpdateCustomVmAsync :ویرایش موجودیت با ویو مدل سفارشی(هنگامی که بخشی از فیلد های موجودیت از ویو ارسال می شود)؛ PartialUpdate : درج موجودیت در ریپوزیتوری بدون ارسال به سمت دیتابیس - پشتیبانی از Async؛ ConvertTo : نتیجه کوئری را بر اساس کانفیگ مپینگ به آبجکتی دیگر تبدیل می کند؛ ProjectTo : نتیجه کوئری را بر اساس کانفیگ مپینگ به آبجکتی دیگر تبدیل می کند؛ SaveChange : در صورتی که از توابع Partial استفاده کنیم وضعیت نهایی را مشخص کنید.؛ SaveChangeAsync :در صورتی که از توابع Partial استفاده کنیم وضعیت نهایی را مشخص کنید. پشتیبانی از Asyncبرنامه نویس می تواند بیزینس خود را در سرویس ها پیاده سازی کند. یک سرویس می تواند از سرویس های دیگر استفاده کند.به راحتی می توان سرویس ها را داخل یکی دیگر اینجکت (Inject) و استفاده نمود.سطوح دسترسیدر فریم ورکAsp.Net Unique Architecture شما به عنوان مدیر می توانید دسترسی کاربران را تا ریزترین سطح ممکن کنترل کنید. این عمل توسط ماژول مدیریت کاربران انجام می گیرد. شما می توانید با تعریف نقش ها (Roles) و اختصاص دادن سطوح دسترسیUserAcess به نقش ها، آنها را به کاربران نسبت دهید. در زیر نمودار دیاگرام سطوح دسترسی در فریم ورد AUA را مشاهده می کنیم.چهار مدل Authentication and Authorization در فریم ورک AUA وجود دارد که به راحتی می توان سطح دسترسی در کنترلر و اکشن را مشخص نمود .1.؛ WebAuthorize با استفاده از این Attribute می توان سطوح دسترسی را برای اکشن و کنترلر مشخص نمود.2.؛ AllowLoggedInUser کاربر فقط کافی است لاگین کرده باشد.3.؛ OnlyLocalActionAuthorize این Attribute برای اکشن و کنترلر های است که فقط باید به صورت لوکال روی سرور فراخوانی شوند.4.؛ AllowAnonymousAuthorize هر کاربری می تواند بدون محدودیت به این اکشن و کنترلر دسترسی داشته باشدبرای استفاده از WebAuthorize باید برای هر کنترل یا اکشن در EUserAccess یک ایتم اضافه کنید. در پایگاه داده در جدول UserAccess نام و شماره دسترسی و توضیحات و URL را اضافه می کنیم.public enum EUserAccess { #region Accounting [Description(&quotمدیریت کاربران&quot)] AppUser = 1, [Description(&quotمدیریت سطح دسترسی&quot)] UserAccess = 2, [Description(&quotمدیریت نقش ها&quot)] UserRole = 3, [Description(&quotمدیریت سطح دسترسی نقش ها&quot)] serRoleAccess = 4, #endregion }برای استفاده از WebAuthorize فقط کافی است آن را بالای کنترلر یا اکشن بنویسید. برای مثال دسترسی به اکشن زیر فقط برای کاربرانی می باشد که دسترسی AppUser = 1را داشته باشند.[WebAuthorize(EUserAccess.AppUser)] public async Task<IActionResult> _Insert(AppUserDto appUserDto) { await _appUserService.InsertAsync(appUserDto); return RedirectToAction(&quotIndex&quot); }ورودی WebAuthorize می تواند چندین سطح دسترسی وجود داشته باشد که با هم OR می شوند. یعنی کاربر اگر یکی از این دسترسی ها را داشته باشد به آن منبع دسترسی دارد.عنوان صفحات به صورت پیشفرض با توجه به توضیحات موجود در جدول UserAccess برای صفحه لود می شود .گزارش گیری Reportingیکی از مهمترین ویژگی های نرم افزار ها گرفتن گزارش با قابلیت های مختلف می باشد و یکی از دغدغه های برنامه نویس ها اضافه کردن و تغییر دادن فیلتر ها می باشد که در فریم ورک Asp.Net Unique Architecture (AUA) این کار به راحتی و با سرعت بالا قابل انجام می باشد و می توان گزارش خود را با فیلتر های متنوع ایجاد نمود.برای ایجاد گزارش ابتدا یک ویو مدل جهت اعمال فیلتر و یک ویو مدل دیگر جهت نمایش خروجی گزارش می سازیم و در نهایت سرچ فیلتر خود را جهت اعمال فیلتر ها می نویسیم.برای مثال . یک گزارش از تمام کاربران و تمام دسترسی های آنها ایجاد می کنیم. ویو مدل سرچ فیلتر:public class UserAccessReportSearchVm : BaseSearchVm { [DisplayName(&quotنام&quot)] public string FirstName { get; set; } [DisplayName(&quotنام خانوادگی&quot)] public string LastName { get; set; } [Display(Name = &quotنام کاربری&quot)] public string UserName { get; set; } [DisplayName(&quotفعال/غیر فعال&quot)] public bool? IsActive { get; set; } public List<SelectListItem> RecordStatusItem { get; set; } [DisplayName(&quotعنوان نقش&quot)] public string RoleTitle { get; set; } [DisplayName(&quotعنوان سطح دسترسی&quot)] public string UserAccessTitle { get; set; } }ویو مدل خروجی گزاش :public class UserAccessReportGridVm : BaseEntityDto<long>, IHaveCustomMappings { public string FirstName { get; set; } public string LastName { get; set; } public string UserName { get; set; } public string RoleTitle { get; set; } public string FullName => FirstName + &quot &quot + LastName; public string Password { get; set; } public string Phone { get; set; } public string Email { get; set; } ublic bool IsActive { get; set; } public ICollection<Role> UserRoles { get; set; } public IEnumerable<UserAccess> UserAccess { get; set; } public string UserRolesTitles => string.Join(&quot<br/> &quot, UserRoles.Select(p => p.Title)); public string UserAccessTitles => string.Join(&quot<br/> &quot, UserAccess.Select(p => p.Title)); public void ConfigureMapping(Profile configuration) { configuration.CreateMap<AppUser, UserAccessReportGridVm>() .ForMember(p => p.UserRoles, p => p.MapFrom(q => q.UserRoles.Select(r => r.Role))) .ForMember(p => p.UserAccess, p => p.MapFrom(q => q.UserRoles.SelectMany(t => t.Role.UserRoleAccess.Select(m => m.UserAccess)))) .ReverseMap(); } }بعد از ساختن سرچ ویو مدل و گرید ویو مدل یک سرچ فیلتر می نویسیم که فیلتر های را اعمال کند.public class UserAccessReportFilter : Specification<AppUser> { private readonly UserAccessReportSearchVm _searchVm; private Expression<Func<AppUser, bool>> _expression = p => true; protected bool IsEmptyFilter => _searchVm == null; public UserAccessReportFilter(UserAccessReportSearchVm searchVm) { _searchVm = searchVm; public override Expression<Func<AppUser, bool>> IsSatisfiedBy() { if (IsEmptyFilter) return p => true; ApplyDefaultFilter(); ApplyFirstNameFilter(); ApplyLastNameFilter();ApplyUserNameFilter(); ApplyIsActiveFilter(); ApplyRoleTitleFilter(); ApplyUserAccessTitleFilter(); return _expression; } private void ApplyDefaultFilter() { } private void ApplyFirstNameFilter() { if (string.IsNullOrWhiteSpace(_searchVm.FirstName)) return; _expression = _expression.And(p => p.FirstName.Contains(_searchVm.FirstName)); } private void ApplyLastNameFilter() { if (string.IsNullOrWhiteSpace(_searchVm.LastName)) return; _expression = _expression.And(p => p.LastName.Contains(_searchVm.LastName)); } private void ApplyUserNameFilter() { if (string.IsNullOrWhiteSpace(_searchVm.UserName)) return; _expression = _expression.And(p => p.UserName.Contains(_searchVm.UserName)); }خروجی این گزارش را می توان به صورت زیر مشاهده نمود.در مقالات بعدی سایت بخش های AUA را مورد بررسی قرار خواهیم داد. لطفا سوال هاتون رو در قسمت کامن ها مطرح کنید