For several of my projects I use a repository pattern for the data access layer which sits above Fluent NHibernate. Using generics I can then replicate the same features for all entity repositories.
An example of the base interface is shown below.
1: /// <summary>
2: /// Base repository for nhibernate
3: /// </summary>
4: /// <typeparam name="T">Entity</typeparam>
5: public interface INHibernateBaseRepository<T>
6: {
7: /// <summary>
8: /// Gets the current session in the repository
9: /// </summary>
10: /// <returns>Current session</returns>
11: ISession GetCurrentSession();
12:
13: /// <summary>
14: /// Finds an entity by passed identifier
15: /// </summary>
16: /// <param name="id">Identifier</param>
17: /// <returns>Entity matching identifier</returns>
18: T FindById(long id);
19:
20: /// <summary>
21: /// Saves entity
22: /// </summary>
23: /// <param name="t">Entity to save</param>
24: void Save(T t);
25:
26: /// <summary>
27: /// Saves or updates entity
28: /// </summary>
29: /// <param name="t">Entity to save or update</param>
30: T SaveOrUpdate(T t);
31:
32: /// <summary>
33: /// Deletes entity
34: /// </summary>
35: /// <param name="t">Entity to delete</param>
36: void Delete(T t);
37:
38: /// <summary>
39: /// Finds all entities and passes back as a collection
40: /// </summary>
41: /// <returns>Collection of entities</returns>
42: IList<T> FindAll();
43:
44: /// <summary>
45: /// Finds all entities by passed order and passes back as a collection
46: /// </summary>
47: /// <param name="orders">Orders</param>
48: /// <returns>Collection of entities</returns>
49: IList<T> FindAll(params Order[] orders);
50:
51: /// <summary>
52: /// Finds all entities and passes back as a collection within the range specified
53: /// </summary>
54: /// <param name="firstResult">First result</param>
55: /// <param name="numberOfResults">Number of results to return</param>
56: /// <returns>Collection of entities</returns>
57: IList<T> FindAll(int firstResult, int numberOfResults);
58:
59: /// <summary>
60: /// Finds all entities and passes back as a collection within the range specified
61: /// </summary>
62: /// <param name="firstResult">First result</param>
63: /// <param name="numberOfResults">Number of results to return</param>
64: /// <param name="orders">Orders</param>
65: /// <returns>Collection of entities</returns>
66: IList<T> FindAll(int firstResult, int numberOfResults, params Order[] orders);
67:
68: /// <summary>
69: /// Finds all entities and passes back as a collection within the range specified
70: /// </summary>
71: /// <param name="firstResult">First result</param>
72: /// <param name="numberOfResults">Number of results</param>
73: /// <param name="sortFields">Sort order dictionary</param>
74: /// <returns>List of entities</returns>
75: IList<T> FindAll(int firstResult, int numberOfResults, IDictionary<string, bool> sortFields);
76:
77: /// <summary>
78: /// Finds entities by criteria
79: /// </summary>
80: /// <param name="dc">Detatched criteria</param>
81: /// <returns>Collection of entities that match the passed criteria</returns>
82: IList<T> FindByCriteria(DetachedCriteria dc);
83:
84: /// <summary>
85: /// Finds entities by criteria and paging
86: /// </summary>
87: /// <param name="dc">Detatched criteria</param>
88: /// <param name="firstResult">First result</param>
89: /// <param name="numberOfResults">Number of results to return</param>
90: /// <returns>Collection of entities that match the passed criteria</returns>
91: IList<T> FindByCriteria(DetachedCriteria dc, int firstResult, int numberOfResults);
92:
93: /// <summary>
94: /// Finds entities by criteria, paging and ordering
95: /// </summary>
96: /// <param name="dc">Detatched criteria</param>
97: /// <param name="firstResult">First result</param>
98: /// <param name="numberOfResults">Number of results to return</param>
99: /// <param name="orders">Orders</param>
100: /// <returns>Collection of entities that match the passed criteria</returns>
101: IList<T> FindByCriteria(DetachedCriteria dc, int firstResult, int numberOfResults, params Order[] orders);
102:
103: /// <summary>
104: /// Gets a list of entities by passed query
105: /// </summary>
106: /// <param name="queryString">Query to get entities</param>
107: /// <returns>Collection of entities</returns>
108: IList<T> FindByQuery(string queryString);
109:
110: /// <summary>
111: /// Gets a list of entities by passed query and parameters
112: /// </summary>
113: /// <param name="queryString">Query to get entities</param>
114: /// <param name="parameters">Parameters to set in query</param>
115: /// <returns>Collection of entities</returns>
116: IList<T> FindByQuery(string queryString, params object[] parameters);
117:
118: /// <summary>
119: /// Gets a list of entities by passed query
120: /// </summary>
121: /// <param name="queryString">Query to get entities</param>
122: /// <param name="firstResult">First result</param>
123: /// <param name="numberOfResults">Number of results to return</param>
124: /// <returns>Collection of entities</returns>
125: IList<T> FindByQuery(string queryString, int firstResult, int numberOfResults);
126:
127: /// <summary>
128: /// Gets a list of entities by passed query and parameters
129: /// </summary>
130: /// <param name="queryString">Query to get entities</param>
131: /// <param name="firstResult">First result</param>
132: /// <param name="numberOfResults">Number of results to return</param>
133: /// <param name="parameters">Parameters to set in query</param>
134: /// <returns>Collection of entities</returns>
135: IList<T> FindByQuery(string queryString, int firstResult, int numberOfResults, params object[] parameters);
136:
137: /// <summary>
138: /// Finds one entity that matches criteria and throws exception otherwise
139: /// </summary>
140: /// <param name="criteria">Criteria</param>
141: /// <returns>Entity matching criteria</returns>
142: T FindOne(DetachedCriteria criteria);
143:
144: /// <summary>
145: /// Finds first entity in collection that matches criteria
146: /// </summary>
147: /// <param name="criteria">Criteria</param>
148: /// <returns>First entity</returns>
149: T FindFirst(DetachedCriteria criteria);
150:
151: /// <summary>
152: /// Finds first entity in collection that matches criteria and order params
153: /// </summary>
154: /// <param name="criteria">Criteria</param>
155: /// <param name="orders">Orders</param>
156: /// <returns>First entity</returns>
157: T FindFirst(DetachedCriteria criteria, params Order[] orders);
158:
159: /// <summary>
160: /// Get count of entities that match criteria
161: /// </summary>
162: /// <param name="criteria">Criteria</param>
163: /// <returns>Count of number of entities that match criteria</returns>
164: long Count(DetachedCriteria criteria);
165:
166: /// <summary>
167: /// Get count of entities
168: /// </summary>
169: /// <returns>Count of entities</returns>
170: long Count();
171:
172: /// <summary>
173: /// Determines if at least one entity exists that matches passed criteria
174: /// </summary>
175: /// <param name="criteria">Criteria</param>
176: /// <returns><c>true</c> if exists</returns>
177: bool Exists(DetachedCriteria criteria);
178: }
Then a base class to implement this interface - as follows:
1: #region Usings
2:
3: using System;
4: using System.Collections.Generic;
5: using Castle.Facilities.NHibernateIntegration;
6: using AimKai.Core.Interfaces;
7: using AimKai.Core.Resources;
8: using AimKai.Core.Util;
9: using log4net;
10: using NHibernate;
11: using NHibernate.Criterion;
12: using System.Linq;
13:
14: #endregion Usings
15:
16: /// <summary>
17: /// Base nhibernate repository
18: /// </summary>
19: /// <typeparam name="T">Type</typeparam>
20: public abstract class NHibernateBaseRepository<T> : INHibernateBaseRepository<T>
21: {
22: #region Fields
23:
24: /// <summary>
25: /// Session manager
26: /// </summary>
27: protected readonly ISessionManager SessionManager;
28:
29: #endregion Fields
30:
31: #region Properties
32:
33: /// <summary>
34: /// Gets the current session
35: /// </summary>
36: public virtual ISession Session
37: {
38: get { return SessionManager.OpenSession(); }
39: }
40:
41: /// <summary>
42: /// Gets the base log here
43: /// </summary>
44: public ILog NHibernateBaseRepositoryLog = LogManager.GetLogger(typeof (NHibernateBaseRepository<T>));
45:
46: #endregion Properties
47:
48: #region Constructors
49:
50: /// <summary>
51: /// Set session manager via constructor
52: /// </summary>
53: /// <param name="sessionManager"></param>
54: protected NHibernateBaseRepository(ISessionManager sessionManager)
55: {
56: SessionManager = sessionManager;
57: }
58:
59: #endregion Constructors
60:
61: #region Public Methods
62:
63: #region INHibernateBaseRespository<T> Members
64:
65: /// <summary>
66: /// Gets the current session in the repository
67: /// </summary>
68: /// <returns>Current session</returns>
69: public ISession GetCurrentSession()
70: {
71: return Session;
72: }
73:
74: /// <summary>
75: /// Finds an entity by passed identifier
76: /// </summary>
77: /// <param name="id">Identifier</param>
78: /// <returns>Entity matching identifier</returns>
79: public T FindById(long id)
80: {
81: return Session.Get<T>(id);
82: }
83:
84: /// <summary>
85: /// Loads an entity by passed identifier
86: /// </summary>
87: /// <param name="id">Identifier</param>
88: /// <returns>Entity matching identifier</returns>
89: public T Load(long id)
90: {
91: return Session.Load<T>(id);
92: }
93:
94: /// <summary>
95: /// Saves entity
96: /// </summary>
97: /// <param name="t">Entity to save</param>
98: public void Save(T t)
99: {
100: //Use transaction here
101: using (var tx = Session.BeginTransaction())
102: {
103: try
104: {
105: //Check to see if we can validate
106: var validateMethod = t.GetType().GetMethod("Validate");
107: if (validateMethod != null)
108: validateMethod.Invoke(t, null);
109:
110: //Save here
111: Session.Save(t);
112: tx.Commit();
113: }
114: catch (Exception ex)
115: {
116: tx.Rollback();
117:
118: // throw not saved exception
119: NHibernateBaseRepositoryLog.Error(ex);
120: throw new CoreException("NHibernateBaseRepository_NotSaved", ExceptionMessages.NHibernateBaseRepository_NotSaved, ex,
121: new object[] {"Save", ex.Message});
122: }
123: }
124: }
125:
126: /// <summary>
127: /// Saves or updates entity
128: /// </summary>
129: /// <param name="t">Entity to save or update</param>
130: public T SaveOrUpdate(T t)
131: {
132: //Use transaction here
133: using (var tx = Session.BeginTransaction())
134: {
135: try
136: {
137: Session.SaveOrUpdate(t);
138: tx.Commit();
139: }
140: catch (Exception ex)
141: {
142: tx.Rollback();
143:
144: // throw not saved exception
145: NHibernateBaseRepositoryLog.Error(ex);
146: throw new CoreException("NHibernateBaseRepository_NotSaved",
147: ExceptionMessages.NHibernateBaseRepository_NotSaved, ex,
148: new object[] {"Save or Update", ex.Message});
149: }
150: }
151: return t;
152: }
153:
154: /// <summary>
155: /// Deletes entity
156: /// </summary>
157: /// <param name="t">Entity to delete</param>
158: public void Delete(T t)
159: {
160: using (var tx = Session.BeginTransaction())
161: {
162: try
163: {
164: Session.Delete(t);
165: tx.Commit();
166: }
167: catch (Exception ex)
168: {
169: tx.Rollback();
170:
171: // throw not saved exception
172: NHibernateBaseRepositoryLog.Error(ex);
173: throw new CoreException("NHibernateBaseRepository_NotSaved",
174: ExceptionMessages.NHibernateBaseRepository_NotSaved, ex,
175: new object[] { "Delete", ex.Message });
176: }
177: }
178: }
179:
180: /// <summary>
181: /// Finds all entities and passes back as a collection
182: /// </summary>
183: /// <returns>Collection of entities</returns>
184: public IList<T> FindAll()
185: {
186: //Builds a criteria to pull back a collection of all items that match this type
187: return Session.CreateCriteria(typeof(T)).List<T>();
188: }
189:
190: /// <summary>
191: /// Finds all entities by passed order and passes back as a collection
192: /// </summary>
193: /// <param name="orders">Orders</param>
194: /// <returns>Collection of entities</returns>
195: public IList<T> FindAll(params Order[] orders)
196: {
197: return AddOrders(Session.CreateCriteria(typeof(T))).List<T>();
198: }
199:
200: /// <summary>
201: /// Finds all entities and passes back as a collection within the range specified
202: /// </summary>
203: /// <param name="firstResult">First result</param>
204: /// <param name="numberOfResults">Number of results to return</param>
205: /// <returns>Collection of entities</returns>
206: public IList<T> FindAll(int firstResult, int numberOfResults)
207: {
208: return Session.CreateCriteria(typeof(T))
209: .SetFirstResult(firstResult)
210: .SetMaxResults(numberOfResults)
211: .List<T>();
212: }
213:
214: /// <summary>
215: /// Finds all entities and passes back as a collection within the range specified
216: /// </summary>
217: /// <param name="firstResult">First result</param>
218: /// <param name="numberOfResults">Number of results to return</param>
219: /// <param name="orders">Orders</param>
220: /// <returns>Collection of entities</returns>
221: public IList<T> FindAll(int firstResult, int numberOfResults, params Order[] orders)
222: {
223: return AddOrders(Session.CreateCriteria(typeof(T)))
224: .SetMaxResults(numberOfResults)
225: .SetFirstResult(firstResult)
226: .List<T>();
227: }
228:
229: /// <summary>
230: /// Finds all entities and passes back as a collection within the range specified
231: /// </summary>
232: /// <param name="firstResult">First result</param>
233: /// <param name="numberOfResults">Number of results</param>
234: /// <param name="sortFields">Sort order dictionary</param>
235: /// <returns>Collection of entities</returns>
236: public IList<T> FindAll(int firstResult, int numberOfResults, IDictionary<string, bool> sortFields)
237: {
238: var orders = new List<Order>();
239: foreach (var item in sortFields)
240: {
241: var item1 = item;
242: if (typeof(T).GetProperties().Where(x => x.Name == item1.Key).Count() == 0)
243: throw new CoreException("NHibernateBaseRepository_SortParameterNotValid",
244: ExceptionMessages.NHibernateBaseRepository_SortParameterNotValid,
245: new object[] { item1.Key });
246: orders.Add(new Order(item1.Key, item1.Value));
247: }
248: return AddOrders(Session.CreateCriteria(typeof(T)))
249: .SetMaxResults(numberOfResults)
250: .SetFirstResult(firstResult)
251: .List<T>();
252: }
253:
254: /// <summary>
255: /// Finds entities by criteria
256: /// </summary>
257: /// <param name="dc">Detatched criteria</param>
258: /// <returns>Collection of entities that match the passed criteria</returns>
259: public IList<T> FindByCriteria(DetachedCriteria dc)
260: {
261: return dc.GetExecutableCriteria(Session).List<T>();
262: }
263:
264: /// <summary>
265: /// Finds entities by criteria and paging
266: /// </summary>
267: /// <param name="dc">Detatched criteria</param>
268: /// <param name="firstResult">First result</param>
269: /// <param name="numberOfResults">Number of results to return</param>
270: /// <returns>Collection of entities that match the passed criteria</returns>
271: public IList<T> FindByCriteria(DetachedCriteria dc, int firstResult, int numberOfResults)
272: {
273: //Set paging
274: dc.SetFirstResult(firstResult).SetMaxResults(numberOfResults);
275:
276: //Execute criteria
277: return dc.GetExecutableCriteria(Session).List<T>();
278: }
279:
280: /// <summary>
281: /// Finds entities by criteria, paging and ordering
282: /// </summary>
283: /// <param name="dc">Detatched criteria</param>
284: /// <param name="firstResult">First result</param>
285: /// <param name="numberOfResults">Number of results to return</param>
286: /// <param name="orders">Orders</param>
287: /// <returns>Collection of entities that match the passed criteria</returns>
288: public IList<T> FindByCriteria(DetachedCriteria dc, int firstResult, int numberOfResults, params Order[] orders)
289: {
290: return AddOrders(dc, orders).SetFirstResult(firstResult).SetMaxResults(numberOfResults)
291: .GetExecutableCriteria(Session).List<T>();
292: }
293:
294: /// <summary>
295: /// Gets a list of entities by passed query
296: /// </summary>
297: /// <param name="queryString">Query to get entities</param>
298: /// <returns>Collection of entities</returns>
299: public IList<T> FindByQuery(string queryString)
300: {
301: return Session.CreateQuery(queryString).List<T>();
302: }
303:
304: /// <summary>
305: /// Gets a list of entities by passed query and parameters
306: /// </summary>
307: /// <param name="queryString">Query to get entities</param>
308: /// <param name="parameters">Parameters to set in query</param>
309: /// <returns>Collection of entities</returns>
310: public IList<T> FindByQuery(string queryString, params object[] parameters)
311: {
312: var query = Session.CreateQuery(queryString);
313: if (parameters != null)
314: {
315: var count = 0;
316: foreach (var parameter in parameters)
317: {
318: query.SetParameter(count, parameter);
319: count++;
320: }
321: }
322: return query.List<T>();
323: }
324:
325: /// <summary>
326: /// Gets a list of entities by passed query
327: /// </summary>
328: /// <param name="queryString">Query to get entities</param>
329: /// <param name="firstResult">First result</param>
330: /// <param name="numberOfResults">Number of results to return</param>
331: /// <returns>Collection of entities</returns>
332: public IList<T> FindByQuery(string queryString, int firstResult, int numberOfResults)
333: {
334: return Session.CreateQuery(queryString)
335: .SetFirstResult(firstResult)
336: .SetMaxResults(numberOfResults)
337: .List<T>();
338: }
339:
340: /// <summary>
341: /// Gets a list of entities by passed query and parameters
342: /// </summary>
343: /// <param name="queryString">Query to get entities</param>
344: /// <param name="firstResult">First result</param>
345: /// <param name="numberOfResults">Number of results to return</param>
346: /// <param name="parameters">Parameters to set in query</param>
347: /// <returns>Collection of entities</returns>
348: public IList<T> FindByQuery(string queryString, int firstResult, int numberOfResults, params object[] parameters)
349: {
350: var query = Session.CreateQuery(queryString);
351: if (parameters != null)
352: {
353: var count = 0;
354: foreach (var parameter in parameters)
355: {
356: query.SetParameter(count, parameter);
357: count++;
358: }
359: }
360: return query.List<T>();
361: }
362:
363: /// <summary>
364: /// Finds one entity that matches criteria and throws exception otherwise
365: /// </summary>
366: /// <param name="criteria">Criteria</param>
367: /// <returns>Entity matching criteria</returns>
368: public T FindOne(DetachedCriteria criteria)
369: {
370: return criteria.GetExecutableCriteria(Session).UniqueResult<T>();
371: }
372:
373: /// <summary>
374: /// Finds first entity in collection that matches criteria
375: /// </summary>
376: /// <param name="criteria">Criteria</param>
377: /// <returns>First entity</returns>
378: public T FindFirst(DetachedCriteria criteria)
379: {
380: //Do we have any results?
381: var results = criteria.SetMaxResults(1)
382: .GetExecutableCriteria(Session).List<T>();
383:
384: return (results.Count > 0) ? results[0] : default(T);
385: }
386:
387: /// <summary>
388: /// Finds first entity in collection that matches criteria and order params
389: /// <remarks>Can be used to get last in a collection</remarks>
390: /// </summary>
391: /// <param name="criteria">Criteria</param>
392: /// <param name="orders">Orders</param>
393: /// <returns>First entity</returns>
394: public T FindFirst(DetachedCriteria criteria, params Order[] orders)
395: {
396: var results = AddOrders(criteria, orders).SetMaxResults(1)
397: .GetExecutableCriteria(Session).List<T>();
398:
399: return (results.Count > 0) ? results[0] : default(T);
400: }
401:
402: /// <summary>
403: /// Get count of entities that match criteria
404: /// </summary>
405: /// <param name="criteria">Criteria</param>
406: /// <returns>Count of number of entities that match criteria</returns>
407: public long Count(DetachedCriteria criteria)
408: {
409: return Convert.ToInt64(criteria.GetExecutableCriteria(Session)
410: .SetProjection(Projections.RowCountInt64()).UniqueResult());
411: }
412:
413: /// <summary>
414: /// Get count of entities
415: /// </summary>
416: /// <returns>Count of entities</returns>
417: public long Count()
418: {
419: return Convert.ToInt64(Session.CreateCriteria(typeof(T)).SetProjection(Projections.RowCountInt64()).UniqueResult());
420: }
421:
422: /// <summary>
423: /// Determines if at least one entity exists that matches passed criteria
424: /// </summary>
425: /// <param name="criteria">Criteria</param>
426: /// <returns><c>true</c> if exists</returns>
427: public bool Exists(DetachedCriteria criteria)
428: {
429: return Count(criteria) > 0;
430: }
431:
432: #endregion INHibernateBaseRespository<T> Members
433:
434: #endregion Public Methods
435:
436: #region Private Methods
437:
438: /// <summary>
439: /// Adds orders to detached criteria
440: /// </summary>
441: /// <param name="dc">Detached criteria</param>
442: /// <param name="orders">Orders</param>
443: /// <returns>Criteria with orders</returns>
444: private static DetachedCriteria AddOrders(DetachedCriteria dc, params Order[] orders)
445: {
446: //Build orders
447: if (orders != null)
448: foreach (var order in orders)
449: dc.AddOrder(order);
450:
451: return dc;
452: }
453:
454: /// <summary>
455: /// Adds orders to detached criteria
456: /// </summary>
457: /// <param name="dc">criteria</param>
458: /// <param name="orders">Orders</param>
459: /// <returns>Criteria with orders</returns>
460: private static ICriteria AddOrders(ICriteria dc, params Order[] orders)
461: {
462: //Build orders
463: if (orders != null)
464: foreach (var order in orders)
465: dc.AddOrder(order);
466:
467: return dc;
468: }
469:
470: #endregion Private Methods
471: }
Then say if I have an entity called 'Mountain' I would name a repository 'MountainRepository' which implements the interface IMountainRepository:
1: /// <summary>
2: /// Provides Mountain repository functionality
3: /// </summary>
4: public interface IMountainRepository : INHibernateBaseRepository<Mountain>
5: {
6: /// <summary>
7: /// Get mountains by height
8: /// </summary>
9: /// <param name="height">Height of mountain</param>
10: IList<Mountain> GetMountainsByHeight(double height);
11: }
and concrete implementation:
1: /// <summary>
2: /// Provides user group repository functionality
3: /// </summary>
4: public class MountainRepository: NHibernateBaseRepository<Mountain>, IMountainRepository
5: {
6: #region Constructors
7:
8: /// <summary>
9: /// Constructor for the suv user group restriction repository
10: /// </summary>
11: /// <param name="sessionManager">Session manager</param>
12: public SuvUserGroupRepository(ISessionManager sessionManager)
13: : base(sessionManager)
14: {
15: }
16:
17: #endregion Constructors
18:
19: #region Public Methods
20:
21: #region IMountainRepository Members
22:
23: /// <summary>
24: /// Get mountains by height
25: /// </summary>
26: /// <param name="height">Height of mountain</param>
27: public IList<Mountain> GetMountainsByHeight(double height)
28: {
29: return FindAll(DetachedCriteria.For<Mountain>().Add<Mountain>(x => x.Height == height));
30: }
31:
32: #endregion IMountainRepository Members
33:
34: #endregion Public Methods
35: }
So the repository classes are all simpler and I have still access to the 20 odd base methods which have all the basic NHibernate functionality required.
No comments:
Post a Comment