Sanctus
Sanctus

Reputation: 273

DocumentDB .Net client using connection string

I checked the MSDN on DocumentDB for .Net (here) and found 3 valid constructors. However none of them makes use of connection strings, which sounds strange for me.

Is there seriously no way to instantiate client with connection string instead of endpoint+authKey combo or I'm missing something?

For example majority of other Microsoft services uses this concept, i.e. https://learn.microsoft.com/en-us/azure/storage/storage-configure-connection-string#parsing-a-connection-string . In our case it would be super if all Azure related stuff is initialized in same manner. Just cleaner, not something show-stopping.

P.S. Please stop telling me about existing constructors with Uri and authKey parameters, question is (slightly) different. I can follow links I provided myself, no needs to help. Thanks.

Upvotes: 15

Views: 9102

Answers (4)

deadwards
deadwards

Reputation: 2118

You can actually do this in a roundabout way.

internal class CosmosDBConnectionString
{
    public CosmosDBConnectionString(string connectionString)
    {
        // Use this generic builder to parse the connection string
        DbConnectionStringBuilder builder = new DbConnectionStringBuilder
        {
            ConnectionString = connectionString
        };

        if (builder.TryGetValue("AccountKey", out object key))
        {
            AuthKey = key.ToString();
        }

        if (builder.TryGetValue("AccountEndpoint", out object uri))
        {
            ServiceEndpoint = new Uri(uri.ToString());
        }
    }

    public Uri ServiceEndpoint { get; set; }

    public string AuthKey { get; set; }
}

Then

var cosmosDBConnectionString = new CosmosDBConnectionString(connectionString)
var client = new DocumentClient(
            cosmosDBConnectionString.ServiceEndpoint,
            cosmosDBConnectionString.AuthKey)

This is taken from the Azure WebJobs Extensions SDK, which is how Azure Functions V2 is able to work with just a connection string. Saves having to try and parse the string yourself.

Upvotes: 19

A.Rowan
A.Rowan

Reputation: 1749

That works for me:

    private static DocumentClient InitializeDocumentClient()
    {

        string connectionString = ConfigurationManager.AppSettings["CosmosTest"];
        string[] connectionStringParts = connectionString.Split(';');
        Uri clientUrl = new Uri(connectionStringParts[0].Split('=')[1]);
        int keyStartPosition = connectionStringParts[1].IndexOf('=') + 1;
        string clientKey = connectionStringParts[1].Substring(keyStartPosition, connectionStringParts[1].Length-keyStartPosition);
        return new DocumentClient(clientUrl, clientKey, connectionPolicy);
    }

Upvotes: 3

MPavlak
MPavlak

Reputation: 2221

I created a class for parsing connection string similar to how the CloudStorageAccount.Parse works. I tried to follow their pattern as closely as possible in case they ever decide to open source it, this could hopefully be contribed without much change.

public static class DocumentDbAccount
{
    public static DocumentClient Parse(string connectionString)
    {
        DocumentClient ret;

        if (String.IsNullOrWhiteSpace(connectionString))
        {
            throw new ArgumentException("Connection string cannot be empty.");
        }

        if(ParseImpl(connectionString, out ret, err => { throw new FormatException(err); }))
        {
            return ret;
        }

        throw new ArgumentException($"Connection string was not able to be parsed into a document client.");
    }

    public static bool TryParse(string connectionString, out DocumentClient documentClient)
    {
        if (String.IsNullOrWhiteSpace(connectionString))
        {
            documentClient = null;
            return false;
        }

        try
        {
            return ParseImpl(connectionString, out documentClient, err => { });
        }
        catch (Exception)
        {
            documentClient = null;
            return false;
        }
    }

    private const string AccountEndpointKey = "AccountEndpoint";
    private const string AccountKeyKey = "AccountKey";
    private static readonly HashSet<string> RequireSettings = new HashSet<string>(new [] { AccountEndpointKey, AccountKeyKey }, StringComparer.OrdinalIgnoreCase);

    internal static bool ParseImpl(string connectionString, out DocumentClient documentClient, Action<string> error)
    {
        IDictionary<string, string> settings = ParseStringIntoSettings(connectionString, error);

        if (settings == null)
        {
            documentClient = null;
            return false;
        }

        if (!RequireSettings.IsSubsetOf(settings.Keys))
        {
            documentClient = null;
            return false;
        }

        documentClient = new DocumentClient(new Uri(settings[AccountEndpointKey]), settings[AccountKeyKey]);
        return true;
    }

    /// <summary>
    /// Tokenizes input and stores name value pairs.
    /// </summary>
    /// <param name="connectionString">The string to parse.</param>
    /// <param name="error">Error reporting delegate.</param>
    /// <returns>Tokenized collection.</returns>
    private static IDictionary<string, string> ParseStringIntoSettings(string connectionString, Action<string> error)
    {
        IDictionary<string, string> settings = new Dictionary<string, string>();
        string[] splitted = connectionString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

        foreach (string nameValue in splitted)
        {
            string[] splittedNameValue = nameValue.Split(new char[] { '=' }, 2);

            if (splittedNameValue.Length != 2)
            {
                error("Settings must be of the form \"name=value\".");
                return null;
            }

            if (settings.ContainsKey(splittedNameValue[0]))
            {
                error(string.Format(CultureInfo.InvariantCulture, "Duplicate setting '{0}' found.", splittedNameValue[0]));
                return null;
            }

            settings.Add(splittedNameValue[0], splittedNameValue[1]);
        }

        return settings;
    }
}

Upvotes: 8

Aravind Krishna R.
Aravind Krishna R.

Reputation: 8003

The DocumentDB SDKs do not have constructor overloads using connection strings. They support initializing with endpoint + master key, and endpoint + permissions/resource tokens.

If you'd like to see a single connection string argument, please propose/upvote here: https://feedback.azure.com/forums/263030-documentdb

Upvotes: 7

Related Questions