This post is about how you can use Func
Background
What does null mean? It might seem like a simple question and you might say that the answer is that the variable has not been initialized. Hopefully you realize that this is only half the fact though. The problem is that null sometimes doesn't match our domain and we often let null mean more than one thing. It could be that the variable is not intialized but it could also be that a call to the database didn't find a matching record and the result therefore is null.
Not thinking through what null really means in your problem domain might lead to defects. I came across this problem today. I was working on the homepage for my club and as usual I was running SQL Server Profiler. What almost caused me to fall of my chair was the lines below in SSP.
The reason this suprised me was damn sure I was caching away the current user in my service. A quick look at the code confirmed that it should be cached.
public User GetUser()
{
if (this.currentUser == null)
{
this.currentUser = this.membershipRepository.GetUser();
if (this.currentUser != null)
{
this.currentUser.Avatar = new AvatarImage(this.avatarSettingsReader.AvatarPath, this.currentUser.UserName, p => File.Exists(this.avatarSettingsReader.MapPath(p)));
}
}
return this.currentUser;
}
The clue that made me realize my misstake was that I was testing in FF and IE in parallell and in FF I was logged in and there the user was only fetched from the db once. I had considered the meaning of null as "The item needs to be fetched" when in reality it could also mean that the "The item has been fetched but had no value".
So how to solve the problem?
To be clear I consider the problem to be that I wanted to give null a meaning(Not fetched) when my domain already had a meaning for null (Not logged in).
Changing the domain was out of the question because that would have meant more work. The first thought was to add a hasLoaded boolean. The first check in the code above would simply have changed in that case. I'm not sure why but I don't like having flags like these in my code so I came up with another approach.
I created a private variable:
private Func
And in the constructor I instialized it to:
this.getUserFunc = () =>
{
this.currentUser = this.membershipRepository.GetUser();
if (this.currentUser != null)
{
this.currentUser.Avatar = new AvatarImage(this.avatarSettingsReader.AvatarPath, this.currentUser.UserName, p => File.Exists(this.avatarSettingsReader.MapPath(p)));
}
this.getUserFunc = () => this.currentUser;
return this.currentUser;
};
Note line 8 where the func alter itself. So the method above only runs once then it alter itself to return the saved value.
My GetUser method now reads:
public User GetUser()
{
return this.getUserFunc();
}
As seen below the generated SQL is now what I would expect.
Conclusion
Take a few seconds to consider if null already has a meaning in your domain before assigning it a new one.
I'm not sure if the way I solved it above is good or not. I'm afraid it might make the code harder to understand. I will have to try it out and see what I think of it.