Skip to content

AUAFramework/WebApi_DotNet6

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 

Repository files navigation

Asp.Net Unique Architecture Framework (AUA)

Asp.net web Api .NET 6

Visit auaframework.com! and more document

Visit auaframework.com! and more Video

AUA WebApi Swagger-UI

For the web api version you can use angular, react, vue.js or .... javascript front framework.

Asp.Net Unique Architecture

Introduction AUA

Using the AUA ( Asp.Net Unique Architecture ) Framework, you can easily have better, faster, and more orderly and focused coding. This framework is based on new and up-to-date concepts, structures and architectures, including: Clean Architecture, Clean Code, Domain-driven design (DDD), SOLID Principle, Code Refactoring, GRASP (object-oriented design principle). 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.

AUA Features

Well Design Clean Architecture, Clean Code, Code Refactoring, Domain-driven design (DDD)
Performance Use the latest methodology and tools to speed up and produce modern web applications.
Simplicity Quick and satisfactory way of producing a database-driven web application.

AUA Structure

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. Software architecture means the structure and organization of a software system that is centralized to support specific operations. In fact, the components are grouped in related domains and exchange and interact with other related domains. Layering reduces the complexity and simplification of development and facilitates maintenance and modification. The layering and structure of the project depends on the nature of the project business. Factors such as the size of the project and the business can change the structure of the project. The number of layers and the separation of layers are different in each project. Below we introduce the main layers of the AUA framework in general.

Asp.Net Unique Architecture Framework (AUA)

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.
  • Domain Driven Design (DDD)
  • EF 6 and EF Core .net 6
  • Based on SOLID Principles
  • Modular design
  • Modular design
  • Layered architecture

Naming Pattern

Every company or project should have its own naming pattern to help us easily understand the previous concepts and add new concepts to the project. Good and consistent naming creates a good flow in your project, thereby increasing code readability and durability. An important feature of a good framework is having a good naming pattern, which all programmers should follow. In this framework, all components are named, such as: folder, file, class, function, variable, etc.

"You should name a variable with the same care we do in naming a first-born child" (Uncle Bob in Clean Code). 

Each team must use a specific naming pattern, such that if any file is separated from the project, all programmers can specify the exact address of the file from the file name. For the AUA framework, we have suggested the following naming pattern.

Naming Layers

The company name should be 3 or 4 characters short for the company name. For example, Heilton uses the following naming pattern for its store project for its layers:

Hil.Shop.Common
Hil.Shop.DataLayer
Hil.Shop.DomainEntities
Hil.Shop.Models
Hil.Shop.ExternalService
Hil.Shop.ExternalServiceProvider
Hil.Shop.InMemoryService
Hil.Shop.Logger
Hil.Shop.Service
Hil.Shop.ServiceInfrastructure
Hil.Shop.FileServer
Hil.Shop.Tests
Hil.Shop.ValidationService
Hil.Shop.WebApi
For example, for the entity of Person, we do naming in this way:

Entity: Person Data Transfer Object(DTO): PersonDto

We can use DTO at the input and output of Api and use DTO instead of sending and receiving to Entity . DTO can be considered larger and equal to Entity. Web Api output and input use DTO instead of Entity. We must have at least one DTO for each entity because the services are designed to take an entity as input when creating an entity.

View Model: PersonVm

For view models, AUA uses the acronym view model, or VM . Of course, if more than one view model is required for an entity, the following combination can be used: ControllerName + ActionName + Vm. For example, for the following control and action, this name is suggested: PesronInsertVm

AUA Framework's Overall Structure

The different layers of the AUA framework are as follows:
  • 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 LayerThis 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)
<h2>Adding New Entity</h2>

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

e 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 , 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..

  • GetAllThis returns all entities and can be filtered. It also supports Async.
  • GetAllDtoThis returns all entities in DTO format and can be filtered. It also supports Async.
  • GetCountNumber of entities - can be filtered.
  • GetFirstThis returns the first entity and can be filtered.
  • GetCountAsyncNumber of entities – filterable; supporting Async
  • GetFirstAsyncThis returns the first entity and can be filtered; supporting Async
  • GetLastAsyncThis returns the last entity and can be filtered; supporting Async
  • GetDtoByIdHolding entity and mapping it in DTO format
  • GetByIdAsyncHolding entity with the primary key; supporting Async
  • GetDtoByIdAsyncHolding entity and mapping it in DTO format; supporting Async
  • DeleteDeleting entity with the primary key or DTO
  • DeleteAsyncDeleting entity with the primary key or DTO; supporting Async
  • InsertInserting new entity with Entity or DTO
  • InsertAsyncInserting new entity; supporting Async
  • InsertManyInserting multiple entities simultaneously
  • InsertManyAsyncInserting multiple entities simultaneously; supporting Async
  • InsertCustomVmInserting entity with custom view model (when part of entity fields is sent from view)
  • InsertCustomVmAsyncInserting entity with custom view model; supporting Async
  • PartialInsertInserting entity into repository without sending to database; supporting Async
  • UpdateEditing new entity with Entity or DTO
  • UpdateAsyncEditing new entity with Entity or DTO; supporting Async
  • UpdateCustomVmEditing entity with custom view model (when part of entity fields is sent from view)
  • UpdateCustomVmAsyncEditing entity with custom view model (when part of entity fields is sent from view)
  • PartialUpdateInserting entity into repository without sending to database; supporting Async
  • ConvertToThis converts a query result to another object based on configuration mapping
  • ProjectToThis projects a query result to another object based on configuration mapping
  • SaveChangeThis specifies the final status when using Partial Functions.
  • SaveChangeAsyncThis 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.
0

  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()
        {
        //--ToDo
        }
        
        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))));
        }

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

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:

  • GetAllThis returns all entities and can be filtered. It also supports Async.
  • GetAllDtoThis 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.

  • GetCountNumber of entities - can be filtered.
  • GetFirstThis returns the first entity and can be filtered.
  • GetLast

    This returns the last entity and can be filtered.

  • GetCountAsyncNumber of entities – filterable; supporting Async
  • GetFirstAsyncGetFirstAsync
  • GetLastAsyncThis returns the last entity and can be filtered; supporting Async
  • GetDtoByIdHolding entity and mapping it in DTO format
  • GetByIdAsyncHolding entity with the primary key; supporting Async
  • GetDtoByIdAsyncHolding entity and mapping it in DTO format; supporting Async
  • ConvertToThis converts a query result to another object based on configuration mapping
  • ProjectToThis projects a query result to another object based on configuration mapping

Visit auaframework.com! and more document

Visit auaframework.com! and more Video

About

Asp.Net Unique Architecture Dot Net 6 (Web Api )

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages