woggles
woggles

Reputation: 7444

MVC 5 global variable per user

I've found that I have a lot of repeated code in all of my actions, and want to know the best way to avoid this. Say for example that each logged on user belongs to a school and I need to access this SchoolId in almost every action.

They way I have it now almost every action will have a repeated database hit and need to reference my userService class...something like:

public ActionResult Index()
{
var schoolId = userService.GetSchoolId(User.Identity.GetUserId());
var textBooksForSchool = textBookService.GetBooks(schoolId);
...
}

public ActionResult Delete()
{
var schoolId = userService.GetSchoolId(User.Identity.GetUserId());//all over the place
var textBooksForSchool = textBookService.DeleteBooks(schoolId);
...
}

I know that I can add the SchoolId to the claims but the syntax for returning it in every method is quite verbose (as far as I understand this avoids the db hit each time the claim is accessed?):

In GenerateIdentityAsync:

 var claims = new Collection<Claim>
        {           
        new Claim("SchoolId", User.SchoolId.ToString())
        };

        userIdentity.AddClaims(claims);        

In Action:

var SchoolId = Convert.ToInt32((User as ClaimsPrincipal).Claims.First(x => x.Type == "SchoolId").Value);   

Is there some kind of best practice here? Possibly storing the claim in a global variable on logon?

Upvotes: 1

Views: 2542

Answers (3)

woggles
woggles

Reputation: 7444

I really like the extension method approach:

  public static int SchoolId(this IPrincipal principal)
        {
            return Convert.ToInt32((principal as ClaimsPrincipal).Claims.First(x => x.Type == "SchoolId").Value); 
        }

Action:

var textBooksForSchool = textBookService.GetBooks(User.SchoolId());

Upvotes: 0

Bikal Bhattarai
Bikal Bhattarai

Reputation: 251

This is how I am doing...

Base Controller

public class BaseController : Controller
{
    public AppUser CurrentUser
    {
        get
        {
            return new AppUser(this.User as ClaimsPrincipal);
        }
    }
}

Claims Principal

public class AppUser : ClaimsPrincipal
{
    public AppUser(ClaimsPrincipal principal)
        : base(principal)
    {
    }

    public string Name
    {
        get
        {
            return this.FindFirst(ClaimTypes.Name).Value;
        }
    }

    public string Email
    {
        get
        {
            return this.FindFirst(ClaimTypes.Email).Value;
        }
    }
}

In the other controller you can access the claim type just by doing

CurrentUser.Email

Upvotes: 1

Derek Strickland
Derek Strickland

Reputation: 31

What about creating your own base controller that all your controllers inherit from that has SchoolId as a property and then creating an ActionFilter that casts each controller as that base controller and sets that value on every request? Then it will be available on all your actions but you only have to write the code once.

It will fire each request, so you might consider other techniques for minimizing the number of times you have to look up the value, but this mechanism is a way to solve your code duplication issue.

Upvotes: 1

Related Questions