در قسمت اول به صورت کلی فریمورک 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 + " " + 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(); } }
مپینگ با استفاده از ProjectTo
public IEnumerable<TestMappingVm> GeTestMappingVms() { var query = GetAll().Where(p => p.IsActive); return MapperInstance .ProjectTo<TestMappingVm>(query) .ToList(); }
مپینگ با استفاده از ConvertTo
public 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 که به صورت خودکار به هر سرویس اضافه می شوند.
برنامه نویس می تواند بیزینس خود را در سرویس ها پیاده سازی کند. یک سرویس می تواند از سرویس های دیگر استفاده کند.
به راحتی می توان سرویس ها را داخل یکی دیگر اینجکت (Inject) و استفاده نمود.
سطوح دسترسی
در فریم ورکAsp.Net Unique Architecture شما به عنوان مدیر می توانید دسترسی کاربران را تا ریزترین سطح ممکن کنترل کنید. این عمل توسط ماژول مدیریت کاربران انجام می گیرد. شما می توانید با تعریف نقش ها (Roles) و اختصاص دادن سطوح دسترسیUserAcess به نقش ها، آنها را به کاربران نسبت دهید. در زیر نمودار دیاگرام سطوح دسترسی در فریم ورد AUA را مشاهده می کنیم.
چهار مدل Authentication and Authorization در فریم ورک AUA وجود دارد که به راحتی می توان سطح دسترسی در کنترلر و اکشن را مشخص نمود .
برای استفاده از WebAuthorize باید برای هر کنترل یا اکشن در EUserAccess یک ایتم اضافه کنید. در پایگاه داده در جدول UserAccess نام و شماره دسترسی و توضیحات و URL را اضافه می کنیم.
public enum EUserAccess { #region Accounting [Description("مدیریت کاربران")] AppUser = 1, [Description("مدیریت سطح دسترسی")] UserAccess = 2, [Description("مدیریت نقش ها")] UserRole = 3, [Description("مدیریت سطح دسترسی نقش ها")] serRoleAccess = 4, #endregion }
برای استفاده از WebAuthorize فقط کافی است آن را بالای کنترلر یا اکشن بنویسید. برای مثال دسترسی به اکشن زیر فقط برای کاربرانی می باشد که دسترسی AppUser = 1را داشته باشند.
[WebAuthorize(EUserAccess.AppUser)] public async Task<IActionResult> _Insert(AppUserDto appUserDto) { await _appUserService.InsertAsync(appUserDto); return RedirectToAction("Index"); }
ورودی WebAuthorize می تواند چندین سطح دسترسی وجود داشته باشد که با هم OR می شوند. یعنی کاربر اگر یکی از این دسترسی ها را داشته باشد به آن منبع دسترسی دارد.
عنوان صفحات به صورت پیشفرض با توجه به توضیحات موجود در جدول UserAccess برای صفحه لود می شود .
گزارش گیری Reporting
یکی از مهمترین ویژگی های نرم افزار ها گرفتن گزارش با قابلیت های مختلف می باشد و یکی از دغدغه های برنامه نویس ها اضافه کردن و تغییر دادن فیلتر ها می باشد که در فریم ورک Asp.Net Unique Architecture (AUA) این کار به راحتی و با سرعت بالا قابل انجام می باشد و می توان گزارش خود را با فیلتر های متنوع ایجاد نمود.برای ایجاد گزارش ابتدا یک ویو مدل جهت اعمال فیلتر و یک ویو مدل دیگر جهت نمایش خروجی گزارش می سازیم و در نهایت سرچ فیلتر خود را جهت اعمال فیلتر ها می نویسیم.
برای مثال . یک گزارش از تمام کاربران و تمام دسترسی های آنها ایجاد می کنیم.
ویو مدل سرچ فیلتر:
public class UserAccessReportSearchVm : BaseSearchVm { [DisplayName("نام")] public string FirstName { get; set; } [DisplayName("نام خانوادگی")] public string LastName { get; set; } [Display(Name = "نام کاربری")] public string UserName { get; set; } [DisplayName("فعال/غیر فعال")] public bool? IsActive { get; set; } public List<SelectListItem> RecordStatusItem { get; set; } [DisplayName("عنوان نقش")] public string RoleTitle { get; set; } [DisplayName("عنوان سطح دسترسی")] 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 + " " + 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("<br/> ", UserRoles.Select(p => p.Title)); public string UserAccessTitles => string.Join("<br/> ", 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 را مورد بررسی قرار خواهیم داد. لطفا سوال هاتون رو در قسمت کامن ها مطرح کنید