Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="Chinchilla Software Limited">
4 : // // Copyright Chinchilla Software Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections.Generic;
11 : using System.Linq;
12 : using System.Text;
13 : using Cqrs.Configuration;
14 : using cdmdotnet.Logging;
15 : using Cqrs.DataStores;
16 : using Cqrs.Exceptions;
17 :
18 : namespace Cqrs.Azure.BlobStorage.DataStores
19 : {
20 : /// <summary>
21 : /// A factory for getting connection strings and container names for <see cref="IDataStore{TData}"/> access.
22 : /// This factory supports reading and writing from separate storage accounts. Specifically you can have as many different storage accounts as you want to configure when writing.
23 : /// This allows for manual mirroring of data while reading from the fastest/closest location possible.
24 : /// </summary>
25 : public class TableStorageDataStoreConnectionStringFactory : ITableStorageDataStoreConnectionStringFactory
26 1 : {
27 : /// <summary>
28 : /// The name of the app setting in <see cref="IConfigurationManager"/> that will have the connection string of the readable storage account if using a separate storage account for reads and writes.
29 : /// </summary>
30 : public static string TableStorageReadableDataStoreConnectionStringKey = "Cqrs.Azure.TableStorage.DataStore.Read.ConnectionStringName";
31 :
32 : /// <summary>
33 : /// The name of the app setting in <see cref="IConfigurationManager"/> that will have the connection string of the writeable storage account if using a separate storage account for reads and writes.
34 : /// This value gets appended with a ".1", ".2" etc allowing you to write to as many different locations as possible.
35 : /// </summary>
36 : public static string TableStorageWritableDataStoreConnectionStringKey = "Cqrs.Azure.TableStorage.DataStore.Write.ConnectionStringName";
37 :
38 : /// <summary>
39 : /// The name of the app setting in <see cref="IConfigurationManager"/> that will have the connection string if using a single storage account for both reads and writes.
40 : /// </summary>
41 : public static string TableStorageDataStoreConnectionStringKey = "Cqrs.Azure.TableStorage.DataStore.ConnectionStringName";
42 :
43 : /// <summary>
44 : /// The name of the app setting in <see cref="IConfigurationManager"/> that will have the base name of the container used.
45 : /// </summary>
46 : public static string TableStorageBaseContainerNameKey = "Cqrs.Azure.TableStorage.DataStore.BaseContainerName";
47 :
48 : /// <summary>
49 : /// Gets or sets the <see cref="IConfigurationManager"/>.
50 : /// </summary>
51 : protected IConfigurationManager ConfigurationManager { get; private set; }
52 :
53 : /// <summary>
54 : /// Gets or sets the <see cref="ILogger"/>.
55 : /// </summary>
56 : protected ILogger Logger { get; private set; }
57 :
58 : /// <summary>
59 : /// Instantiates a new instance of <see cref="TableStorageDataStoreConnectionStringFactory"/>.
60 : /// </summary>
61 1 : public TableStorageDataStoreConnectionStringFactory(IConfigurationManager configurationManager, ILogger logger)
62 : {
63 : ConfigurationManager = configurationManager;
64 : Logger = logger;
65 : }
66 :
67 : /// <summary>
68 : /// Gets all writeable connection strings. If using a single storage account, then <see cref="TableStorageDataStoreConnectionStringKey"/> will most likely be returned.
69 : /// If a value for <see cref="TableStorageWritableDataStoreConnectionStringKey"/> is found, it will append ".1", ".2" etc returning any additionally found connection string values in <see cref="ConfigurationManager"/>.
70 : /// </summary>
71 1 : public virtual IEnumerable<string> GetWritableConnectionStrings()
72 : {
73 : Logger.LogDebug("Getting table storage writeable connection strings", "TableStorageDataStoreConnectionStringFactory\\GetWritableConnectionStrings");
74 : try
75 : {
76 : var collection = new List<string> ();
77 :
78 : string blobStorageWritableDataStoreConnectionString = ConfigurationManager.GetSetting(TableStorageWritableDataStoreConnectionStringKey);
79 : if (blobStorageWritableDataStoreConnectionString == null)
80 : {
81 : Logger.LogDebug(string.Format("No application setting named '{0}' was found in the configuration file with the cloud storage connection string.", TableStorageWritableDataStoreConnectionStringKey), "TableStorageDataStoreConnectionStringFactory\\GetWritableConnectionStrings");
82 : blobStorageWritableDataStoreConnectionString = ConfigurationManager.GetSetting(TableStorageDataStoreConnectionStringKey);
83 : }
84 :
85 : int writeIndex = 1;
86 : while (!string.IsNullOrWhiteSpace(blobStorageWritableDataStoreConnectionString))
87 : {
88 : collection.Add(blobStorageWritableDataStoreConnectionString);
89 :
90 : blobStorageWritableDataStoreConnectionString = ConfigurationManager.GetSetting(string.Format("{0}.{1}", TableStorageWritableDataStoreConnectionStringKey, writeIndex));
91 : writeIndex++;
92 : }
93 :
94 : if (!collection.Any())
95 : throw new NullReferenceException();
96 :
97 : return collection;
98 : }
99 : catch (NullReferenceException exception)
100 : {
101 : throw new MissingApplicationSettingException(TableStorageDataStoreConnectionStringKey, string.Format("No application setting named '{0}' was found in the configuration file with the cloud storage connection string.", TableStorageDataStoreConnectionStringKey), exception);
102 : }
103 : finally
104 : {
105 : Logger.LogDebug("Getting table storage writeable connection string... Done", "TableStorageDataStoreConnectionStringFactory\\GetWritableConnectionStrings");
106 : }
107 : }
108 :
109 : /// <summary>
110 : /// Gets the readable connection string. If using a single storage account, then <see cref="TableStorageDataStoreConnectionStringKey"/> will most likely be returned.
111 : /// If a value for <see cref="TableStorageReadableDataStoreConnectionStringKey"/> is found, that will be returned instead.
112 : /// </summary>
113 1 : public virtual string GetReadableConnectionString()
114 : {
115 : Logger.LogDebug("Getting table storage readable connection strings", "TableStorageDataStoreConnectionStringFactory\\GetReadableConnectionStrings");
116 : try
117 : {
118 : string blobStorageWritableDataStoreConnectionString = ConfigurationManager.GetSetting(TableStorageReadableDataStoreConnectionStringKey);
119 : if (blobStorageWritableDataStoreConnectionString == null)
120 : {
121 : Logger.LogDebug(string.Format("No application setting named '{0}' was found in the configuration file with the cloud storage connection string.", TableStorageReadableDataStoreConnectionStringKey), "TableStorageDataStoreConnectionStringFactory\\GetReadableConnectionStrings");
122 : blobStorageWritableDataStoreConnectionString = ConfigurationManager.GetSetting(TableStorageDataStoreConnectionStringKey);
123 : }
124 :
125 : if (string.IsNullOrWhiteSpace(blobStorageWritableDataStoreConnectionString))
126 : throw new NullReferenceException();
127 :
128 : return blobStorageWritableDataStoreConnectionString;
129 : }
130 : catch (NullReferenceException exception)
131 : {
132 : throw new MissingApplicationSettingException(TableStorageDataStoreConnectionStringKey, string.Format("No application setting named '{0}' was found in the configuration file with the cloud storage connection string.", TableStorageDataStoreConnectionStringKey), exception);
133 : }
134 : finally
135 : {
136 : Logger.LogDebug("Getting table storage readable connection string... Done", "TableStorageDataStoreConnectionStringFactory\\GetReadableConnectionStrings");
137 : }
138 : }
139 :
140 : /// <summary>
141 : /// Returns the name of the base contain to be used.
142 : /// This will be the value from <see cref="ConfigurationManager"/> keyed <see cref="TableStorageBaseContainerNameKey"/>.
143 : /// </summary>
144 1 : public string GetBaseContainerName()
145 : {
146 : Logger.LogDebug("Getting table storage base container name", "TableStorageDataStoreConnectionStringFactory\\GetContainerName");
147 : try
148 : {
149 : string result = ConfigurationManager.GetSetting(TableStorageBaseContainerNameKey);
150 :
151 : if (string.IsNullOrWhiteSpace(result))
152 : throw new NullReferenceException();
153 :
154 : return result;
155 : }
156 : catch (NullReferenceException exception)
157 : {
158 : throw new MissingApplicationSettingException(TableStorageBaseContainerNameKey, exception);
159 : }
160 : finally
161 : {
162 : Logger.LogDebug("Getting table storage base container name... Done", "TableStorageDataStoreConnectionStringFactory\\GetContainerName");
163 : }
164 : }
165 :
166 : /// <summary>
167 : /// Returns <see cref="GetBaseContainerName"/>.
168 : /// </summary>
169 : /// <returns><see cref="GetBaseContainerName"/></returns>
170 1 : public virtual string GetContainerName()
171 : {
172 : return GetBaseContainerName();
173 : }
174 :
175 : readonly char[] _alphanumericCharacters =
176 : {
177 : '0','1','2','3','4','5','6','7','8','9',
178 : 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
179 : 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
180 : };
181 :
182 : /// <summary>
183 : /// Generates the name of the table for <typeparamref name="TData"/> that matches naming rules for Azure Storage.
184 : /// </summary>
185 : /// <typeparam name="TData">The <see cref="Type"/> of data to read or write.</typeparam>
186 : /// <remarks>https://blogs.msdn.microsoft.com/jmstall/2014/06/12/azure-storage-naming-rules/</remarks>
187 1 : public virtual string GetTableName<TData>()
188 : {
189 : Type type = typeof (TData);
190 : string fullTableName = type.AssemblyQualifiedName ?? "Entities";
191 : StringBuilder sb;
192 :
193 : string name = fullTableName;
194 : int index1 = name.IndexOf(",", StringComparison.InvariantCultureIgnoreCase);
195 : int index2 = -1;
196 : if (index1 > -1)
197 : index2 = name.IndexOf(",", index1 + 1, StringComparison.InvariantCultureIgnoreCase);
198 : if (index2 > -1)
199 : {
200 : name = name.Substring(0, index2);
201 : string[] nameParts = name.Split(',');
202 : if (nameParts.Length == 2)
203 : {
204 : if (nameParts[0].StartsWith(nameParts[1].Trim()))
205 : {
206 : name = name.Substring(0, index1);
207 : sb = new StringBuilder();
208 : foreach (var c in name.Where(c => _alphanumericCharacters.Contains(c)))
209 : sb.Append(c);
210 :
211 : name = sb.ToString();
212 : if (name.Length > 36)
213 : name = name.Substring(name.Length - 36);
214 :
215 : return name;
216 : }
217 : }
218 : }
219 : else if (index1 > -1)
220 : name = name.Substring(0, index1);
221 :
222 : sb = new StringBuilder();
223 : foreach (var c in name.Where(c => _alphanumericCharacters.Contains(c)))
224 : sb.Append(c);
225 :
226 : name = sb.ToString();
227 : return name.Substring(0, 36);
228 : }
229 : }
230 : }
|