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.Linq.Expressions;
13 : using cdmdotnet.Logging;
14 : using Cqrs.Entities;
15 : using Cqrs.DataStores;
16 : using Cqrs.Repositories.Queries;
17 :
18 : namespace Cqrs.Repositories
19 : {
20 : /// <summary>
21 : /// Provides basic repository methods for operations with an <see cref="IDataStore{TData}"/>.
22 : /// </summary>
23 : /// <typeparam name="TQueryStrategy">The <see cref="Type"/> of <see cref="IQueryStrategy"/>.</typeparam>
24 : /// <typeparam name="TQueryBuilder">The <see cref="Type"/> of the <see cref="Cqrs.Repositories.Queries.QueryBuilder{TQueryStrategy,TData}"/> that will be used to build queries.</typeparam>
25 : /// <typeparam name="TData">The <see cref="Type"/> of data held in storage.</typeparam>
26 : public abstract class Repository<TQueryStrategy, TQueryBuilder, TData> : IRepository<TQueryStrategy, TData>
27 : where TQueryStrategy : IQueryStrategy
28 : where TQueryBuilder : QueryBuilder<TQueryStrategy, TData>
29 : where TData : Entity
30 1 : {
31 : /// <summary>
32 : /// Gets or sets the <see cref="Func{TResult}"/> that is used to create new instances of <see cref="IDataStore{TData}"/>.
33 : /// </summary>
34 : protected Func<IDataStore<TData>> CreateDataStoreFunction { get; private set; }
35 :
36 : /// <summary>
37 : /// Gets or sets the <typeparamref name="TQueryBuilder"/> that will be used to build queries.
38 : /// </summary>
39 : protected TQueryBuilder QueryBuilder { get; private set; }
40 :
41 : /// <summary>
42 : /// Gets or sets the <see cref="ITelemetryHelper"/>.
43 : /// </summary>
44 : protected ITelemetryHelper TelemetryHelper { get; set; }
45 :
46 : /// <summary>
47 : /// Instantiates a new instance of <see cref="Repository{TQueryStrategy,TQueryBuilder,TData}"/>
48 : /// </summary>
49 1 : protected Repository(Func<IDataStore<TData>> createDataStoreFunction, TQueryBuilder queryBuilder)
50 : {
51 : CreateDataStoreFunction = createDataStoreFunction;
52 : QueryBuilder = queryBuilder;
53 : TelemetryHelper = new NullTelemetryHelper();
54 : }
55 :
56 : #region Implementation of IRepository<TData>
57 :
58 : /// <summary>
59 : /// Create the newly provided <paramref name="data"/> to storage.
60 : /// </summary>
61 1 : public virtual void Create(TData data)
62 : {
63 : using (var dataStore = CreateDataStoreFunction())
64 : dataStore.Add(data);
65 : }
66 :
67 : /// <summary>
68 : /// Create the newly provided <paramref name="data"/> to storage.
69 : /// </summary>
70 1 : public virtual void Create(IEnumerable<TData> data)
71 : {
72 : using (var dataStore = CreateDataStoreFunction())
73 : dataStore.Add(data);
74 : }
75 :
76 : /// <summary>
77 : /// Builds and executes the provided <paramref name="singleResultQuery"/>.
78 : /// </summary>
79 : /// <param name="singleResultQuery">The <see cref="ISingleResultQuery{TQueryStrategy,TData}"/> to build and execute.</param>
80 : /// <param name="throwExceptionWhenNoQueryResults">If true will throw an <see cref="Exception"/> if no data is found in storage.</param>
81 1 : public virtual ISingleResultQuery<TQueryStrategy, TData> Retrieve(ISingleResultQuery<TQueryStrategy, TData> singleResultQuery, bool throwExceptionWhenNoQueryResults = true)
82 : {
83 : // The .Select(i => i) is necessary due to inheritance
84 : // http://stackoverflow.com/questions/1021274/linq-to-sql-mapping-exception-when-using-abstract-base-classes
85 : IQueryable<TData> query = QueryBuilder.CreateQueryable(singleResultQuery).Select(i => i);
86 :
87 : IEnumerable<TData> result = query.AsEnumerable();
88 : int finalResultCount = 0;
89 : TData finalResult = throwExceptionWhenNoQueryResults
90 : ? result.Single()
91 : : result.SingleOrDefault();
92 : if (finalResult != null)
93 : finalResultCount = 1;
94 :
95 : try
96 : {
97 : return new SingleResultQuery<TQueryStrategy, TData>
98 : {
99 : QueryStrategy = singleResultQuery.QueryStrategy,
100 : Result = finalResult
101 : };
102 : }
103 : finally
104 : {
105 : TelemetryHelper.TrackMetric(string.Format("Cqrs/Repository/Retrieve/Single/{0}", typeof(TData).Name), finalResultCount);
106 :
107 : // This is disabled until I can figure out a better way to handle disposing these... as it will most likely dispose the data store... and that's not cool.
108 : /*
109 : var disposable = result as IDisposable;
110 : if (disposable != null)
111 : disposable.Dispose();
112 : */
113 : }
114 : }
115 :
116 : /// <summary>
117 : /// Builds and executes the provided <paramref name="resultQuery"/>.
118 : /// </summary>
119 : /// <param name="resultQuery">The <see cref="ICollectionResultQuery{TQueryStrategy,TData}"/> to build and execute.</param>
120 1 : public virtual ICollectionResultQuery<TQueryStrategy, TData> Retrieve(ICollectionResultQuery<TQueryStrategy, TData> resultQuery)
121 : {
122 : // The .Select(i => i) is necessary due to inheritance
123 : // http://stackoverflow.com/questions/1021274/linq-to-sql-mapping-exception-when-using-abstract-base-classes
124 : IQueryable<TData> result = QueryBuilder.CreateQueryable(resultQuery).Select(i => i);
125 :
126 : IList<TData> finalResult = result.ToList();
127 :
128 : try
129 : {
130 : return new CollectionResultQuery<TQueryStrategy, TData>
131 : {
132 : QueryStrategy = resultQuery.QueryStrategy,
133 : Result = finalResult
134 : };
135 : }
136 : finally
137 : {
138 : TelemetryHelper.TrackMetric(string.Format("Cqrs/Repository/Retrieve/Collection/{0}", typeof(TData).Name), finalResult.Count);
139 : // This is disabled until I can figure out a better way to handle disposing these... as it will most likely dispose the data store... and that's not cool.
140 : /*
141 : var disposable = result as IDisposable;
142 : if (disposable != null)
143 : disposable.Dispose();
144 : */
145 : }
146 : }
147 :
148 : /// <summary>
149 : /// Update the provided <paramref name="data"/> in storage.
150 : /// </summary>
151 1 : public virtual void Update(TData data)
152 : {
153 : using (var dataStore = CreateDataStoreFunction())
154 : dataStore.Update(data);
155 : }
156 :
157 : /// <summary>
158 : /// Will mark the <paramref name="data"/> as logically (or soft).
159 : /// </summary>
160 1 : public virtual void Delete(TData data)
161 : {
162 : using (var dataStore = CreateDataStoreFunction())
163 : dataStore.Remove(data);
164 : }
165 :
166 : /// <summary>
167 : /// Delete all contents (normally by use of a truncate operation) in storage.
168 : /// </summary>
169 1 : public virtual void DeleteAll()
170 : {
171 : using (var dataStore = CreateDataStoreFunction())
172 : dataStore.RemoveAll();
173 : }
174 :
175 : /// <summary>
176 : /// Remove the provided <paramref name="data"/> from storage.
177 : /// </summary>
178 1 : public void Destroy(TData data)
179 : {
180 : using (var dataStore = CreateDataStoreFunction())
181 : dataStore.Destroy(data);
182 : }
183 :
184 : /// <summary>
185 : /// Load the <typeparamref name="TData"/> from storage identified by the provided <paramref name="rsn"/>.
186 : /// </summary>
187 : /// <param name="rsn">The identifier if the <typeparamref name="TData"/> to load.</param>
188 : /// <param name="throwExceptionOnMissingEntity">If true will throw an <see cref="Exception"/> if no data is found in storage.</param>
189 1 : public virtual TData Load(Guid rsn, bool throwExceptionOnMissingEntity = true)
190 : {
191 : using (IDataStore<TData> dataStore = CreateDataStoreFunction())
192 : {
193 : IEnumerable<TData> query = dataStore
194 : // The .Select(i => i) is necessary due to inheritance
195 : // http://stackoverflow.com/questions/1021274/linq-to-sql-mapping-exception-when-using-abstract-base-classes
196 : .Select(i => i)
197 : .Where(entity => entity.Rsn == rsn)
198 : .AsEnumerable();
199 :
200 : int finalResultCount = 0;
201 : TData result = throwExceptionOnMissingEntity
202 : ? query.Single()
203 : : query.SingleOrDefault();
204 : if (result != null)
205 : finalResultCount = 1;
206 :
207 : try
208 : {
209 : return result;
210 : }
211 : finally
212 : {
213 : TelemetryHelper.TrackMetric(string.Format("Cqrs/Repository/Load/{0}", typeof(TData).Name), finalResultCount);
214 : }
215 : }
216 : }
217 :
218 : #endregion
219 :
220 : /// <summary>
221 : /// Calls <see cref="CreateDataStoreFunction"/> passing the <paramref name="predicate"/>.
222 : /// </summary>
223 : /// <param name="predicate">A function defining a filter if required.</param>
224 1 : protected virtual IQueryable<TData> CreateQueryable(Expression<Func<TData, bool>> predicate)
225 : {
226 : return CreateDataStoreFunction().Where(predicate);
227 : }
228 : }
229 : }
|