1
\$\begingroup\$

The DocumentClient is an object that accesses CosmosDb, but Microsoft recommends its kept as a Singleton class. I'm relying on System.Lazy to ensure thread safety.

In addition, I want to be able to overwrite my DocumentClient when I need to test. Is this correct?

public sealed class DocumentClientSingleton
{
    public static IDocumentClient GetInstance()
    {
        return _lazy.Value;
    }

    /// <summary>
    /// Only use this for testing!
    /// </summary>
    /// <returns></returns>
    public static IDocumentClient SetInstanceForTesting(IDocumentClient client)
    {
        _lazy = new Lazy<IDocumentClient>(() => client );
        return _lazy.Value;
    }

    private static  Lazy<IDocumentClient> _lazy = 
        new Lazy<IDocumentClient>(InstantiateDocument);

    private static IDocumentClient InstantiateDocument()
    {
        return new DocumentClient(
            new Uri(ConfigurationManager.AppSettings["endpoint"]),
            ConfigurationManager.AppSettings["authKey"],
            new ConnectionPolicy
            {
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp
            }
        );
    }
}
\$\endgroup\$
3
\$\begingroup\$

This answer only has one tiny section (IDisposable) that I feel is an appropriate kind of answer for Code Review. The rest is more suitable as a StackOverflow answer (since it dives into the specifics of the DocumentClient class). I apologize in advance, and if I should move my answer or edit it in anyway, please feel free to advise me in the comments section.

The DocumentClientclass is already thread-safe.

From what I can gather reading the latest documentation on the DocumentClient class, is that it is already inherently thread-safe. Provided that this remains true, your class wrapper is obsolete.

It seems that this confusion was also asked as a question over at StackOverflow


Microsoft's recommendation of using DocumentClient as a singleton

If you can draw a parallel to how Microsoft recommends usage of the HttpClient then you might have a better idea of what I mean from this point onwards.

Microsoft recommends that you use a single instance* per application. (again, think of HttpClient)

In the case of a web application using ASP.NET this usually comes in the form of dependency injection via declaring the lifetime of the class to Singleton.

// dependency injection for ASP.NET
services.AddSingleton(myDocumentClientInstance);

This might be the root cause of your misunderstanding. The use of the word "singleton" from the article you probably read, is actually an ASP.NET convention for specifying a lifetime of the instance and also ensuring that only a single (shared) instance is used across your entire web application.

This doesn't mean that implementing the DocumentClient as a singleton class is wrong.

In such a case, a static instance (like you have done) could be the answer. But you're going to get a group of developers that will throw fists in the air at you for making the code difficult-to-impossible to unit test if you do so... You have been warned!


The DocumentClient class implements IDisposable.

Based on your code it will become quite a complex task of making sure that the instance is properly disposed.

The recommended way to handle an object that implements the IDisposable interface is by using the the using block.

using (IDocumentClient client = new DocumentClient(new Uri("endpoint"), "authKey"))
{
    ...
}

Special cases

To summarize with a special case, which could hopefully shine some additional light why your implementation will be troublesome as a true singleton.

There are some special cases for using more than one instance. In particular, if you have multiple CosmosDb accounts, then you should actually use one instance for each account. You can read about that here.

Note: The parallel to HttpClient does not work here, since we are creating multiple instances on purpose.

Your particular implementation will fall short when you have a scenario with multiple CosmosDb accounts. So please keep that in mind.

\$\endgroup\$
  • \$\begingroup\$ What to do for multiple CosmosDb configurations? \$\endgroup\$ – Vijay Nirmal Jul 4 at 13:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.