Search code examples
c#architecturesolid-principles

Architecture for Avoiding Switch Statements


I am working on a new project and I am trying to adhere to proper design methods. I have ran into an issue with a switch statement that I know is a problem but I am unable to re-factor it out in an object oriented way.

In the system a User has 0..n roles. Based on which role the user is at the moment the system will return a specific set of data to that user. The user will be able to perform certain actions but not others etc.

public class User
{
    public bool HasRole(string roleName)
    {
        return this.UserRoles.Any(r => r.Name == roleName);
    }

    public string Username { get; set; }

    public long? CurrentRoleId { get; set; }
    public Role CurrentRole { get; set; }

    public virtual IList<Role> UserRoles { get; set; }
}

public class Role
{
    public Role() { }

    public string Name { get; set; }
}

public class GetEventsQuery : IQuery<List<Event>>
{
    public List<Event> Query()
    {
        switch (this.user.CurrentRole.Name)
        {
            case "Administrator":
                UserIsNotInAdministratorRole();
                return repository.All.ToList();

            case "Supervisor":
                UserIsNotInSupervisorRole();
                return repository.All.Where(evnt => evnt.SupervisorId == this.user.RecordId).ToList();

            case "User":
                UserIsNotInUserRole();
                return repository.All.Where(evnt => evnt.UserId == this.user.RecordId).ToList();

            default:
                throw new Exception("GetEventsQuery Unknow exception.");
        }
    }

    private void UserIsNotInUserRole()
    {
        if (!this.user.HasRole("User"))
        {
            throw new NoUserRoleException("User does not have user role!");
        }
    }

    private void UserIsNotInSupervisorRole()
    {
        if (!this.user.HasRole("Supervisor"))
        {
            throw new NoSupervisorRoleException("User does not have supervisor role!");
        }
    }

    private void UserIsNotInAdministratorRole()
    {
        if (!this.user.HasRole("Administrator"))
        {
            throw new NoAdministratorRoleException("User does not have administrator role!");
        }
    }

    public GetEventsQuery(string username, IUserRepository userRepository, IEventRepository repository)
    {
        this.repository = repository;

        var userQuery = new GetUserQuery(username, userRepository);
        this.user = userQuery.Query();
    }

    private readonly User user;
    private readonly IEventRepository repository;
}

This switch statement will show up in all parts of the system. If there was a way to re-factor it out into a class and have it in a single location I don't mind keeping it but having it repeated over and over again is definitely a code smell. I am just starting this project out so if there is a better way to design the object hierarchy or make major changes to eliminate this issue I am open to it.


Solution

  • Role should be a common base class (or interface) inherited by each role (e.g Administrator, Supervisor, User.) Each implementation of Role should have them information on what is or is not allowed for that particular role.