فریم ورک Asp.Net Unique Architecture

 

 

 

 

 

 

 

 

 

 


Contents

Abstract . 1

Why Framework? . 1

Asp.Net Unique Architecture (AUA) Framework . 1

AUA Framework's Overall Structure . 1

Adding New Entity . 1

Entity Class . 1

ConvertTo and ProjectTo Functions . 1

Services . 1

Access levels . 1

Reporting . 1

Calling SQL Stored Procedure in the AUA Framework . 1

Working with SQL Function in AUA Framework . 1

Working with SQL Views in the AUA Framework . 1

Message Provider in the AUA framework . 1

Adding Message Provider to View.csHtml 1

Displaying messages as HtmlMessages . 1

Displaying messages as DialogMessages . 1

Controlling the Exception Handling Error in the AUA Framework . 1

Schema of Tables in the AUA Framework . 1

Working with the Setting File in the AUA Framework . 1

Rest WebApi in the AUA Framework . 1

 

 

 

 

Abstract

Software projects require constant changes and updates. If the structure develops in the wrong way, it will prevent changes and extensions, and most of the time will lead to task duplication or rewriting of the project from scratch. To get rid of the complexity and task duplication that most programmers and developers face, which is also caused by the inconsistency of code at different levels of the program, we need a simple consistent structure for writing software projects so that we can hide some of the complexity and focus on business of the task. For example, the Bootstrap framework is a very useful framework for Front End, but few people would prefer to use frameworks like Bootstrap for design, and write all of their design with CSS from the beginning. For the Back End section, however, a simple, general-purpose framework can save time and cost and produce high-quality code and a uniform architecture. This framework allows developers to develop their projects based on an appropriate integrated pattern. The framework must be flexible enough to allow the programmer to apply any changes needed, relying on its robust structure.

Why Framework?

One of the problems of software companies is the lack of the right structure for developing their projects. As a result, they have often produced such complex and nested codes that creating changes in one part of the project severely affects or disrupts other parts. Therefore, lack of the right structure for development makes it impossible to update the previous code and reduces the efficiency of the team to almost zero. The reason for this is the difference in coding and lack of structure and architecture. The development team must first agree on a set of rules and structures. Architectural patterns are not the result of a programmer's experiences; they have resulted from the experiences of hundreds of programmers and design professionals over years. These frameworks are not innovations or inventions, but are feedbacks on redesign and recoding that programmers have been involved with in order to achieve the highest levels of flexibility, extensibility and reusability. Their use makes the produced codes more simple, flexible and extensible. The use of a framework can help us save time and cost and make it easier to document and maintain the system.

Asp.Net Unique Architecture (AUA) Framework

AUA is a simple, lightweight framework for producing projects of any size (small and large). It can be applied in all architectures (Micro service, CQRS, etc.) due to its transparency in structure. It is also full of different design patterns, thus a great source for software architects and developers.

AUA Framework's Versions:

AUA Framework's Overall Structure

The different layers of the AUA framework are as follows:

Layer's Name

Use

Common Layer

This layer contains common items used in other layers, such as

Enums, Consts, Extensions, ، Tools

Data Layer

This layer contains items associated with the data source, including Entity Framework Context, Db Extensions, Search Filters, Unit of Work Pattern, Configuration Tools, and Dapper Context

 

Domain Entity Layer

This layer contains the entities and their configuration.

Models Layer

This layer contains DTOs, View Models and Config mapping:

EntitiesDto, ReportModels, View Models ,

Service Infrastructure Layer

The overall infrastructure of Services and Repository is written and becomes ready for use in this layer.

Service Layer

This layer includes all the business services of your project, including BaseServices, BusinessService, EntitiesService, ReportService, etc.

WebApi or Ui Mvc Layer

This is an interface user layer that can be written with General MVC- WebApi- GraphQl- Grapc.

Test Layer

This layer is designed for writing Unit Tests (ToDo)

External web service Layer

This layer is for calling external services. (ToDo)

 

 

 

 

 

 

 

 

 

 

 

 

 

Adding New Entity

Entity is the main concept, and indeed at the heart of the architecture of, the AUA framework. Each entity is defined by a class that contains its features and its relation to other entities. Each entity has an identifier that can be of any Data Type allowed in .NET, or it can be a combination of two or more Data Types allowed therein (combination key).

