3

So we have a WCF service that provides some object to the client. Our wcf service gets the data from a Data Access Layer (DAL). The DAL gets the data from an API and formats it into an object that we can use. For the most of the objects this works well.

But we also need a list of objects from the API and the objects don't change. The query against the API takes up 15 to 20 seconds. Way to long to run this query multiple times for the same data. So we would like to store the list in memory. Because the DBA's are not our best friends, and SQLite or SQL CE is not an option.

Now we have something like this, the first time the groups are loaded we store them in a private static variable. Is this the right way to do something like this, or is there a better way?


    public static class GroupStore
    {
        private static DTO.Group[] _groups;

        public static DTO.Group[] GetAll()
        {
            if (_groups == null)
            {
                var dal = PluginHandler.Instance.GetPlugin();
                _groups = dal.GetAll();
            }
            return _groups;
        }
    }
Preben Huybrechts
  • 5,853
  • 2
  • 27
  • 63
  • 3
    This is not thread safe. – Filip De Vos Jun 08 '12 at 11:30
  • Your question is not an exact duplicate, but this similar question may help: http://stackoverflow.com/questions/922116/caching-in-wcf – Joel Etherton Jun 08 '12 at 11:31
  • Read up on IoC & Dependency injection. They help you get rid of singletons like your plugin handler. http://www.codeproject.com/Articles/386164/Get-injected-into-the-world-of-inverted-dependenci – jgauffin Jun 08 '12 at 11:40
  • @JoelEtherton I would like to cache, before i get in to my service layer. But thanks for the link, i'll have a look in it. – Preben Huybrechts Jun 08 '12 at 11:44
  • @jgauffin I know what IoC and Dependecy Injection is, but at the customer where i'm working they work with spring.net and there own wrapper – Preben Huybrechts Jun 08 '12 at 11:45

1 Answers1

4

If you are using .NET4 you can use the Lazy<T> class. It's thread safe and wraps the lazy loading for you.

The code becomes a bit more clean:

public static class GroupStore
{
    private static Lazy<DTO.Group[]> _groups = new Lazy<DTO.Group[]>(GetAll);

    public static DTO.Group[] Groups { get { return _groups.Value; } }

    private static DTO.Group[] GetAll()
    {
        var dal = PluginHandler.Instance.GetPlugin();
        return dal.GetAll();
    }
}

Update

The answer that you linked in a comment is OK. But the IsValueCreated uses locking without reason (as the list could have changed between the check and the next access).

I also would not create the value if "ToString" get's called.

A little more clean solution:

public sealed class Lazy<T> where T : class
{
    private readonly object _syncRoot = new object();
    private readonly Func<T> _factory;
    private T _value;

    public Lazy(Func<T> factory)
    {
        if (factory == null) throw new ArgumentNullException("factory");

        _factory = factory;
    }

    public T Value
    {
        get
        {
            if (_value == null)
            {
                lock (_syncRoot)
                {
                    if (_value == null)
                    {
                        _value = _factory();
                    }
                }
            }
            return _value;
        }
    }

    public override string ToString()
    {
        return _value == null ? "Not created" : _value.ToString();
    }
}
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • We use .NET 3.5 so no lazy, or we could implement lazy like this: http://stackoverflow.com/questions/3207580/implementation-of-lazyt-for-net-3-5 – Preben Huybrechts Jun 08 '12 at 11:51
  • 1
    @PrebenHuybrechts: Yes. The double check (check, lock, check) is the best thing to do since it doesn't hurt performance as much as always locking. – jgauffin Jun 08 '12 at 12:16
  • 1
    @PrebenHuybrechts: Just explained the double check pattern: http://blog.gauffin.org/2012/06/double-check-pattern/ – jgauffin Jun 08 '12 at 12:39