Line data Source code
1 : #region Copyright
2 : // // -----------------------------------------------------------------------
3 : // // <copyright company="cdmdotnet Limited">
4 : // // Copyright cdmdotnet Limited. All rights reserved.
5 : // // </copyright>
6 : // // -----------------------------------------------------------------------
7 : #endregion
8 :
9 : using System;
10 : using System.Collections;
11 : using System.Collections.Generic;
12 : using System.IO;
13 : using System.Linq;
14 : using System.Linq.Expressions;
15 : using System.Text;
16 : using cdmdotnet.Logging;
17 : using Cqrs.Events;
18 : using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
19 : using Microsoft.Practices.EnterpriseLibrary.WindowsAzure.TransientFaultHandling;
20 : using Microsoft.Practices.TransientFaultHandling;
21 : using Microsoft.WindowsAzure.Storage;
22 : using Newtonsoft.Json;
23 :
24 : namespace Cqrs.Azure.BlobStorage
25 : {
26 : public abstract class StorageStore<TData, TSource>
27 : : IEnumerable<TData>
28 0 : {
29 : protected IList<Tuple<CloudStorageAccount, TSource>> WritableCollection { get; private set; }
30 :
31 : protected CloudStorageAccount ReadableStorageAccount { get; private set; }
32 :
33 : internal TSource ReadableSource { get; private set; }
34 :
35 : protected ILogger Logger { get; private set; }
36 :
37 : protected Func<string> GetContainerName { get; set; }
38 :
39 : protected Func<bool> IsContainerPublic { get; set; }
40 :
41 : /// <summary>
42 : /// Initializes a new instance of the <see cref="StorageStore{TData,TSource}"/> class using the specified container.
43 : /// </summary>
44 1 : protected StorageStore(ILogger logger)
45 : {
46 : Logger = logger;
47 : }
48 :
49 : public static JsonSerializerSettings DefaultSettings { get; private set; }
50 :
51 : static StorageStore()
52 : {
53 : DefaultSettings = DefaultJsonSerializerSettings.DefaultSettings;
54 : }
55 :
56 0 : protected virtual void Initialise(IStorageStoreConnectionStringFactory storageDataStoreConnectionStringFactory)
57 : {
58 : WritableCollection = new List<Tuple<CloudStorageAccount, TSource>>();
59 : ReadableStorageAccount = CloudStorageAccount.Parse(storageDataStoreConnectionStringFactory.GetReadableConnectionString());
60 : ReadableSource = CreateSource(ReadableStorageAccount, GetContainerName(), IsContainerPublic());
61 :
62 : foreach (string writableConnectionString in storageDataStoreConnectionStringFactory.GetWritableConnectionStrings())
63 : {
64 : CloudStorageAccount storageAccount = CloudStorageAccount.Parse(writableConnectionString);
65 : TSource container = CreateSource(storageAccount, GetContainerName(), IsContainerPublic());
66 :
67 : WritableCollection.Add(new Tuple<CloudStorageAccount, TSource>(storageAccount, container));
68 : }
69 : }
70 :
71 : #region Implementation of IEnumerable
72 :
73 : /// <summary>
74 : /// Returns an enumerator that iterates through the collection.
75 : /// </summary>
76 : /// <returns>
77 : /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
78 : /// </returns>
79 1 : public abstract IEnumerator<TData> GetEnumerator();
80 :
81 : /// <summary>
82 : /// Returns an enumerator that iterates through a collection.
83 : /// </summary>
84 : /// <returns>
85 : /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
86 : /// </returns>
87 : IEnumerator IEnumerable.GetEnumerator()
88 : {
89 : return GetEnumerator();
90 : }
91 :
92 : #endregion
93 :
94 : #region Implementation of IQueryable
95 :
96 : /// <summary>
97 : /// Gets the expression tree that is associated with the instance of <see cref="T:System.Linq.IQueryable"/>.
98 : /// </summary>
99 : /// <returns>
100 : /// The <see cref="T:System.Linq.Expressions.Expression"/> that is associated with this instance of <see cref="T:System.Linq.IQueryable"/>.
101 : /// </returns>
102 : public abstract Expression Expression { get; }
103 :
104 : /// <summary>
105 : /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of <see cref="T:System.Linq.IQueryable"/> is executed.
106 : /// </summary>
107 : /// <returns>
108 : /// A <see cref="T:System.Type"/> that represents the type of the element(s) that are returned when the expression tree associated with this object is executed.
109 : /// </returns>
110 : public abstract Type ElementType { get; }
111 :
112 : /// <summary>
113 : /// Gets the query provider that is associated with this data source.
114 : /// </summary>
115 : /// <returns>
116 : /// The <see cref="T:System.Linq.IQueryProvider"/> that is associated with this data source.
117 : /// </returns>
118 : public abstract IQueryProvider Provider { get; }
119 :
120 : #endregion
121 :
122 : #region Implementation of IDisposable
123 :
124 : /// <summary>
125 : /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
126 : /// </summary>
127 1 : public void Dispose()
128 : {
129 : ReadableSource = default(TSource);
130 : ReadableStorageAccount = null;
131 :
132 : WritableCollection = null;
133 : }
134 :
135 : #endregion
136 :
137 : #region Implementation of IDataStore<TData>
138 :
139 0 : public abstract void Add(TData data);
140 :
141 0 : public virtual void Add(IEnumerable<TData> data)
142 : {
143 : foreach (TData dataItem in data)
144 : Add(dataItem);
145 : }
146 :
147 0 : public abstract void Destroy(TData data);
148 :
149 0 : public abstract void RemoveAll();
150 :
151 0 : public abstract void Update(TData data);
152 :
153 : #endregion
154 :
155 : /// <summary>
156 : /// Creates a <typeparam name="TSource" /> with the specified name <paramref name="sourceName"/> if it doesn't already exist.
157 : /// </summary>
158 : /// <param name="storageAccount">The storage account to create the container is</param>
159 : /// <param name="sourceName">The name of the source.</param>
160 : /// <param name="isPublic">Whether or not this source is publicly accessible.</param>
161 1 : protected abstract TSource CreateSource(CloudStorageAccount storageAccount, string sourceName, bool isPublic = true);
162 :
163 0 : protected virtual string GetSafeSourceName(string sourceName)
164 : {
165 : return GetSafeSourceName(sourceName, true);
166 : }
167 :
168 0 : protected virtual string GetSafeSourceName(string sourceName, bool lowerCaseName)
169 : {
170 : if (sourceName.Contains(":"))
171 : return sourceName;
172 :
173 : string safeContainerName = sourceName.Replace(@"\", @"/").Replace(@"/", @"-");
174 : if (lowerCaseName)
175 : safeContainerName = safeContainerName.ToLowerInvariant();
176 : if (safeContainerName.StartsWith("-"))
177 : safeContainerName = safeContainerName.Substring(1);
178 : if (safeContainerName.EndsWith("-"))
179 : safeContainerName = safeContainerName.Substring(0, safeContainerName.Length - 1);
180 : safeContainerName = safeContainerName.Replace(" ", "-");
181 :
182 : return safeContainerName;
183 : }
184 :
185 : /// <summary>
186 : /// Characters Disallowed in Key Fields
187 : ///
188 : /// The following characters are not allowed in values for the PartitionKey and RowKey properties:
189 : ///
190 : /// The forward slash (/) character
191 : /// The backslash (\) character
192 : /// The number sign (#) character
193 : /// The question mark (?) character
194 : /// Control characters from U+0000 to U+001F, including:
195 : /// The horizontal tab (\t) character
196 : /// The linefeed (\n) character
197 : /// The carriage return (\r) character
198 : /// Control characters from U+007F to U+009F
199 : /// </summary>
200 : /// <param name="sourceName"></param>
201 : /// <returns></returns>
202 : internal static string GetSafeStorageKey(string sourceName)
203 : {
204 : var sb = new StringBuilder();
205 : foreach (var c in sourceName
206 : .Where(c => c != '/'
207 : && c != '\\'
208 : && c != '#'
209 : && c != '/'
210 : && c != '?'
211 : && !char.IsControl(c)))
212 : sb.Append(c);
213 : return sb.ToString();
214 : }
215 :
216 : /// <summary>
217 : /// Gets the default retry policy dedicated to handling transient conditions with Windows Azure Storage.
218 : /// </summary>
219 : protected virtual RetryPolicy AzureStorageRetryPolicy
220 : {
221 : get
222 : {
223 : RetryManager retryManager = EnterpriseLibraryContainer.Current.GetInstance<RetryManager>();
224 : RetryPolicy retryPolicy = retryManager.GetDefaultAzureStorageRetryPolicy();
225 : retryPolicy.Retrying += (sender, args) =>
226 : {
227 : var msg = string.Format("Retrying action - Count: {0}, Delay: {1}", args.CurrentRetryCount, args.Delay);
228 : Logger.LogWarning(msg, exception: args.LastException);
229 : };
230 : return retryPolicy;
231 : }
232 : }
233 :
234 0 : protected virtual TData Deserialise(Stream dataStream)
235 : {
236 : using (dataStream)
237 : {
238 : using (var reader = new StreamReader(dataStream))
239 : {
240 : string jsonContents = reader.ReadToEnd();
241 : TData obj = Deserialise(jsonContents);
242 : return obj;
243 : }
244 : }
245 : }
246 :
247 0 : protected virtual TData Deserialise(string json)
248 : {
249 : using (var stringReader = new StringReader(json))
250 : using (var jsonTextReader = new JsonTextReader(stringReader))
251 : return GetSerialiser().Deserialize<TData>(jsonTextReader);
252 : }
253 :
254 0 : protected virtual Stream Serialise(TData data)
255 : {
256 : string dataContent = JsonConvert.SerializeObject(data, GetSerialisationSettings());
257 :
258 : byte[] byteArray = Encoding.UTF8.GetBytes(dataContent);
259 : var stream = new MemoryStream(byteArray);
260 :
261 : return stream;
262 : }
263 :
264 0 : protected virtual JsonSerializerSettings GetSerialisationSettings()
265 : {
266 : return DefaultSettings;
267 : }
268 :
269 0 : protected virtual JsonSerializer GetSerialiser()
270 : {
271 : JsonSerializerSettings settings = GetSerialisationSettings();
272 : return JsonSerializer.Create(settings);
273 : }
274 : }
275 : }
|