Entity Class

 

Each entity inherits from the DomainEntity class, to which a primary key field called Id and one or more monitoring fields (depending on the setting type) are added.

public class Student : DomainEntity

{

public string FirstName { get; set; }

 

public string LastName { get; set; }

 

public int Age { get; set; }

}

 

It should be specified if the primary key has a data type other than the int data type (e.g. the Long data type is considered under the primary key)

 

public class Student : DomainEntity<long>

{

public string FirstName { get; set; }

 

public string LastName { get; set; }

 

public int Age { get; set; }

}

 

By default the following fields are added to each entity.

The Id key of the primary key and its data type can be specified when defining an entity.

The IsActive field shows whether the entity is active or inactive and it has a bool data type.

The RegDate displays the date and time the entity is created (automatically created inside SQL Server) and does not need to be filled in and sent.

 

public class DomainEntity<TPrimaryKey> : BaseDomainEntity<TPrimaryKey>, IAuditInfo

{

public DateTime RegDate { get; set; }

 

}

 

public class BaseDomainEntity<TPrimaryKey> : IDomainEntity<TPrimaryKey>

{

 

public TPrimaryKey Id { get; set; }

 

public bool IsActive { get; set; }

 

}

 

 

The AUA framework is open-code and can be easily customized.

Monitoring fields:

You can add more monitoring fields to the entities if you wish depending on your business.

Monitoring Field Creating the ICreationAudited Entity

public interface ICreationAudited

{

long CreatorUserId { get; set; }

 

}

 

To add the monitoring field of CreatorUserId, we can simply implement the ICreationAudited interface for the DomainEntity class, as follows:

public class DomainEntity<TPrimaryKey> : BaseDomainEntity<TPrimaryKey>, IAuditInfo, ICreationAudited

{

public DateTime RegDate { get; set; }

 

public long CreatorUserId { get; set; }

 

}

 

Auditing fields of deleting the IDeletionAudited entity

The IDeletionAudited interface can be used to prevent the physical deletion and add the monitoring fields of entity deletion.

 

public interface IDeletionAudited: ISoftDelete

{

long? DeleterUserId { get; set; }

 

DateTime? DeleteDate { get; set; }

 

}

 

public interface ISoftDelete

{

bool IsDeleted { get; set; }

 

}

 

 

Auditing fields for editing IModifiedAudited

The IModifiedAudited interface can be used to add monitoring fields for editing an entity.

 

public interface IModifiedAudited

{

 

long? ModifierUserId { get; set; }

 

DateTime? ModifyDate { get; set; }

 

}

 

Configuration of entities:

There is a configuration class for each entity that can specify the length of field settings for it.

 

public class StudentConfig : IEntityTypeConfiguration< Student>

{

public void Configure( EntityTypeBuilder<Student> builder)

{

 

builder

.Property(p => p.FirstName)

.HasMaxLength( LengthConsts.MaxStringLen50);

 

builder

.Property(p => p.LastName)

.HasMaxLength( LengthConsts.MaxStringLen50);

 

}

}

 

We configure the entity with the combination key as follows. The AppUserId and RoleId fields are both

Keys to the UserRole entity

We configure the entity with the combination key as follows. The AppUserId and RoleId fields are both keys to the UserRole entity

 

public class UserRoleConfig : IEntityTypeConfiguration<UserRole>

