Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / ActiveRecord / Castle.ActiveRecord / Framework / Queries / ProjectionQuery.cs
blob0dbab51dd06762713f47b713d48f59ddf8c53903
1 // Copyright 2004-2008 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.Queries
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using Castle.ActiveRecord.Framework;
21 using NHibernate;
22 using NHibernate.Expressions;
23 using NHibernate.Transform;
25 /// <summary>
26 /// Performs a projected selection from an entity, lifting only the required fields.
27 /// Similar to SELECT Id,Name FROM MyTable instead of selecting everything.
28 /// It is possible to combine this with grouping.
29 /// </summary>
30 /// <typeparam name="ARType">The active record entity type</typeparam>
31 /// <typeparam name="TResultItem">The result value to use: object[] means returning as is</typeparam>
32 /// /// <example>
33 /// <code>
34 /// <![CDATA[
35 /// ProjectionQuery<Post, PostTitleAndId> proj = new ProjectionQuery<Post, PostTitleAndId>(Projections.Property("Title"), Projections.Property("Id"));
36 /// ICollection<PostTitleAndId> posts = proj.Execute();
37 /// foreach(PostTitleAndId titleAndId in posts)
38 /// {
39 /// //push to site...
40 /// }
41 /// ]]>
42 /// </code>
43 /// </example>
44 public class ProjectionQuery<ARType, TResultItem> : IActiveRecordQuery
46 private readonly ProjectionList projections;
47 private readonly DetachedCriteria detachedCriteria;
48 private readonly Order[] orders;
49 private int? first, max;
51 /// <summary>
52 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
53 /// At least one projections must be given
54 /// </summary>
55 /// <param name="projections">The projections to use in the query</param>
56 public ProjectionQuery(ProjectionList projections)
58 this.projections = projections;
59 orders = new Order[0];
60 detachedCriteria = DetachedCriteria.For(RootType);
63 /// <summary>
64 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
65 /// At least one projections must be given.
66 /// The DetachedCriteria is mostly used for filtering, although it is possible to use it for ordering, limiting the
67 /// result set, etc.
68 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
69 /// </summary>
70 /// <param name="detachedCriteria">Criteria to select by</param>
71 /// <param name="orders">The order by which to get the result</param>
72 /// <param name="projections">The projections</param>
73 public ProjectionQuery(DetachedCriteria detachedCriteria, Order[] orders, ProjectionList projections)
75 this.projections = projections;
76 this.detachedCriteria = detachedCriteria;
77 this.orders = orders;
80 /// <summary>
81 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
82 /// At least one projections must be given.
83 /// The DetachedCriteria is mostly used for filtering, although it is possible to use it for ordering, limiting the
84 /// result set, etc.
85 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
86 /// </summary>
87 /// <param name="detachedCriteria">Criteria to select by</param>
88 /// <param name="order">The order by which to get the result</param>
89 /// <param name="projections">The projections</param>
90 public ProjectionQuery(DetachedCriteria detachedCriteria, Order order, ProjectionList projections)
92 this.projections = projections;
93 this.detachedCriteria = detachedCriteria;
94 orders = new Order[] { order };
97 /// <summary>
98 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
99 /// At least one projections must be given.
100 /// The results will be loaded according to the order specified
101 /// </summary>
102 public ProjectionQuery(Order order, ProjectionList projections)
104 this.projections = projections;
105 detachedCriteria = DetachedCriteria.For(RootType);
106 orders = new Order[] { order };
109 /// <summary>
110 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
111 /// At least one projections must be given.
112 /// The DetachedCriteria is mostly used for filtering, although it is possible to use it for ordering, limiting the
113 /// result set, etc.
114 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
115 /// </summary>
116 public ProjectionQuery(DetachedCriteria detachedCriteria, ProjectionList projections)
118 this.projections = projections;
119 this.detachedCriteria = detachedCriteria;
120 orders = new Order[0];
123 /// <summary>
124 /// Gets the target type of this query
125 /// </summary>
126 public Type RootType
128 get { return typeof(ARType); }
131 /// <summary>
132 /// Sets the query range.
133 /// </summary>
134 /// <param name="first">The first row to return.</param>
135 /// <param name="max">The max number of rows to return.</param>
136 /// <returns>The instance</returns>
137 public ProjectionQuery<ARType, TResultItem> SetRange(int first, int max)
139 this.first = first;
140 this.max = max;
142 return this;
145 /// <summary>
146 /// Executes the specified query and return the results
147 /// </summary>
148 /// <param name="session">The session to execute the query in.</param>
149 /// <returns>IList&lt;TResultItem&gt; cast to object because of interface</returns>
150 object IActiveRecordQuery.Execute(ISession session)
152 ICriteria criteria = CreateCriteria(session);
154 if (first.HasValue)
156 criteria.SetFirstResult(first.Value);
157 criteria.SetMaxResults(max.Value);
160 return criteria.List<TResultItem>();
163 /// <summary>
164 /// Enumerates over the result of the query.
165 /// Note: Only use if you expect most of your values to already exist in the second level cache!
166 /// </summary>
167 public IEnumerable Enumerate(ISession session)
169 return Execute();
172 /// <summary>
173 /// Executes the specified query and return the results
174 /// </summary>
175 /// <returns>the result of the query</returns>
176 public IList<TResultItem> Execute()
178 return (IList<TResultItem>) ActiveRecordMediator.ExecuteQuery(this);
181 private ICriteria CreateCriteria(ISession session)
183 AssertAllArgumentsValid();
184 ICriteria criteria = detachedCriteria.GetExecutableCriteria(session);
185 criteria.SetProjection(projections);
187 // we are not returning a tuple, so we need the result transformer
188 if (!typeof(TResultItem).IsPrimitive && typeof(TResultItem) != typeof(object[]))
190 criteria.SetResultTransformer(new TypedResultTransformer<TResultItem>());
193 CriteriaHelper.AddOrdersToCriteria(criteria, orders);
195 return criteria;
198 private void AssertAllArgumentsValid()
200 if (projections == null)
202 throw new ArgumentNullException("projections");
204 if (orders == null)
206 throw new ArgumentNullException("orders");
208 if (detachedCriteria == null)
210 throw new ArgumentNullException("detachedCriteria");
212 if (projections.Length == 0)
214 throw new ActiveRecordException("Can't use projection query with zero projections!");
218 /// <summary>
219 /// This is used to convert the resulting tuples into strongly typed objects.
220 /// </summary>
221 /// <typeparam name="T"></typeparam>
222 private class TypedResultTransformer<T> : IResultTransformer
224 ///<summary>
225 ///Convert the tuples into a strongly typed object
226 ///</summary>
227 public object TransformTuple(object[] tuple, string[] aliases)
229 return Activator.CreateInstance(typeof(T), tuple);
232 public IList TransformList(IList collection)
234 return collection;
239 /// <summary>
240 /// Default implemenation of ProjectionQuery that returns an Untyped object array tuples
241 /// </summary>
242 public class ProjectionQuery<ARType> : ProjectionQuery<ARType, object[]>
244 /// <summary>
245 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
246 /// At least one projections must be given
247 /// </summary>
248 /// <param name="projections">The projections to use in the query</param>
249 public ProjectionQuery(ProjectionList projections) : base(projections)
253 /// <summary>
254 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
255 /// At least one projections must be given.
256 /// The DetachedCriteria is mostly used for filtering, although it is possible to use it for ordering, limiting the
257 /// result set, etc.
258 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
259 /// </summary>
260 /// <param name="detachedCriteria">Criteria to select by</param>
261 /// <param name="orders">The order by which to get the result</param>
262 /// <param name="projections">The projections</param>
263 public ProjectionQuery(DetachedCriteria detachedCriteria, Order[] orders, ProjectionList projections)
264 : base(detachedCriteria, orders,projections)
268 /// <summary>
269 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
270 /// At least one projections must be given.
271 /// The DetachedCriteria is mostly used for filtering, although it is possible to use it for ordering, limiting the
272 /// result set, etc.
273 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
274 /// </summary>
275 /// <param name="detachedCriteria">Criteria to select by</param>
276 /// <param name="order">The order by which to get the result</param>
277 /// <param name="projections">The projections</param>
278 public ProjectionQuery(DetachedCriteria detachedCriteria, Order order, ProjectionList projections)
279 : base(detachedCriteria,order, projections)
283 /// <summary>
284 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
285 /// At least one projections must be given.
286 /// The results will be loaded according to the order specified
287 /// </summary>
288 public ProjectionQuery(Order order, ProjectionList projections) : base(order, projections)
292 /// <summary>
293 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
294 /// At least one projections must be given.
295 /// The DetachedCriteria is mostly used for filtering, although it is possible to use it for ordering, limiting the
296 /// result set, etc.
297 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
298 /// </summary>
299 public ProjectionQuery(DetachedCriteria detachedCriteria, ProjectionList projections)
300 : base(detachedCriteria, projections)