Fixing Brail Templates for VS Net wizards.
[castle.git] / ActiveRecord / Castle.ActiveRecord / Framework / ActiveRecordBase.cs
blob7a33aaa04231e52a81cc7d4f1df00225202780f1
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.ActiveRecord
17 using System;
18 using System.Collections;
19 using Castle.ActiveRecord.Framework;
20 using Castle.ActiveRecord.Framework.Internal;
21 using Castle.ActiveRecord.Queries;
23 using NHibernate;
24 using NHibernate.Expression;
25 using Castle.Components.Validator;
27 /// <summary>
28 /// Allow custom executions using the NHibernate's ISession.
29 /// </summary>
30 public delegate object NHibernateDelegate(ISession session, object instance);
32 /// <summary>
33 /// Base class for all ActiveRecord classes. Implements
34 /// all the functionality to simplify the code on the
35 /// subclasses.
36 /// </summary>
37 [Serializable]
38 public abstract class ActiveRecordBase : ActiveRecordHooksBase
40 /// <summary>
41 /// The global holder for the session factories.
42 /// </summary>
43 protected internal static ISessionFactoryHolder holder;
45 #region internal static
47 internal static void EnsureInitialized(Type type)
49 if (holder == null)
51 String message = String.Format("An ActiveRecord class ({0}) was used but the framework seems not " +
52 "properly initialized. Did you forget about ActiveRecordStarter.Initialize() ?",
53 type.FullName);
54 throw new ActiveRecordException(message);
56 if (type != typeof(ActiveRecordBase) && GetModel(type) == null)
58 String message = String.Format("You have accessed an ActiveRecord class that wasn't properly initialized. " +
59 "The only explanation is that the call to ActiveRecordStarter.Initialize() didn't include {0} class",
60 type.FullName);
61 throw new ActiveRecordException(message);
65 /// <summary>
66 /// Internally used
67 /// </summary>
68 /// <param name="arType">The type.</param>
69 /// <param name="model">The model.</param>
70 internal static void Register(Type arType, ActiveRecordModel model)
72 ActiveRecordModel.Register(arType, model);
75 /// <summary>
76 /// Internally used
77 /// </summary>
78 /// <param name="arType">The type.</param>
79 /// <returns>An <see cref="ActiveRecordModel"/></returns>
80 internal static ActiveRecordModel GetModel(Type arType)
82 return ActiveRecordModel.GetModel(arType);
85 #endregion
87 #region protected internal static
89 #region Create/Update/Save/Delete/DeleteAll/Refresh
91 #region Create
93 /// <summary>
94 /// Creates (Saves) a new instance to the database.
95 /// </summary>
96 /// <param name="instance">The ActiveRecord instance to be created on the database</param>
97 protected internal static void Create(object instance)
99 InternalCreate(instance, false);
102 /// <summary>
103 /// Creates (Saves) a new instance to the database and flushes the session.
104 /// </summary>
105 /// <param name="instance">The ActiveRecord instance to be created on the database</param>
106 protected internal static void CreateAndFlush(object instance)
108 InternalCreate(instance, true);
111 /// <summary>
112 /// Creates (Saves) a new instance to the database.
113 /// </summary>
114 /// <param name="instance">The ActiveRecord instance to be created on the database</param>
115 /// <param name="flush">if set to <c>true</c>, the operation will be followed by a session flush.</param>
116 private static void InternalCreate(object instance, bool flush)
118 if (instance == null) throw new ArgumentNullException("instance");
120 EnsureInitialized(instance.GetType());
122 ISession session = holder.CreateSession(instance.GetType());
126 session.Save(instance);
128 if (flush)
130 session.Flush();
133 catch(Exception ex)
135 holder.FailSession(session);
137 // NHibernate catches our ValidationException, and as such it is the innerexception here
138 if (ex.InnerException is ValidationException)
140 throw ex.InnerException;
142 else
144 throw new ActiveRecordException("Could not perform Create for " + instance.GetType().Name, ex);
147 finally
149 holder.ReleaseSession(session);
153 #endregion
155 #region Delete
157 /// <summary>
158 /// Deletes the instance from the database.
159 /// </summary>
160 /// <param name="instance">The ActiveRecord instance to be deleted</param>
161 protected internal static void Delete(object instance)
163 InternalDelete(instance, false);
166 /// <summary>
167 /// Deletes the instance from the database and flushes the session.
168 /// </summary>
169 /// <param name="instance">The ActiveRecord instance to be deleted</param>
170 protected internal static void DeleteAndFlush(object instance)
172 InternalDelete(instance, true);
175 /// <summary>
176 /// Deletes the instance from the database.
177 /// </summary>
178 /// <param name="instance">The ActiveRecord instance to be deleted</param>
179 /// <param name="flush">if set to <c>true</c>, the operation will be followed by a session flush.</param>
180 private static void InternalDelete(object instance, bool flush)
182 if (instance == null) throw new ArgumentNullException("instance");
184 EnsureInitialized(instance.GetType());
186 ISession session = holder.CreateSession(instance.GetType());
190 session.Delete(instance);
192 if (flush)
194 session.Flush();
197 catch (ValidationException)
199 holder.FailSession(session);
201 throw;
204 catch (Exception ex)
206 holder.FailSession(session);
208 throw new ActiveRecordException("Could not perform Delete for " + instance.GetType().Name, ex);
210 finally
212 holder.ReleaseSession(session);
216 #endregion
218 #region Replicate
220 /// <summary>
221 /// From NHibernate documentation:
222 /// Persist all reachable transient objects, reusing the current identifier
223 /// values. Note that this will not trigger the Interceptor of the Session.
224 /// </summary>
225 /// <param name="instance">The instance.</param>
226 /// <param name="replicationMode">The replication mode.</param>
227 protected internal static void Replicate(object instance, ReplicationMode replicationMode)
229 if (instance == null)
231 throw new ArgumentNullException("instance");
234 EnsureInitialized(instance.GetType());
236 ISession session = holder.CreateSession(instance.GetType());
240 session.Replicate(instance, replicationMode);
242 catch(Exception ex)
244 holder.FailSession(session);
246 // NHibernate catches our ValidationException, and as such it is the innerexception here
247 if (ex.InnerException is ValidationException)
249 throw ex.InnerException;
251 else
253 throw new ActiveRecordException("Could not perform Replicate for " + instance.GetType().Name, ex);
256 finally
258 holder.ReleaseSession(session);
262 #endregion
264 #region Refresh
266 /// <summary>
267 /// Refresh the instance from the database.
268 /// </summary>
269 /// <param name="instance">The ActiveRecord instance to be reloaded</param>
270 protected internal static void Refresh(object instance)
272 if (instance == null) throw new ArgumentNullException("instance");
274 EnsureInitialized(instance.GetType());
276 ISession session = holder.CreateSession(instance.GetType());
280 session.Refresh(instance);
282 catch (Exception ex)
284 holder.FailSession(session);
286 // NHibernate catches our ValidationException, and as such it is the innerexception here
287 if (ex.InnerException is ValidationException)
289 throw ex.InnerException;
291 else
293 throw new ActiveRecordException("Could not perform Refresh for " + instance.GetType().Name, ex);
296 finally
298 holder.ReleaseSession(session);
302 #endregion
304 #region DeleteAll
306 /// <summary>
307 /// Deletes all rows for the specified ActiveRecord type
308 /// </summary>
309 /// <remarks>
310 /// This method is usually useful for test cases.
311 /// </remarks>
312 /// <param name="type">ActiveRecord type on which the rows on the database should be deleted</param>
313 protected internal static void DeleteAll(Type type)
315 EnsureInitialized(type);
317 ISession session = holder.CreateSession(type);
321 session.Delete(String.Format("from {0}", type.Name));
323 session.Flush();
325 catch (ValidationException)
327 holder.FailSession(session);
329 throw;
331 catch (Exception ex)
333 holder.FailSession(session);
335 throw new ActiveRecordException("Could not perform DeleteAll for " + type.Name, ex);
337 finally
339 holder.ReleaseSession(session);
343 /// <summary>
344 /// Deletes all rows for the specified ActiveRecord type that matches
345 /// the supplied HQL condition
346 /// </summary>
347 /// <remarks>
348 /// This method is usually useful for test cases.
349 /// </remarks>
350 /// <param name="type">ActiveRecord type on which the rows on the database should be deleted</param>
351 /// <param name="where">HQL condition to select the rows to be deleted</param>
352 protected internal static void DeleteAll(Type type, String where)
354 EnsureInitialized(type);
356 ISession session = holder.CreateSession(type);
360 session.Delete(String.Format("from {0} where {1}", type.Name, where));
362 session.Flush();
364 catch (ValidationException)
366 holder.FailSession(session);
368 throw;
370 catch (Exception ex)
372 holder.FailSession(session);
374 throw new ActiveRecordException("Could not perform DeleteAll for " + type.Name, ex);
376 finally
378 holder.ReleaseSession(session);
382 /// <summary>
383 /// Deletes all <paramref name="targetType" /> objects, based on the primary keys
384 /// supplied on <paramref name="pkValues" />.
385 /// </summary>
386 /// <param name="targetType">The target ActiveRecord type</param>
387 /// <param name="pkValues">A list of primary keys</param>
388 /// <returns>The number of objects deleted</returns>
389 protected internal static int DeleteAll(Type targetType, IEnumerable pkValues)
391 if (pkValues == null)
393 return 0;
396 int counter = 0;
398 foreach (object pk in pkValues)
400 Object obj = FindByPrimaryKey(targetType, pk, false);
402 if (obj != null)
404 ActiveRecordBase arBase = obj as ActiveRecordBase;
406 if (arBase != null)
408 arBase.Delete(); // in order to allow override of the virtual "Delete()" method
410 else
412 Delete(obj);
415 counter++;
419 return counter;
422 #endregion
424 #region Update
426 /// <summary>
427 /// Persists the modification on the instance
428 /// state to the database.
429 /// </summary>
430 /// <param name="instance">The ActiveRecord instance to be updated on the database</param>
431 protected internal static void Update(object instance)
433 InternalUpdate(instance, false);
436 /// <summary>
437 /// Persists the modification on the instance
438 /// state to the database and flushes the session.
439 /// </summary>
440 /// <param name="instance">The ActiveRecord instance to be updated on the database</param>
441 protected internal static void UpdateAndFlush(object instance)
443 InternalUpdate(instance, true);
446 /// <summary>
447 /// Persists the modification on the instance
448 /// state to the database.
449 /// </summary>
450 /// <param name="instance">The ActiveRecord instance to be updated on the database</param>
451 /// <param name="flush">if set to <c>true</c>, the operation will be followed by a session flush.</param>
452 private static void InternalUpdate(object instance, bool flush)
454 if (instance == null) throw new ArgumentNullException("instance");
456 EnsureInitialized(instance.GetType());
458 ISession session = holder.CreateSession(instance.GetType());
462 session.Update(instance);
464 if (flush)
466 session.Flush();
469 catch (ValidationException)
471 holder.FailSession(session);
473 throw;
475 catch (Exception ex)
477 holder.FailSession(session);
479 throw new ActiveRecordException("Could not perform Update for " + instance.GetType().Name, ex);
481 finally
483 holder.ReleaseSession(session);
487 #endregion
489 #region Save
491 /// <summary>
492 /// Saves the instance to the database. If the primary key is unitialized
493 /// it creates the instance on the database. Otherwise it updates it.
494 /// <para>
495 /// If the primary key is assigned, then you must invoke <see cref="Create()"/>
496 /// or <see cref="Update()"/> instead.
497 /// </para>
498 /// </summary>
499 /// <param name="instance">The ActiveRecord instance to be saved</param>
500 protected internal static void Save(object instance)
502 InternalSave(instance, false);
505 /// <summary>
506 /// Saves the instance to the database and flushes the session. If the primary key is unitialized
507 /// it creates the instance on the database. Otherwise it updates it.
508 /// <para>
509 /// If the primary key is assigned, then you must invoke <see cref="Create()"/>
510 /// or <see cref="Update()"/> instead.
511 /// </para>
512 /// </summary>
513 /// <param name="instance">The ActiveRecord instance to be saved</param>
514 protected internal static void SaveAndFlush(object instance)
516 InternalSave(instance, true);
519 /// <summary>
520 /// Saves a copy of the instance to the database. If the primary key is unitialized
521 /// it creates the instance on the database. Otherwise it updates it.
522 /// <para>
523 /// If the primary key is assigned, then you must invoke <see cref="Create()"/>
524 /// or <see cref="Update()"/> instead.
525 /// </para>
526 /// </summary>
527 /// <param name="instance">The transient instance to be saved</param>
528 /// <returns>The saved ActiveRecord instance</returns>
529 protected internal static object SaveCopy(object instance)
531 return InternalSaveCopy(instance, false);
534 /// <summary>
535 /// Saves a copy of the instance to the database and flushes the session. If the primary key is unitialized
536 /// it creates the instance on the database. Otherwise it updates it.
537 /// <para>
538 /// If the primary key is assigned, then you must invoke <see cref="Create()"/>
539 /// or <see cref="Update()"/> instead.
540 /// </para>
541 /// </summary>
542 /// <param name="instance">The transient instance to be saved</param>
543 /// <returns>The saved ActiveRecord instance</returns>
544 protected internal static object SaveCopyAndFlush(object instance)
546 return InternalSaveCopy(instance, true);
549 /// <summary>
550 /// Saves the instance to the database. If the primary key is unitialized
551 /// it creates the instance on the database. Otherwise it updates it.
552 /// <para>
553 /// If the primary key is assigned, then you must invoke <see cref="Create()"/>
554 /// or <see cref="Update()"/> instead.
555 /// </para>
556 /// </summary>
557 /// <param name="instance">The ActiveRecord instance to be saved</param>
558 /// <param name="flush">if set to <c>true</c>, the operation will be followed by a session flush.</param>
559 private static void InternalSave(object instance, bool flush)
561 if (instance == null) throw new ArgumentNullException("instance");
563 EnsureInitialized(instance.GetType());
565 ISession session = holder.CreateSession(instance.GetType());
569 session.SaveOrUpdate(instance);
571 if (flush)
573 session.Flush();
576 catch (ValidationException)
578 holder.FailSession(session);
580 throw;
582 catch(Exception ex)
584 holder.FailSession(session);
586 // NHibernate catches our ValidationException on Create so it could be the innerexception here
587 if (ex.InnerException is ValidationException)
589 throw ex.InnerException;
591 else
593 throw new ActiveRecordException("Could not perform Save for " + instance.GetType().Name, ex);
596 finally
598 holder.ReleaseSession(session);
602 /// <summary>
603 /// Saves a copy of the instance to the database. If the primary key is unitialized
604 /// it creates the instance on the database. Otherwise it updates it.
605 /// <para>
606 /// If the primary key is assigned, then you must invoke <see cref="Create()"/>
607 /// or <see cref="Update()"/> instead.
608 /// </para>
609 /// </summary>
610 /// <param name="instance">The transient instance to be saved</param>
611 /// <param name="flush">if set to <c>true</c>, the operation will be followed by a session flush.</param>
612 /// <returns>The saved ActiveRecord instance.</returns>
613 private static object InternalSaveCopy(object instance, bool flush)
615 if (instance == null) throw new ArgumentNullException("instance");
617 EnsureInitialized(instance.GetType());
619 ISession session = holder.CreateSession(instance.GetType());
623 object persistent = session.SaveOrUpdateCopy(instance);
625 if (flush)
627 session.Flush();
630 return persistent;
632 catch (ValidationException)
634 holder.FailSession(session);
636 throw;
638 catch (Exception ex)
640 holder.FailSession(session);
642 // NHibernate catches our ValidationException on Create so it could be the innerexception here
643 if (ex.InnerException is ValidationException)
645 throw ex.InnerException;
647 else
649 throw new ActiveRecordException("Could not perform SaveCopy for " + instance.GetType().Name, ex);
652 finally
654 holder.ReleaseSession(session);
658 #endregion
660 #endregion
662 #region Execute
664 /// <summary>
665 /// Invokes the specified delegate passing a valid
666 /// NHibernate session. Used for custom NHibernate queries.
667 /// </summary>
668 /// <param name="targetType">The target ActiveRecordType</param>
669 /// <param name="call">The delegate instance</param>
670 /// <param name="instance">The ActiveRecord instance</param>
671 /// <returns>Whatever is returned by the delegate invocation</returns>
672 protected internal static object Execute(Type targetType, NHibernateDelegate call, object instance)
674 if (targetType == null) throw new ArgumentNullException("targetType", "Target type must be informed");
675 if (call == null) throw new ArgumentNullException("call", "Delegate must be passed");
677 EnsureInitialized(targetType);
679 ISession session = holder.CreateSession(targetType);
683 return call(session, instance);
685 catch (ValidationException)
687 holder.FailSession(session);
689 throw;
691 catch (Exception ex)
693 holder.FailSession(session);
695 throw new ActiveRecordException("Error performing Execute for " + targetType.Name, ex);
697 finally
699 holder.ReleaseSession(session);
703 #endregion
705 #region ExecuteQuery
707 /// <summary>
708 /// Enumerates the query
709 /// Note: only use if you expect most of the values to exist on the second level cache.
710 /// </summary>
711 /// <param name="query">The query.</param>
712 /// <returns>An <see cref="IEnumerable"/></returns>
713 protected internal static IEnumerable EnumerateQuery(IActiveRecordQuery query)
715 Type rootType = query.RootType;
717 EnsureInitialized(rootType);
719 ISession session = holder.CreateSession(rootType);
723 return query.Enumerate(session);
725 catch (Exception ex)
727 holder.FailSession(session);
729 throw new ActiveRecordException("Could not perform EnumerateQuery for " + rootType.Name, ex);
731 finally
733 holder.ReleaseSession(session);
737 #endregion
739 #region ExecuteQuery
741 /// <summary>
742 /// Executes the query.
743 /// </summary>
744 /// <param name="query">The query.</param>
745 /// <returns>The query result.</returns>
746 public static object ExecuteQuery(IActiveRecordQuery query)
748 Type rootType = query.RootType;
750 EnsureInitialized(rootType);
752 ISession session = holder.CreateSession(rootType);
756 return query.Execute(session);
758 catch (Exception ex)
760 holder.FailSession(session);
762 throw new ActiveRecordException("Could not perform ExecuteQuery for " + rootType.Name, ex);
764 finally
766 holder.ReleaseSession(session);
770 #endregion
772 #region Count
774 /// <summary>
775 /// Returns the number of records of the specified
776 /// type in the database
777 /// </summary>
778 /// <example>
779 /// <code>
780 /// [ActiveRecord]
781 /// public class User : ActiveRecordBase
782 /// {
783 /// ...
784 ///
785 /// public static int CountAllUsers()
786 /// {
787 /// return Count(typeof(User));
788 /// }
789 /// }
790 /// </code>
791 /// </example>
792 /// <param name="targetType">The target type.</param>
793 /// <returns>The count result</returns>
794 protected internal static int Count(Type targetType)
796 CountQuery query = new CountQuery(targetType);
798 return (int)ExecuteQuery(query);
801 /// <summary>
802 /// Returns the number of records of the specified
803 /// type in the database
804 /// </summary>
805 /// <example>
806 /// <code>
807 /// [ActiveRecord]
808 /// public class User : ActiveRecordBase
809 /// {
810 /// ...
811 ///
812 /// public static int CountAllUsersLocked()
813 /// {
814 /// return Count(typeof(User), "IsLocked = ?", true);
815 /// }
816 /// }
817 /// </code>
818 /// </example>
819 /// <param name="targetType">The target type.</param>
820 /// <param name="filter">A sql where string i.e. Person=? and DOB &gt; ?</param>
821 /// <param name="args">Positional parameters for the filter string</param>
822 /// <returns>The count result</returns>
823 protected internal static int Count(Type targetType, string filter, params object[] args)
825 CountQuery query = new CountQuery(targetType, filter, args);
827 return (int)ExecuteQuery(query);
830 /// <summary>
831 /// Returns the number of records of the specified
832 /// type in the database
833 /// </summary>
834 /// <param name="targetType">The target type.</param>
835 /// <param name="criteria">The criteria expression</param>
836 /// <returns>The count result</returns>
837 protected internal static int Count(Type targetType, ICriterion[] criteria)
839 CountQuery query = new CountQuery(targetType, criteria);
841 return (int) ExecuteQuery(query);
844 /// <summary>
845 /// Returns the number of records of the specified
846 /// type in the database
847 /// </summary>
848 /// <param name="targetType">The target type.</param>
849 /// <param name="detachedCriteria">The criteria expression</param>
850 /// <returns>The count result</returns>
851 protected internal static int Count(Type targetType, DetachedCriteria detachedCriteria)
853 CountQuery query = new CountQuery(targetType, detachedCriteria);
855 return (int)ExecuteQuery(query);
858 #endregion
860 #region Exists
862 /// <summary>
863 /// Check if there is any records in the db for the target type
864 /// </summary>
865 /// <param name="targetType">The target type.</param>
866 /// <returns><c>true</c> if there's at least one row</returns>
867 protected internal static bool Exists(Type targetType)
869 return Count(targetType) > 0;
872 /// <summary>
873 /// Check if there is any records in the db for the target type
874 /// </summary>
875 /// <param name="targetType">The target type.</param>
876 /// <param name="filter">A sql where string i.e. Person=? and DOB &gt; ?</param>
877 /// <param name="args">Positional parameters for the filter string</param>
878 /// <returns><c>true</c> if there's at least one row</returns>
879 protected internal static bool Exists(Type targetType, string filter, params object[] args)
881 return Count(targetType, filter, args) > 0;
884 /// <summary>
885 /// Check if the <paramref name="id"/> exists in the database.
886 /// </summary>
887 /// <param name="targetType">The target type.</param>
888 /// <param name="id">The id to check on</param>
889 /// <returns><c>true</c> if the ID exists; otherwise <c>false</c>.</returns>
890 protected internal static bool Exists(Type targetType, object id)
892 EnsureInitialized(targetType);
893 ISession session = holder.CreateSession(targetType);
897 return session.Get(targetType, id) != null;
899 catch(Exception ex)
901 throw new ActiveRecordException("Could not perform Exists for " + targetType.Name + ". Id: " + id, ex);
903 finally
905 holder.ReleaseSession(session);
909 /// <summary>
910 /// Check if any instance matching the criteria exists in the database.
911 /// </summary>
912 /// <param name="targetType">The target type.</param>
913 /// <param name="criteria">The criteria expression</param>
914 /// <returns><c>true</c> if an instance is found; otherwise <c>false</c>.</returns>
915 protected internal static bool Exists(Type targetType, params ICriterion[] criteria)
917 return Count(targetType, criteria) > 0;
920 /// <summary>
921 /// Check if any instance matching the criteria exists in the database.
922 /// </summary>
923 /// <param name="targetType">The target type.</param>
924 /// <param name="detachedCriteria">The criteria expression</param>
925 /// <returns><c>true</c> if an instance is found; otherwise <c>false</c>.</returns>
926 protected internal static bool Exists(Type targetType, DetachedCriteria detachedCriteria)
928 return Count(targetType, detachedCriteria) > 0;
931 #endregion
933 #region FindAll
935 /// <summary>
936 /// Returns all instances found for the specified type according to the criteria
937 /// </summary>
938 /// <param name="targetType">The target type.</param>
939 /// <param name="detachedCriteria">The criteria.</param>
940 /// <param name="orders">An <see cref="Array"/> of <see cref="Order"/> objects.</param>
941 /// <returns>The <see cref="Array"/> of results.</returns>
942 protected internal static Array FindAll(Type targetType, DetachedCriteria detachedCriteria, params Order[] orders)
944 EnsureInitialized(targetType);
946 ISession session = holder.CreateSession(targetType);
950 ICriteria criteria = detachedCriteria.GetExecutableCriteria(session);
952 AddOrdersToCriteria(criteria, orders);
954 return SupportingUtils.BuildArray(targetType, criteria.List());
956 catch(ValidationException)
958 holder.FailSession(session);
960 throw;
962 catch(Exception ex)
964 holder.FailSession(session);
966 throw new ActiveRecordException("Could not perform FindAll for " + targetType.Name, ex);
968 finally
970 holder.ReleaseSession(session);
974 /// <summary>
975 /// Returns all instances found for the specified type.
976 /// </summary>
977 /// <param name="targetType">The target type.</param>
978 /// <returns>The <see cref="Array"/> of results</returns>
979 protected internal static Array FindAll(Type targetType)
981 return FindAll(targetType, (Order[])null);
984 /// <summary>
985 /// Returns all instances found for the specified type
986 /// using sort orders and criteria.
987 /// </summary>
988 /// <param name="targetType">The The target type.</param>
989 /// <param name="orders">An <see cref="Array"/> of <see cref="Order"/> objects.</param>
990 /// <param name="criteria">The criteria expression</param>
991 /// <returns>The <see cref="Array"/> of results.</returns>
992 protected internal static Array FindAll(Type targetType, Order[] orders, params ICriterion[] criteria)
994 EnsureInitialized(targetType);
996 ISession session = holder.CreateSession(targetType);
1000 ICriteria sessionCriteria = session.CreateCriteria(targetType);
1002 foreach(ICriterion cond in criteria)
1004 sessionCriteria.Add(cond);
1007 AddOrdersToCriteria(sessionCriteria, orders);
1009 return SupportingUtils.BuildArray(targetType, sessionCriteria.List());
1011 catch (ValidationException)
1013 holder.FailSession(session);
1015 throw;
1017 catch (Exception ex)
1019 holder.FailSession(session);
1021 throw new ActiveRecordException("Could not perform FindAll for " + targetType.Name, ex);
1023 finally
1025 holder.ReleaseSession(session);
1029 private static void AddOrdersToCriteria(ICriteria criteria, Order[] orders)
1031 if (orders != null)
1033 foreach (Order order in orders)
1035 criteria.AddOrder(order);
1040 /// <summary>
1041 /// Returns all instances found for the specified type
1042 /// using criteria.
1043 /// </summary>
1044 /// <param name="targetType">The target type.</param>
1045 /// <param name="criteria">The criteria expression</param>
1046 /// <returns>The <see cref="Array"/> of results.</returns>
1047 protected internal static Array FindAll(Type targetType, params ICriterion[] criteria)
1049 return FindAll(targetType, null, criteria);
1052 #endregion
1054 #region FindAllByProperty
1056 /// <summary>
1057 /// Finds records based on a property value - automatically converts null values to IS NULL style queries.
1058 /// </summary>
1059 /// <param name="targetType">The target type</param>
1060 /// <param name="property">A property name (not a column name)</param>
1061 /// <param name="value">The value to be equals to</param>
1062 /// <returns>The <see cref="Array"/> of results.</returns>
1063 protected internal static Array FindAllByProperty(Type targetType, String property, object value)
1065 ICriterion criteria = (value == null) ? Expression.IsNull(property) : Expression.Eq(property, value);
1066 return FindAll(targetType, criteria);
1069 /// <summary>
1070 /// Finds records based on a property value - automatically converts null values to IS NULL style queries.
1071 /// </summary>
1072 /// <param name="targetType">The target type</param>
1073 /// <param name="orderByColumn">The column name to be ordered ASC</param>
1074 /// <param name="property">A property name (not a column name)</param>
1075 /// <param name="value">The value to be equals to</param>
1076 /// <returns>The <see cref="Array"/> of results.</returns>
1077 protected internal static Array FindAllByProperty(Type targetType, String orderByColumn, String property, object value)
1079 ICriterion criteria = (value == null) ? Expression.IsNull(property) : Expression.Eq(property, value);
1080 return FindAll(targetType, new Order[] {Order.Asc(orderByColumn)}, criteria);
1083 #endregion
1085 #region FindByPrimaryKey
1087 /// <summary>
1088 /// Finds an object instance by an unique ID
1089 /// </summary>
1090 /// <param name="targetType">The AR subclass type</param>
1091 /// <param name="id">ID value</param>
1092 /// <returns>The object instance.</returns>
1093 protected internal static object FindByPrimaryKey(Type targetType, object id)
1095 return FindByPrimaryKey(targetType, id, true);
1098 /// <summary>
1099 /// Finds an object instance by an unique ID
1100 /// </summary>
1101 /// <param name="targetType">The AR subclass type</param>
1102 /// <param name="id">ID value</param>
1103 /// <param name="throwOnNotFound"><c>true</c> if you want to catch an exception
1104 /// if the object is not found</param>
1105 /// <returns>The object instance.</returns>
1106 /// <exception cref="ObjectNotFoundException">if <c>throwOnNotFound</c> is set to
1107 /// <c>true</c> and the row is not found</exception>
1108 protected internal static object FindByPrimaryKey(Type targetType, object id, bool throwOnNotFound)
1110 EnsureInitialized(targetType);
1111 bool hasScope = holder.ThreadScopeInfo.HasInitializedScope;
1112 ISession session = holder.CreateSession(targetType);
1116 object loaded;
1117 // Load() and Get() has different semantics with regard to the way they
1118 // handle null values, Get() _must_ check that the value exists, Load() is allowed
1119 // to return an uninitialized proxy that will throw when you access it later.
1120 // in order to play well with proxies, we need to use this approach.
1121 if (throwOnNotFound)
1123 loaded = session.Load(targetType, id);
1125 else
1127 loaded = session.Get(targetType, id);
1129 //If we are not in a scope, we want to initialize the entity eagerly, since other wise the
1130 //user will get an exception when it access the entity's property, and it will try to lazy load itself and find that
1131 //it has no session.
1132 //If we are in a scope, it is the user responsability to keep the scope alive if he wants to use
1133 if (!hasScope)
1135 NHibernateUtil.Initialize(loaded);
1137 return loaded;
1139 catch (ObjectNotFoundException ex)
1141 holder.FailSession(session);
1143 String message = String.Format("Could not find {0} with id {1}", targetType.Name, id);
1144 throw new NotFoundException(message, ex);
1146 catch (ValidationException)
1148 holder.FailSession(session);
1150 throw;
1152 catch (Exception ex)
1154 holder.FailSession(session);
1156 throw new ActiveRecordException("Could not perform FindByPrimaryKey for " + targetType.Name + ". Id: " + id, ex);
1158 finally
1160 holder.ReleaseSession(session);
1164 #endregion
1166 #region FindFirst
1168 /// <summary>
1169 /// Searches and returns the first row.
1170 /// </summary>
1171 /// <param name="targetType">The target type.</param>
1172 /// <param name="detachedCriteria">The criteria.</param>
1173 /// <param name="orders">The sort order - used to determine which record is the first one.</param>
1174 /// <returns>A <c>targetType</c> instance or <c>null.</c></returns>
1175 protected internal static object FindFirst(Type targetType, DetachedCriteria detachedCriteria, params Order[] orders)
1177 Array result = SlicedFindAll(targetType, 0, 1, orders, detachedCriteria);
1178 return (result != null && result.Length > 0 ? result.GetValue(0) : null);
1181 /// <summary>
1182 /// Searches and returns the first row.
1183 /// </summary>
1184 /// <param name="targetType">The target type</param>
1185 /// <param name="orders">The sort order - used to determine which record is the first one</param>
1186 /// <param name="criteria">The criteria expression</param>
1187 /// <returns>A <c>targetType</c> instance or <c>null</c></returns>
1188 protected internal static object FindFirst(Type targetType, Order[] orders, params ICriterion[] criteria)
1190 Array result = SlicedFindAll(targetType, 0, 1, orders, criteria);
1191 return (result != null && result.Length > 0 ? result.GetValue(0) : null);
1194 /// <summary>
1195 /// Searches and returns the first row.
1196 /// </summary>
1197 /// <param name="targetType">The target type</param>
1198 /// <param name="criteria">The criteria expression</param>
1199 /// <returns>A <c>targetType</c> instance or <c>null</c></returns>
1200 protected internal static object FindFirst(Type targetType, params ICriterion[] criteria)
1202 return FindFirst(targetType, null, criteria);
1205 #endregion
1207 #region FindOne
1209 /// <summary>
1210 /// Searches and returns a row. If more than one is found,
1211 /// throws <see cref="ActiveRecordException"/>
1212 /// </summary>
1213 /// <param name="targetType">The target type</param>
1214 /// <param name="criteria">The criteria expression</param>
1215 /// <returns>A <c>targetType</c> instance or <c>null</c></returns>
1216 protected internal static object FindOne(Type targetType, params ICriterion[] criteria)
1218 Array result = SlicedFindAll(targetType, 0, 2, criteria);
1220 if (result.Length > 1)
1222 throw new ActiveRecordException(targetType.Name + ".FindOne returned " + result.Length +
1223 " rows. Expecting one or none");
1226 return (result.Length == 0) ? null : result.GetValue(0);
1229 /// <summary>
1230 /// Searches and returns a row. If more than one is found,
1231 /// throws <see cref="ActiveRecordException"/>
1232 /// </summary>
1233 /// <param name="targetType">The target type</param>
1234 /// <param name="criteria">The criteria</param>
1235 /// <returns>A <c>targetType</c> instance or <c>null</c></returns>
1236 protected internal static object FindOne(Type targetType, DetachedCriteria criteria)
1238 Array result = SlicedFindAll(targetType, 0, 2, criteria);
1240 if (result.Length > 1)
1242 throw new ActiveRecordException(targetType.Name + ".FindOne returned " + result.Length +
1243 " rows. Expecting one or none");
1246 return (result.Length == 0) ? null : result.GetValue(0);
1249 #endregion
1251 #region SlicedFindAll
1253 /// <summary>
1254 /// Returns a portion of the query results (sliced)
1255 /// </summary>
1256 /// <param name="targetType">The target type.</param>
1257 /// <param name="firstResult">The number of the first row to retrieve.</param>
1258 /// <param name="maxResults">The maximum number of results retrieved.</param>
1259 /// <param name="orders">An <see cref="Array"/> of <see cref="Order"/> objects.</param>
1260 /// <param name="criteria">The criteria expression</param>
1261 /// <returns>The sliced query results.</returns>
1262 protected internal static Array SlicedFindAll(Type targetType, int firstResult, int maxResults,
1263 Order[] orders, params ICriterion[] criteria)
1265 EnsureInitialized(targetType);
1267 ISession session = holder.CreateSession(targetType);
1271 ICriteria sessionCriteria = session.CreateCriteria(targetType);
1273 foreach(ICriterion cond in criteria)
1275 sessionCriteria.Add(cond);
1278 if (orders != null)
1280 foreach (Order order in orders)
1282 sessionCriteria.AddOrder(order);
1286 sessionCriteria.SetFirstResult(firstResult);
1287 sessionCriteria.SetMaxResults(maxResults);
1289 return SupportingUtils.BuildArray(targetType, sessionCriteria.List());
1291 catch (ValidationException)
1293 holder.FailSession(session);
1295 throw;
1297 catch (Exception ex)
1299 holder.FailSession(session);
1301 throw new ActiveRecordException("Could not perform SlicedFindAll for " + targetType.Name, ex);
1303 finally
1305 holder.ReleaseSession(session);
1309 /// <summary>
1310 /// Returns a portion of the query results (sliced)
1311 /// </summary>
1312 /// <param name="targetType">The target type.</param>
1313 /// <param name="firstResult">The number of the first row to retrieve.</param>
1314 /// <param name="maxResults">The maximum number of results retrieved.</param>
1315 /// <param name="orders">An <see cref="Array"/> of <see cref="Order"/> objects.</param>
1316 /// <param name="criteria">The criteria expression</param>
1317 /// <returns>The sliced query results.</returns>
1318 protected internal static Array SlicedFindAll(Type targetType, int firstResult, int maxResults,
1319 Order[] orders, DetachedCriteria criteria)
1321 EnsureInitialized(targetType);
1323 ISession session = holder.CreateSession(targetType);
1327 ICriteria executableCriteria = criteria.GetExecutableCriteria(session);
1328 AddOrdersToCriteria(executableCriteria, orders);
1329 executableCriteria.SetFirstResult(firstResult);
1330 executableCriteria.SetMaxResults(maxResults);
1332 return SupportingUtils.BuildArray(targetType, executableCriteria.List());
1334 catch(ValidationException)
1336 holder.FailSession(session);
1338 throw;
1340 catch(Exception ex)
1342 holder.FailSession(session);
1344 throw new ActiveRecordException("Could not perform SlicedFindAll for " + targetType.Name, ex);
1346 finally
1348 holder.ReleaseSession(session);
1352 /// <summary>
1353 /// Returns a portion of the query results (sliced)
1354 /// </summary>
1355 /// <param name="targetType">The target type.</param>
1356 /// <param name="firstResult">The number of the first row to retrieve.</param>
1357 /// <param name="maxResults">The maximum number of results retrieved.</param>
1358 /// <param name="criteria">The criteria expression</param>
1359 /// <returns>The sliced query results.</returns>
1360 protected internal static Array SlicedFindAll(Type targetType, int firstResult, int maxResults,
1361 params ICriterion[] criteria)
1363 return SlicedFindAll(targetType, firstResult, maxResults, null, criteria);
1366 /// <summary>
1367 /// Returns a portion of the query results (sliced)
1368 /// </summary>
1369 /// <param name="targetType">The target type.</param>
1370 /// <param name="firstResult">The number of the first row to retrieve.</param>
1371 /// <param name="maxResults">The maximum number of results retrieved.</param>
1372 /// <param name="criteria">The criteria expression</param>
1373 /// <returns>The sliced query results.</returns>
1374 protected internal static Array SlicedFindAll(Type targetType, int firstResult, int maxResults,
1375 DetachedCriteria criteria)
1377 return SlicedFindAll(targetType, firstResult, maxResults, null, criteria);
1380 #endregion
1382 #endregion
1384 #region protected internal
1386 /// <summary>
1387 /// Invokes the specified delegate passing a valid
1388 /// NHibernate session. Used for custom NHibernate queries.
1389 /// </summary>
1390 /// <param name="call">The delegate instance</param>
1391 /// <returns>Whatever is returned by the delegate invocation</returns>
1392 protected virtual internal object Execute(NHibernateDelegate call)
1394 return Execute(GetType(), call, this);
1397 #endregion
1399 #region public virtual
1401 /// <summary>
1402 /// Saves the instance information to the database.
1403 /// May Create or Update the instance depending
1404 /// on whether it has a valid ID.
1405 /// </summary>
1406 /// <remarks>
1407 /// If within a <see cref="SessionScope"/> the operation
1408 /// is going to be on hold until NHibernate (or you) decides to flush
1409 /// the session.
1410 /// </remarks>
1411 public virtual void Save()
1413 Save(this);
1416 /// <summary>
1417 /// Saves the instance information to the database.
1418 /// May Create or Update the instance depending
1419 /// on whether it has a valid ID.
1420 /// </summary>
1421 /// <remarks>
1422 /// Even within a <see cref="SessionScope"/> the operation
1423 /// is going to be flushed immediately. This might have side effects such as
1424 /// flushing (persisting) others operations that were on hold.
1425 /// </remarks>
1426 public virtual void SaveAndFlush()
1428 SaveAndFlush(this);
1431 /// <summary>
1432 /// Saves a copy of the instance information to the database.
1433 /// May Create or Update the instance depending
1434 /// on whether it has a valid ID.
1435 /// </summary>
1436 /// <returns>An saved ActiveRecord instance</returns>
1437 /// <remarks>
1438 /// If within a <see cref="SessionScope"/> the operation
1439 /// is going to be on hold until NHibernate (or you) decides to flush
1440 /// the session.
1441 /// </remarks>
1442 public virtual object SaveCopy()
1444 return SaveCopy(this);
1447 /// <summary>
1448 /// Saves a copy of the instance information to the database.
1449 /// May Create or Update the instance depending
1450 /// on whether it has a valid ID.
1451 /// </summary>
1452 /// <returns>A saved ActiveRecord instance</returns>
1453 /// <remarks>
1454 /// Even within a <see cref="SessionScope"/> the operation
1455 /// is going to be flushed immediately. This might have side effects such as
1456 /// flushing (persisting) others operations that were on hold.
1457 /// </remarks>
1458 public virtual object SaveCopyAndFlush()
1460 return SaveCopyAndFlush(this);
1463 /// <summary>
1464 /// Creates (Saves) a new instance to the database.
1465 /// </summary>
1466 /// <remarks>
1467 /// If within a <see cref="SessionScope"/> the operation
1468 /// is going to be on hold until NHibernate (or you) decides to flush
1469 /// the session.
1470 /// </remarks>
1471 public virtual void Create()
1473 Create(this);
1476 /// <summary>
1477 /// Creates (Saves) a new instance to the database.
1478 /// </summary>
1479 /// <remarks>
1480 /// Even within a <see cref="SessionScope"/> the operation
1481 /// is going to be flushed immediately. This might have side effects such as
1482 /// flushing (persisting) others operations that were on hold.
1483 /// </remarks>
1484 public virtual void CreateAndFlush()
1486 CreateAndFlush(this);
1489 /// <summary>
1490 /// Persists the modification on the instance
1491 /// state to the database.
1492 /// </summary>
1493 /// <remarks>
1494 /// If within a <see cref="SessionScope"/> the operation
1495 /// is going to be on hold until NHibernate (or you) decides to flush
1496 /// the session.
1497 /// </remarks>
1498 public virtual void Update()
1500 Update(this);
1503 /// <summary>
1504 /// Persists the modification on the instance
1505 /// state to the database.
1506 /// </summary>
1507 /// <remarks>
1508 /// Even within a <see cref="SessionScope"/> the operation
1509 /// is going to be flushed immediately. This might have side effects such as
1510 /// flushing (persisting) others operations that were on hold.
1511 /// </remarks>
1512 public virtual void UpdateAndFlush()
1514 UpdateAndFlush(this);
1517 /// <summary>
1518 /// Deletes the instance from the database.
1519 /// </summary>
1520 /// <remarks>
1521 /// If within a <see cref="SessionScope"/> the operation
1522 /// is going to be on hold until NHibernate (or you) decides to flush
1523 /// the session.
1524 /// </remarks>
1525 public virtual void Delete()
1527 Delete(this);
1530 /// <summary>
1531 /// Deletes the instance from the database.
1532 /// </summary>
1533 /// <remarks>
1534 /// Even within a <see cref="SessionScope"/> the operation
1535 /// is going to be flushed immediately. This might have side effects such as
1536 /// flushing (persisting) others operations that were on hold.
1537 /// </remarks>
1538 public virtual void DeleteAndFlush()
1540 DeleteAndFlush(this);
1543 /// <summary>
1544 /// Refresh the instance from the database.
1545 /// </summary>
1546 public virtual void Refresh()
1548 Refresh(this);
1551 #endregion
1553 #region public override
1555 /// <summary>
1556 /// Return the type of the object with its PK value.
1557 /// Useful for logging/debugging
1558 /// </summary>
1559 /// <returns>A string representation of this object.</returns>
1560 public override String ToString()
1562 ActiveRecordModel model = GetModel(GetType());
1564 if (model == null || model.PrimaryKey == null)
1566 return base.ToString();
1569 PrimaryKeyModel pkModel = model.PrimaryKey;
1571 object pkVal = pkModel.Property.GetValue(this, null);
1573 return base.ToString() + "#" + pkVal;
1576 #endregion