{

public void Configure(EntityTypeBuilder< UserRole> builder)

{

 

builder.Ignore(p => p.Id);

 

builder

.HasKey(p => new { p.AppUserId, p.RoleId });

 

}

 

{

 

Models and Mapping:

Models inherit the BaseEntityDto class at AUA, and the monitoring and Id fields are added automatically to them. The AUA framework has two mapping methods of IMapFrom and IHaveCustomMappings to map one object to another. In the IMapFrom model, only fields with the same name are mapped, and there is no mapping for those with different names. This type of mapping is the simplest and fastest type of mapping. In the example below, a Student entity is mapped to a StudentDto, as follows:

public class StudentDto : BaseEntityDto, IMapFrom<Student>

{

[Display(Name = "First Name ")]

public string FirstName { get; set; }

 

[Display(Name = "Last Name")]

public string LastName { get; set; }

 

[Display(Name = "Age")]

public int Age { get; set; }

 

public string FullName => $"{FirstName} {LastName}";

}

 

IHaveCustomMappings Method

In the IHaveCustomMappings model, not only fields with the same name, but also each field of the source model can be mapped to the Linq commands. In this case, your map includes configuration, which is performed at the bottom of the model. This method is very flexible. Anything that can be written with the Linq commands for an entity can be written with this type of mapping. Below is a mapping example that maps an object from AppUser to AppUserDto. In addition, access levels and roles are mapped. In the reporting section, we will use complex mapping for reporting.

 

public class AppUserDto: BaseEntityDto<long>, IHaveCustomMappings

{

public string FirstName { get; set; }

 

public string LastName { get; set; }

 

public string UserName { get; set; }

 

public string FullName => FirstName + " " + LastName;

 

public string Password { get; set; }

 

public string Phone { get; set; }

 

public string Email { get; set; }

 

public bool IsActive { get; set; }

 

public virtual ICollection<UserRole> UserRoles { get; set; }

 

public void ConfigureMapping(Profile configuration)

{

configuration.CreateMap< AppUser, AppUserDto>()

.ForMember(p => p.UserRoles, p => p.MapFrom(q => q.UserRoles))

.ReverseMap();

}

}

 

ConvertTo and ProjectTo Functions

To help with mapping operations, you can convert an object or list of objects into one or a list of view models without any restrictions, and use IMapFrom and IHaveCustomMappings to configure mapping operations (MapperInstance is by default an instance of AutoMapper in all services). For example, we have created a view model called TestMappingVm that uses IHaveCustomMappings to configure mapping operations, thereby mapping a query of the AppUser entity to this view model using ConvertTo and ProjectTo.

public class TestMappingVm : IHaveCustomMappings

{

public string Email { get; set; }

 

public string PersonName { get; set; }

 

public int RoleCount { get; set; }

 

public ICollection<Role> UserRoles { get; set; }

 

public void ConfigureMapping(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.UserRoles, p => p.MapFrom(q => q.UserRoles.Select(m => m.Role)))

.ReverseMap();

}

 

}

 

 

Mapping using ProjectTo:

public IEnumerable<TestMappingVm> GeTestMappingVms()

{

var query = GetAll().Where(p => p.IsActive);

 

return MapperInstance

. ProjectTo<TestMappingVm>(query)

.ToList();

 

}

 

Mapping using ConvertTo

 

public IEnumerable<TestMappingVm> GeTestMappingVms()

{

var query = GetAll().Where(p => p.IsActive);

 

 

return query

. ConvertTo<TestMappingVm>(MapperInstance)

.ToList();

}

 

One of the most important features of the AUA framework is its high security. Security and performance have always been in conflict. The Heilton security team has worked hard to design a very secure way to raise security while maintaining the performance and has used it in the AUA framework.

Each view model and DTO inherit from the generic class BaseEncryptionVm <TPrimaryKey>, and two EncKeyId and DecKeyId fields are added to it.

 

 

EncKeyId is the encrypted equivalent of the primary key (Id). The encryption key is generated for each different user. If the programmer specifies this field in its Select command, it will produce its framework; otherwise, it will not produce it.

 

DecKeyId is equivalent to Id.

In addition, the DecKeyId function has been considered in the BaseController. It receives EncKeyId and converts it to Id, and the programmer is not involved in encryption and hash algorithms under any circumstances (however, this feature can be avoided and that id may be used instead).

 

 

public async Task<IActionResult> _Update(string keyId)

{

var userId = DecKeyId < long >(keyId);

 

var model = await _appUserService.GetAppUserVmAsync(userId);

 

 

return View(model);

}

 

Services

All business is implemented in the form of services and created in the service layer. The service layer uses the Service Infrastructure layer and automatically connects to each service in its own Repository. The advantage of this approach is that the developer is not involved in the two concepts of repository and service and focuses only on the service itself. The service has its own built-in Repository, which is one of the most important features of the AUA framework architecture. For example, if we want to write a service for Student Entity, we must first create an interface for Student Entity which inherits from the IGenericEntityService class.

public interface IStudentService : IGenericEntityService<Student, StudentDto>

{

 

}

 

After Interface you can create the desired service. The service must inherit from the GenericEntityService class and implement the IStudentService interface built in the previous step.

public class StudentService: GenericEntityService<Student,StudentDto>,IStudentService

{

public StudentService(IUnitOfWork unitOfWork) : base(unitOfWork)

{

}

 

}

 

 

By default, the service created contains all the functions required to work with Repository.

List of Repository functions that are automatically added to each service. .

Function

Description

GetAll

This returns all entities and can be filtered. It also supports Async.

GetAllDto

This returns all entities in DTO format and can be filtered. It also supports Async.

GetCount

Number of entities - can be filtered.

 

GetFirst

This returns the first entity and can be filtered.

GetLast

This returns the last entity and can be filtered.

GetCountAsync

Number of entities filterable; supporting Async

 

GetFirstAsync

This returns the first entity and can be filtered; supporting Async

GetLastAsync

This returns the last entity and can be filtered; supporting Async

GetDtoById

Holding entity and mapping it in DTO format

 

GetByIdAsync

Holding entity with the primary key; supporting Async

GetDtoByIdAsync

Holding entity and mapping it in DTO format; supporting Async

 

Delete

Deleting entity with the primary key or DTO

 

 

DeleteAsync

Deleting entity with the primary key or DTO; supporting Async

 

 

 

Insert

Inserting new entity with Entity or DTO

 

InsertAsync

Inserting new entity; supporting Async

 

InsertMany

Inserting multiple entities simultaneously

InsertManyAsync

Inserting multiple entities simultaneously; supporting Async

 

InsertCustomVm

Inserting entity with custom view model (when part of entity fields is sent from view)

 

 

InsertCustomVmAsync

Inserting entity with custom view model ; supporting Async

 

PartialInsert

Inserting entity into repository without sending to database; supporting Async

 

 

Update

Editing new entity with Entity or DTO

 

 

UpdateAsync

Editing new entity with Entity or DTO ; supporting Async

 

UpdateCustomVm

Editing entity with custom view model (when part of entity fields is sent from view)

 

 

UpdateCustomVmAsync

Editing entity with custom view model (when part of entity fields is sent from view)

 

 

PartialUpdate

Inserting entity into repository without sending to database; supporting Async

 

 

ConvertTo

This converts a query result to another object based on configuration mapping

 

 

ProjectTo

This projects a query result to another object based on configuration mapping

 

 

SaveChange

This specifies the final status when using Partial Functions.

 

 

SaveChangeAsync

This specifies the final status when using Partial Functions; supporting Async

 

 

The developer can implement his business into the services. One service can use other services.

You can easily inject services into another and use it.

Access levels

In the AUA framework, you as administrator can control the users' access to the smallest possible level. This is done by the user management module. You can assign roles to users by defining them and assigning User Access levels to them. The diagram below shows the levels of access to the AUA Word Framework.

.

Accounting

There are four models of Authentication and Authorization in the AUA framework and it is possible to specify the level of access in the controller and action.

1. WebAuthorize: This attribute allows you to specify access levels for the action and controller.

2. AllowLoggedInUser: The user has just to log in.

3. OnlyLocalActionAuthorize: This attribute is for actions and controllers that only need to be called locally on the server.

4. AllowAnonymousAuthorize: Any user can have unlimited access to this action and controller

To use WebAuthorize, you must add an item for each control or action in EUserAccess. In the database in the UserAccess table we add the name, access number, description and URL.

 

 

 

public enum EUserAccess

{

#region Accounting

 

[ Description(" User Management ")]

AppUser = 1,

 

[ Description(" Access level management ")]

UserAccess = 2,

 

[ Description(" Role management ")]

UserRole = 3,

 

[ Description(" Role-level access management ")]

UserRoleAccess = 4,

 

#endregion

}

 

 

 

 

To use WebAuthorize, you need to write it for the controller or action. For example, the following action is accessible only for users who have access to AppUser = 1.

 

[ WebAuthorize ( EUserAccess . AppUser )]

public async Task<IActionResult > _Insert(AppUserDto appUserDto)

{

await _appUserService.InsertAsync(appUserDto);

 

return RedirectToAction("Index");

}

 

 

WebAuthorize input can have several levels of access. That is, the user has access to that resource if he has access to one of these.

The page title is loaded by default according to the description in the UserAccess table for the page.

 

Reporting

One of the most important features of software is reporting with different capabilities and one of the concerns of programmers is the addition and modification of filters which is performed in the AUA framework easily and at a high speed and you can create your own report with various filters. To create a report, we first make a view model to apply filters and another view model to display the output and finally write our search filter to apply the filters.

For example, we create a report of all users and all their access.

View Model of Search Filter:

public class UserAccessReportSearchVm : BaseSearchVm

{

public string FirstName { get; set; }

 

public string LastName { get; set; }

 

public string UserName { get; set; }

 

public bool ? IsActive { get; set; }

 

public List<SelectListItem> RecordStatusItem { get; set; }

 

public string RoleTitle { get; set; }

 

public string UserAccessTitle { get; set; }

}

 

 

View Model of Report output:

 

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; }

 

