1

For every page request, I need to get some current user permissions data from the database and then use this information several times in the logic (which is made of several objects)

I want to get data from the database once per request, and reuse it where needed by injecting it.

Is the following the best way to do this?

1) define a object like this

class ApplicationUser : IApplicationUserProvider
{
    private UserPermissions _userPermissions;
    ...

    public ApplicationUser(IDatabase userService)
    {
        _userPermissions = userService.GetUserPermission(); // this would executed for every request once
    }

    UserRoles GetRoles()
    {
        return _userPermissions;
    }
}

2) define the ioc something like this

 var container1 = new Container(c =>
            {
        c.For<IDatabase>().Use<Database>();
        c.For<IApplicationUser>().Use<ApplicationUser >();
        c.For<IApplicationLogic1>().Use<ApplicationLogic1>(); // this has IApplicationUser in ctor
        c.For<IApplicationLogic2>().Use<ApplicationLogic2>(); // this has IApplicationUser in ctor
            }); 

3) the controller would be

void PageController(IApplicationLogic1 l1)
{
    l1.Process();
    l2.Process();   
}
Steven
  • 166,672
  • 24
  • 332
  • 435
user2418209
  • 109
  • 8

1 Answers1

2

The UserPermissions information is runtime data, and as explained here, runtime data should not be injected or resolved during construction of the object graphs.

Instead, the call to userService.GetUserPermission() should be moved out of the constructor. For instance by delaying the call by using a Lazy<T>:

class ApplicationUser : IApplicationUserProvider
{
    private Lazy<UserPermissions> _userPermissions;

    public ApplicationUser(IDatabase userService) {
        _userPermissions = new Lazy<UserPermissions>(userService.GetUserPermission);
    }

    UserRoles GetRoles() {
        return _userPermissions.Value.GetRoles();
    }
}

Another option is to define a decorator on IDatabase that will implement the caching:

public class PerRequestCacheDatabaseDecorator : IDatabase
{
    private IDatabase _decoratee;

    public PerRequestCacheDatabaseDecorator(IDatabase decoratee) {
        _decoratee = decoratee;
    }

    public UserPermissions GetUserPermission() {
        var items = HttpContext.Current.Items;
        if (items["permissions"] == null)
            items["permissions"] = _decoratee.GetUserPermission();
        return (UserPermissions)items["permissions"];
    }
}

By wrapping the real database inside the PerRequestCacheDatabaseDecorator, you can simplify the ApplicationUser to the following:

class ApplicationUser : IApplicationUserProvider
{
    private IDatabase _userService;

    public ApplicationUser(IDatabase userService) {
        _userService = userService;
    }

    public UserRoles GetRoles() {
        return _userService.GetUserPermission().GetRoles();
    }
}

Now neither the ApplicationUser nor the Database class are concerned with this performance optimization, which is a good thing. The decorator allows us to plugin this performance optimization without having to change any of the existing classes.

Steven
  • 166,672
  • 24
  • 332
  • 435