1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using Castle
.ActiveRecord
.Framework
;
22 using NHibernate
.Expressions
;
23 using NHibernate
.Transform
;
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.
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>
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)
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
;
52 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
53 /// At least one projections must be given
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
);
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
68 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
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
;
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
85 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
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 }
;
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
102 public ProjectionQuery(Order order
, ProjectionList projections
)
104 this.projections
= projections
;
105 detachedCriteria
= DetachedCriteria
.For(RootType
);
106 orders
= new Order
[] { order }
;
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
114 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
116 public ProjectionQuery(DetachedCriteria detachedCriteria
, ProjectionList projections
)
118 this.projections
= projections
;
119 this.detachedCriteria
= detachedCriteria
;
120 orders
= new Order
[0];
124 /// Gets the target type of this query
128 get { return typeof(ARType); }
132 /// Sets the query range.
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
)
146 /// Executes the specified query and return the results
148 /// <param name="session">The session to execute the query in.</param>
149 /// <returns>IList<TResultItem> cast to object because of interface</returns>
150 object IActiveRecordQuery
.Execute(ISession session
)
152 ICriteria criteria
= CreateCriteria(session
);
156 criteria
.SetFirstResult(first
.Value
);
157 criteria
.SetMaxResults(max
.Value
);
160 return criteria
.List
<TResultItem
>();
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!
167 public IEnumerable
Enumerate(ISession session
)
173 /// Executes the specified query and return the results
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
);
198 private void AssertAllArgumentsValid()
200 if (projections
== null)
202 throw new ArgumentNullException("projections");
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!");
219 /// This is used to convert the resulting tuples into strongly typed objects.
221 /// <typeparam name="T"></typeparam>
222 private class TypedResultTransformer
<T
> : IResultTransformer
225 ///Convert the tuples into a strongly typed object
227 public object TransformTuple(object[] tuple
, string[] aliases
)
229 return Activator
.CreateInstance(typeof(T
), tuple
);
232 public IList
TransformList(IList collection
)
240 /// Default implemenation of ProjectionQuery that returns an Untyped object array tuples
242 public class ProjectionQuery
<ARType
> : ProjectionQuery
<ARType
, object[]>
245 /// Create a new <see cref="ProjectionQuery{ARType,TResultItem}"/> with the given projections.
246 /// At least one projections must be given
248 /// <param name="projections">The projections to use in the query</param>
249 public ProjectionQuery(ProjectionList projections
) : base(projections
)
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
258 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
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
)
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
273 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
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
)
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
288 public ProjectionQuery(Order order
, ProjectionList projections
) : base(order
, projections
)
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
297 /// Note: Do not call SetProjection() on the detached criteria, since that is overwritten.
299 public ProjectionQuery(DetachedCriteria detachedCriteria
, ProjectionList projections
)
300 : base(detachedCriteria
, projections
)