public 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();

}

 

}

 

 

 

After creating the search view and grid view of the model, we write a search filter to apply the filters.

 

 

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));

}

 

 

private void ApplyIsActiveFilter()

{

if (_searchVm.IsActive == null)

return;

 

_expression = _expression.And(p => p.IsActive == _searchVm.IsActive);

}

 

private void ApplyRoleTitleFilter()

{

if (string .IsNullOrWhiteSpace(_searchVm.RoleTitle))

return;

 

_expression = _expression.And(p => p.UserRoles.Any(r => r.Role.Title.Contains(_searchVm.RoleTitle)));

}

 

private void ApplyUserAccessTitleFilter()

{

if (string .IsNullOrWhiteSpace(_searchVm.UserAccessTitle))

return;

 

_ expression = _expression.And(p => p.UserRoles.Any(r =>

r.Role.UserRoleAccess.Any(a =>

a.UserAccess.Title.Contains(_searchVm.UserAccessTitle))));

}

 

 

 

The output of this report can be viewed as follows.

 

 

Calling SQL Stored Procedure in the AUA Framework

 

Most of the time, the programmer resorts to other ways like Ado.net and Dapper to easily work with the SQL Stored Procedure , not knowing that he can simply manage the number of connections made or create a separate context for it. SQL Stored Procedure is called easily in the AUA Framework and has none of the above problems.

Example: Procedure call that gives the list of a user's access in the AUA framework is as follows:

CREATE PROCEDURE [dbo].[uspGetUserRoles]

 

@userId bigint

 

AS

 

SELECT ROLE.Id as RoleId,ROLE.Title,ROLE.Description

FROM UserRole INNER JOIN ROLE

ON UserRole.RoleId=ROLE.Id

 

WHERE UserRole.AppUserId=@userId

 

We create a view model for the output of the procedure.

 

public class GetUserRolesSpResult

{

public int RoleId { get; set; }

 

public string Title { get; set; }

 

public string Description { get; set; }

 

}

 

 

To call a procedure in StoredProcContext , we add the following code.

public IQueryable<GetUserRolesSpResult> GetUserRolesSp(long userId)

{

var cmd = LoadStoredProc(StoredProcedureConsts. GetUserRoles )

.WithSqlParam( "userId", userId);

 

return cmd

.ExecuteStoredProc<GetUserRolesSpResult>();

 

}

 

 

To avoid the dispersal of the procedures' names, we place them in the class in a fixed order.

 

 

public class StoredProcedureConsts

{

public const string GetAppUsersCount = "uspGetAppUsersCount" ;

 

public const string GetUserRoles = "uspGetUserRoles";

 

}

 

 

The framework allows you to query the output of the procedure, but this is not correct, as it should write its own procedure for each task. The StoredProcService service, which includes all procedures, allows you to call your own processor.

public class StoredProcService : FuncBaseService, IStoredProcService

{

public StoredProcService(IUnitOfWork unitOfWork) : base(unitOfWork)

{

}

public IQueryable<GetUserRolesSpResult> GetUserRolesSp(long userId)

{

return UnitOfWork

.StoredProc

.GetUserRolesSp(userId);

 

}

 

}

 

 

Working with SQL Function in AUA Framework

One of the good features of the AUA framework is that it works with functions in the SQL Server to map their result to objects, and to write a LINQ query on their results, which can easily be done in AUA frameworks and it avoids generating a dirty code for this task.

For example: We call with the AUA framework a function that takes the user code and returns its name.

 

 

CREATE FUNCTION [dbo] .[GetUserName] (@userId BIGINT)

