ASP.NET Core Web API - How to Select Records based on logged in user role using DB Identity

Viewed 47

In ASP.NET Core-6 Web API Entity Framework Db Identity, I am implementing Role Based Application.

Each User can only have just one Role.

There are there roles:

Admin
Teacher
Student

If Role is Student, the application should query:

var examScores = _dbContext.Scores
            .Where(m => (bool)m.Approval.IsFirstLevel == false)
            .Where(x => string.IsNullOrEmpty(filter.SearchQuery)
        || x.Subject.ToLower().Contains(filter.SearchQuery.ToLower())
        || x.StartDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower())
        || x.EndDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower()))
            .OrderByDescending(x => x.CreatedAt);
return examScores;

If Role is Teacher, the application should query:

var examScores = _dbContext.Scores
            .Where(m => (bool)m.Approval.IsSecondLevel == false)
            .Where(x => string.IsNullOrEmpty(filter.SearchQuery)
        || x.Subject.ToLower().Contains(filter.SearchQuery.ToLower())
        || x.StartDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower())
        || x.EndDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower()))
            .OrderByDescending(x => x.CreatedAt);
return examScores;

If Role is Admin, the application should query:

var examScores = _dbContext.Scores
            .Where(m => (bool)m.Approval.IsFirstLevel && (bool)m.Approval.IsSecondLevel)
            .Where(x => string.IsNullOrEmpty(filter.SearchQuery)
        || x.Subject.ToLower().Contains(filter.SearchQuery.ToLower())
        || x.StartDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower())
        || x.EndDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower()))
            .OrderByDescending(x => x.CreatedAt);
return examScores;

So far, this is what I have done:

Controller:

public async Task<ActionResult<Response<PageResult<IEnumerable<ScoreListDto>>>>> GetExamScoreAsync([FromQuery] PagingFilter filter)
{
    var result = await _examScoresService.GetExamScoreAsync(filter);

    return StatusCode(result.StatusCode, result);
}

Service:

   public async Task<Response<PageResult<IEnumerable<ScoreListDto>>>> GetScoreAsync(PagingFilter filter)
    {
        var score = await _unitOfWork.ExamScore.GetExamScoreAsync(filter);
        var item = await score.PaginationAsync<Score, ScoreListDto>(filter.PageSize, filter.PageNumber, _mapper);
        return new Response<PageResult<IEnumerable<ScoreListDto>>>()
        {
            StatusCode = (int)HttpStatusCode.OK,
            Successful = true,
            Data = item
        };
    }

Repository:

public async Task<IQueryable<Score>> GetExamScoreAsync(PagingFilter filter)
{
        var userName = _currentUserService.UserName;
        var getUser = await _userManager.FindByNameAsync(userName);
        var loggedUserRole = await _userManager.GetRolesAsync(getUser);
        var loggedUserRoleName = loggedUserRole[0].ToString();
}

This is the flow of the application

Repository -> Service -> Controller

How do I implement var examScores into my code based on (condition) the role of the logged in user?

Thanks

1 Answers

Since your queries as far as I can see only vary on the first Where clause you could do either;

1)

Build the query by chaining commands to an IQueryable<Score>.

var query = loggedUserRoleName switch {
    "Student" => _dbContext.Scores.Where(m => (bool)m.Approval.IsFirstLevel == false),
    "Teacher" => _dbContext.Scores.Where(m => (bool)m.Approval.IsSecondLevel == false),
    "Admin" => _dbContext.Scores.Where(m => (bool)m.Approval.IsFirstLevel && (bool)m.Approval.IsSecondLevel),
    _ => throw new ArgumentException($"Unknown role {loggedUserRoleName}")
};

return query.Where(x => string.IsNullOrEmpty(filter.SearchQuery)
        || x.Subject.ToLower().Contains(filter.SearchQuery.ToLower())
        || x.StartDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower())
        || x.EndDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower()))
            .OrderByDescending(x => x.CreatedAt);

Build the query using a Dictionary<> having the role as key and filter expression as value.

Dictionary<string, Expression<Func<Score, bool>>> filters = new() {
    {"Student", m => (bool)m.Approval.IsFirstLevel == false},
    {"Teacher", m => (bool)m.Approval.IsSecondLevel == false}
    {"Admin", m => (bool)m.Approval.IsFirstLevel && (bool)m.Approval.IsSecondLevel}
};
return _dbContext.Scores
            .Where(filters[loggedUserRoleName])
            .Where(x => string.IsNullOrEmpty(filter.SearchQuery)
        || x.Subject.ToLower().Contains(filter.SearchQuery.ToLower())
        || x.StartDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower())
        || x.EndDate.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture).Contains(filter.SearchQuery.ToLower()))
            .OrderByDescending(x => x.CreatedAt);
Related