Q:

Thread safe cached object for asp request

First of all, I couldn't make the title more explanatory, I will try to lay out the problem then provide my solution for it
I'm implementing a backend in asp core for our game, we have few requests that are somewhat large, like requesting the items we provide in the store, every user starts the game loads the store info which makes a database trip to pull the entire store info, which RARELY change -less than once a month-, so we are making thousands of database trip that aren't needed.
on top of that we return timestamps for when was the last time an item image has changed, the images are stored in a blob which makes me query the blob for change date, which makes the request way costlier
so to solve all of this, I implemented a small class to cache the request until we need to update it,for this request and some others, but I'm not sure if I'm looking at this correctly
here is the base abstract class:
public abstract class CachedModel<T>
{
    protected T Model { get; set; }

    private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1,1);

    protected abstract Task ThreadSafeUpdateAsync();
    protected abstract bool NeedsUpdate();

    public async Task<T> GetModel()
    {
        if (NeedsUpdate())
        {
            try
            {
                await semaphore.WaitAsync();
                if(NeedsUpdate()) // not sure if this is needed, can other threads enter here after the first one already updated the object?
                    await ThreadSafeUpdateAsync();
            }
            finally
            {
                semaphore.Release();
            }
        }
        return Model;
    }
}

and then I implement this class per request like this:
public class CachedStoreInfo : CachedModel<DesiredModel>
{
    protected override async Task ThreadSafeUpdateAsync()
    {
        // make the trip to db and Blob service
        Model = some result
    }

    protected override bool NeedsUpdate()
    {
        return someLogicToDecideIfNeedsUpdate;
    }
}

finally, in the asp controller all what I need to do is this:
[HttpGet]
public async Task<DesiredModel> GetStoreInfo()
{
    return await cachedStoreInfo.GetModel();
}

Is this a proper implementation ? and is this even necessary or there is a smarter way to achieve this? getting the time stamps from the blob was the main reason I though about caching the result

A:

Your implementation looks correct. Of course the instance of CachedStoreInfo should be a singleton in a required scope (as I understand in your case it should be a singleton in scope of application). 

can other threads enter here after the first one already updated the object?

As Kevin Gosse noted other threads can enter here. Your second check for NeedsUpdate() is a part of Double-checked locking pattern. And it might be a good optimization.

and is this even necessary or there is a smarter way to achieve this?

As for me your implementation is minimalist and smart enough