RETURNS NVARCHAR(200) AS

BEGIN

RETURN (SELECT UserName FROM AppUser WHERE Id=@userId)

END

 

 

In SqlFunctionContext, you can add a new function call.

 

 

public class SqlFunctionContext

{

 

private readonly DbContext _dbContext;

 

public SqlFunctionContext(DbContext dbContext)

{

_dbContext = dbContext;

}

 

public string GetUserName( long userId)

{

var resultParameter = new SqlParameter("@result", SqlDbType.NVarChar, 200)

{

Direction = ParameterDirection.Output

};

ExecuteSqlCommand( $"SET @result= dbo.GetUserName(' {userId}')",resultParameter);

 

return resultParameter.Value as string;

 

}

 

private void ExecuteSqlCommand( string sqlCommand, IDbDataParameter resultParameter)

{

_dbContext?

.Database?

.ExecuteSqlCommand(sqlCommand, resultParameter);

 

}

 

}

 

 

In the SqlFunctionService service, the function can be accessed and called.

.

public class SqlFunctionService : FuncBaseService, ISqlFunctionService

{

public SqlFunctionService(IUnitOfWork unitOfWork) : base(unitOfWork)

{

}

 

public string GetUserName( long userId)

{

return UnitOfWork

.SqlFunction

.GetUserName(userId);

}

 

 

}

 

 

Working with SQL Views in the AUA Framework

One of the concerns of .NET programmers is working with SQL Views so that they can map their results in objects and apply filters on the outputs of the view. The Entity Framework recognizes the views as a table, but the AUA framework makes it possible to map the view output to the objects and apply a filter to it.

CREATE VIEW [dbo].[UserRolesVw] AS

 

SELECT AppUser.Id AS userId,AppUser.UserName,Role.Title

FROM AppUser INNER JOIN UserRole

ON AppUser.Id=UserRole.AppUserId

INNER JOIN Role

ON UserRole.RoleId=Role.Id

 

GO

For the view output, we write a class that inherits from BaseView.

public class UserRolesVw : BaseView

{

public long UserId { get; set; }

 

public string UserName { get; set; }

 

public string Title { get; set; }

}

 

 

We also make a View DTO for the View output (if we want to create a change in the View output, we apply it to the DTO).

public class UserRolesVwDto : IMapFrom<UserRolesVw>

{

public long UserId { get; set; }

 

public string UserName { get; set; }

 

public string Title { get; set; }

 

}

 

 

We also create a service to work with the view, where filters will be written for the view.

 

Interface:

public interface IUserRolesVwService : IBaseGenericService<UserRolesVw, UserRolesVwDto>

{

 

}

 

View Service:

public class UserRolesVwService : BaseGenericService< UserRolesVw, UserRolesVwDto>, IUserRolesVwService

{

public UserRolesVwService(IUnitOfWork unitOfWork) : base(unitOfWork)

{

 

}

 

}

 

Functions that are added to the view service by default are as follows:

GetAll

This returns all entities and can be filtered. It also supports Async.

GetAllDto

This returns all entities in DTO format and can be filtered. It also supports Async.

GetCount

Number of entities - can be filtered.

 

GetFirst

This returns the first entity and can be filtered.

GetLast

This returns the last entity and can be filtered.

GetCountAsync

Number of entities filterable; supporting Async

 

GetFirstAsync

This returns the first entity and can be filtered; supporting Async

GetLastAsync

This returns the last entity and can be filtered; supporting Async

GetDtoById

Holding entity and mapping it in DTO format

 

GetByIdAsync

Holding entity with the primary key; supporting Async

GetDtoByIdAsync

Holding entity and mapping it in DTO format; supporting Async

 

ConvertTo

This converts a query result to another object based on configuration mapping

 

 

ProjectTo

This projects a query result to another object based on configuration mapping

 

 

Message Provider in the AUA framework

The AUA framework contains two types of Message Box, namely Html Messagebox and Dialog Messagebox.

If we use HtmlMessages in View.csHtml, messages will be displayed in Html in the right color, if we use DialogMessages, they will be displayed in dialog, and if both models are loaded in the view, they will be displayed as Html and dialog.

Messagebox Types:

NotifyMessage

SuccessMessage

ErrorMessage

WarningMessage

Message

