This project is read-only.

Returning null from an instanceCreator delegate

Jan 19, 2014 at 9:41 PM
Edited Jan 19, 2014 at 9:41 PM
I was reading another thread where you mentioned this:
Simple Injector however does not permit registered factory delegates to return null. It ever wraps your delegate with a special guard delegate to prevent null from ever being injected into any constructor.
Is a way around this? My use case is that I want to return null if a user is not logged in:
container.Register(() => GetLoggedInUser(container.GetInstance<IDataRepository>()), perRequestLifestyle);

private static User GetLoggedInUser(IDataRepository dataRepository)
{
    if (!HttpContext.Current.Request.IsAuthenticated)
        return null;

    string authenticationData = HttpContext.Current.User.Identity.Name;
    int userId = int.Parse(authenticationData.Split('|').First());

    return dataRepository.Users.Get(userId);
}
Coordinator
Jan 19, 2014 at 10:19 PM
There's no way around this and this is deliberate. This prevents users from having to worry whether the injected value is null or not. It is never null. Constructor parameters should never allow null. If the value is optional, you should either use property injection, or -better- use the Null Object Pattern. In other words, return a special 'anonymous user' User instance. Example:
private static User GetLoggedInUser(IDataRepository dataRepository)
{
    if (!HttpContext.Current.Request.IsAuthenticated)
        return new AnonymousUser();

    // more here
}
btw, your registration is problematic, because you are doing too much during construction of the object graph (you are querying the database). You should always try to Compose object graphs with confidence.

So instead of injecting a user object directly, try hiding it behind an abstraction, say: IUserContext:
container.RegisterPerWebRequest<IUserContext, AspNetUserContext>();

public interface IUserContext {
    User User { get; }
}

public class AspNetUserContext : IUserContext {
    private readonly IDataRepository dataRepository;
    private readonly Lazy<User> user;

    public AspNetUserContext(IDataRepository dataRepository) {
        this.dataRepository = dataRepository;
        this.user = new Lazy<User>(this.GetLoggedInUser, false);
    }

    public User User { get { return this.user.Value; } }

    private User GetLoggedInUser()
    {
        if (!HttpContext.Current.Request.IsAuthenticated)
            return User.AnonymousUser;

        string authenticationData = HttpContext.Current.User.Identity.Name;
        int userId = int.Parse(authenticationData.Split('|').First());

        return this.dataRepository.Users.Get(userId);
    }    
}
Instead of injecting some entity, you now inject an IUserContext service which allows access to the User object that is lazily loaded (after the object graph is constructed). This also allows easy access to other user related properties such as its name, its roles, etc.
Marked as answer by ajbeaven on 1/19/2014 at 2:51 PM
Jan 19, 2014 at 10:38 PM
Wow, thanks so much! Some great suggestions here that I will most definitely implement.