Adding Message Provider to View.csHtml

<div class="form-group">

< partial name ="MessageProvider/HtmlMessages/_AllHtmlMessage" />

</div>

 

@* OR *@

 

<div class="form-group">

< partial name ="MessageProvider/DialogMessages/_AllDialogMessage" />

</div>

 

In the controller, we can easily send the message to the view and display it.

public IActionResult Index()

{

NotifyMessage("** NotifyMessage **");

SuccessMessage("** SuccessMessage **");

ErrorMessage("** ErrorMessage **");

WarningMessage( "** WarningMessage **" );

Message( "** Message **");

 

return View();

}

 

 

Displaying messages as HtmlMessages

 

 

 

Displaying messages as DialogMessages

Controlling the Exception Handling Error in the AUA Framework

One of the most important modules of the AUA framework is its error control module. This framework is perfect at error control and management. For example: When a user enters one of the actions, an expression occurs but the error page does is not displayed and the error text is displayed as a controlled message and the URL does not change.

Anywhere we want to interrupt request processing a request, we can throw an Exception and interrupt the process, in which case the error is sent to the error control module.

For example: If the user is not logged in, the request processing should be stopped and an error will be displayed.

public IActionResult Index()

{

if (CurrentUser is null)

throw new ClientException("Custom Error Exception" );

 

{

public IActionResult Index()

{

if (CurrentUser is null )

throw new ClientException("Custom Error Exception");

 

{

 

 

Error management module and displaying it as a message

Schema of Tables in the AUA Framework

One of the security issues in SQL Server is the schema of tables. By default, the names of tables are stored with the dbo prefix in the SQL Server. It is easily possible to customize this schema in the AUA framework.

To manage and prevent the dispersal of constants in the program, we write them in the classes in a fixed order.

public class SchemaConsts

{

 

public const string Accounting = "Acc";

 

public const string School = "Sch";

 

}

[ Table ( "Student" , Schema = SchemaConsts . School )]

public class Student : DomainEntity

{

public string FirstName { get; set; }

 

public string LastName { get; set; }

 

public int Age { get; set; }

 

}

 

Results of schema application in database

Working with the Setting File in the AUA Framework

One of the dirty codes found in most projects is how to read values from files like AppSetting.json, WebConfig, and setting files in ASp.net Core, which in most cases causes errors. In the AUA framework with appropriate design patterns, we read these files in Json, Xml. There are two types of setting files in Json and Xml formats (Setting.xml, and appsettings.json).

The following code uses the appsettings.json file to read the value of the encryption key

public class AppSetting

{

 

public static string GetDataEncryptionKey =>

AppConfiguration

.GetConfigurationRoot()

.GetSection(AppSettingConsts.AppSetting)

.GetSection(AppSettingConsts.DataEncryptionKey)

.Value;

}

 

To read the value of the encryption key from the configuration file:

Var dataEncryptionKey= AppSetting.GetDataEncryptionKey

 

 

To work with the XML setting file, we have used the Xml Pattern

https://github.com/Heilton/XmlPattern

Rest WebApi in the AUA Framework

API is a software implementation interface that allows other applications to interact with it. It enables us to implement HTTP protocol-based services more easily than ever. Many service receivers, such as web browsers, mobile devices, and desktop applications, can take advantage of the capabilities of this platform (Web Api).

In the last layer of the AUA framework, we can use different technologies such as the MVC - Rest WebApi-Grpc- Graphql. JWT (Json web Token) technology can be implemented and used for WebApi access levels in the AUA framework.

Swagger is used for ease of use and WebApi testing. All the requirements for login and access levels using Token for the AUA framework are written in WebApi version and are ready for your business development.

Note that the AUA framework can also be used for Windows form application, WPF, etc., because the last layer changes. The AUA framework is beyond the scope of this document. This document is just aimed at familiarizing you with how to work with the AUA framework. Using this document and tutorial videos, the programmer, regardless of his/her level, can work with this framework within just a week.

The AUA framework has tutorial videos for all stages and can be easily worked with. You can download the tutorial video on how to work with the AUA framework from the Hilton website at www.heilton.com or www.youtube.com .

Contact Information

Email WebSite Phone
AUA_Framework@yahoo.com
mr_Lotfi@ymail.com
www.Heilton.com +98-9199906342
Persian Document