1+ <chapter id =" querylinq" >
2+ <title >Linq Queries</title >
3+
4+ <para >
5+ NHibernate 3.0 introduces the Linq to NHibernate provider, which allows the use of the Linq API
6+ for querying with NHibernate.
7+ </para >
8+ <para >
9+ The Linq provider works as an extension of the <literal >ISession</literal >. It is defined in the
10+ <literal >NHibernate.Linq</literal > namespace, so this namespace has to be imported for using the
11+ Linq provider. Of course, the LINQ namespace is still needed too.
12+ </para >
13+ <programlisting ><![CDATA[ using System.Linq;
14+ using NHibernate.Linq;]]> </programlisting >
15+ <para >
16+ Note: NHibernate has another querying API which uses lambda, <link linkend =" queryqueryover" >QueryOver</link >.
17+ It should not be confused with a LINQ provider.
18+ </para >
19+
20+ <sect1 id =" querylinq-querystructure" >
21+ <title >Structure of a Query</title >
22+
23+ <para >
24+ Queries are created from an ISession using the syntax:
25+ </para >
26+ <programlisting ><![CDATA[ IList<Cat> cats =
27+ session.Query<Cat>()
28+ .Where(c => c.Color == "white")
29+ .ToList();]]> </programlisting >
30+ <para >
31+ The <literal >Query< TEntity> </literal > function yields an <literal >IQueryable< TEntity> </literal >,
32+ with which Linq extension methods or Linq syntax can be used. When executed, the <literal >IQueryable< TEntity> </literal >
33+ will be translated to a SQL query on the database.
34+ </para >
35+ <para > </para >
36+
37+ <para >
38+ It is possible to query a specific sub-class while still using a queryable of the base class.
39+ </para >
40+ <programlisting ><![CDATA[ IList<Cat> cats =
41+ session.Query<Cat>("Eg.DomesticCat, Eg")
42+ .Where(c => c.Name == "Max")
43+ .ToList();]]> </programlisting >
44+ <para > </para >
45+ </sect1 >
46+
47+ <sect1 id =" querylinq-parametertypes" >
48+ <title >Parameter types</title >
49+
50+ <para >
51+ Query parameters get extracted from the Linq expression. Their types are selected according to
52+ <link linkend =" mapping-types" >NHibernate types</link > default for .Net types.
53+ </para >
54+ <para >
55+ The <literal >MappedAs</literal > extension method allows to override the default type.
56+ </para >
57+ <programlisting ><![CDATA[ IList<Cat> cats =
58+ session.Query<Cat>()
59+ .Where(c => c.BirthDate == DateTime.Today.MappedAs(NHibernateUtil.Date))
60+ .ToList();]]> </programlisting >
61+ </sect1 >
62+
63+ <sect1 id =" querylinq-futureresults" >
64+ <title >Future results</title >
65+
66+ <para >
67+ Future results are supported by the Linq provider. They are not evaluated till one gets executed.
68+ At that point, all defined future results are evaluated in one single round-trip to database.
69+ </para >
70+ <programlisting ><![CDATA[ // Define queries
71+ IEnumerable<Cat> cats =
72+ session.Query<Cat>()
73+ .Where(c => c.Color == "black")
74+ .ToFuture();
75+ IFutureValue<int> catCount =
76+ session.Query<Cat>()
77+ .ToFutureValue(q => q.Count());
78+ // Execute them
79+ foreach(Cat cat in cats)
80+ {
81+ // Do something
82+ }
83+ if (catCount.Value > 10)
84+ {
85+ // Do something
86+ }
87+ ]]> </programlisting >
88+ <para >
89+ In above example, accessing <literal >catCount.Value</literal > does not trigger a round-trip to database: it has been
90+ evaluated with <literal >cats</literal > enumeration.
91+ </para >
92+ </sect1 >
93+
94+ <sect1 id =" querylinq-fetching" >
95+ <title >Fetching associations</title >
96+
97+ <para >
98+ A Linq query may load associated entities or collection of entities. Once the query is defined, using
99+ <literal >Fetch</literal > allows fetching a related entity, and <literal >FetchMany</literal > allows fetching a
100+ collection.
101+ </para >
102+ <programlisting ><![CDATA[ IList<Cat> oldCats =
103+ session.Query<Cat>()
104+ .Where(c => c.BirthDate.Year < 2010)
105+ .Fetch(c => c.Mate)
106+ .FetchMany(c => c.Kittens)
107+ .ToList();]]> </programlisting >
108+ <para >
109+ Issuing many <literal >FetchMany</literal > on the same query may cause a cartesian product over
110+ the fetched collections. This can be avoided by splitting the fetches among
111+ <link linkend =" querylinq-futureresults" >future queries</link >.
112+ </para >
113+ <programlisting ><![CDATA[ IQueryable<Cat> oldCatsQuery =
114+ session.Query<Cat>()
115+ .Where(c => c.BirthDate.Year < 2010);
116+ oldCatsQuery
117+ .Fetch(c => c.Mate)
118+ .FetchMany(c => c.Kittens)
119+ .ToFuture();
120+ IList<Cat> oldCats =
121+ oldCatsQuery
122+ .FetchMany(c => c.AnotherCollection)
123+ .ToFuture()
124+ .ToList();]]> </programlisting >
125+ <para > </para >
126+
127+ <para >
128+ Use <literal >ThenFetch</literal > and <literal >ThenFetchMany</literal > for fetching associations
129+ of the previously fetched association.
130+ </para >
131+ <programlisting ><![CDATA[ IList<Cat> oldCats =
132+ session.Query<Cat>()
133+ .Where(c => c.BirthDate.Year < 2010)
134+ .Fetch(c => c.Mate)
135+ .FetchMany(c => c.Kittens)
136+ .ThenFetch(k => k.Mate)
137+ .ToList();]]> </programlisting >
138+ <para > </para >
139+ </sect1 >
140+
141+ <sect1 id =" querylinq-querycache" >
142+ <title >Query cache</title >
143+
144+ <para >
145+ The Linq provider can use the query cache if it is setup. Refer to <xref linkend =" performance-querycache" /> for more
146+ details on how to set it up.
147+ </para >
148+ <para > </para >
149+
150+ <para >
151+ <literal >Cacheable</literal > extension method enables the cache for the query.
152+ </para >
153+ <programlisting ><![CDATA[ IList<Cat> oldCats =
154+ session.Query<Cat>()
155+ .Where(c => c.BirthDate.Year < 2010)
156+ .Cacheable()
157+ .ToList();]]> </programlisting >
158+ <para > </para >
159+
160+ <para >
161+ <literal >CacheMode</literal > and <literal >CacheRegion</literal > extension methods set
162+ the cache mode and the cache region respectively.
163+ </para >
164+ <programlisting ><![CDATA[ IList<Cat> cats =
165+ session.Query<Cat>()
166+ .Where(c => c.Name == "Max")
167+ .Cacheable()
168+ .CacheRegion("catNames")
169+ .ToList();]]> </programlisting >
170+ </sect1 >
171+
172+ <sect1 id =" querylinq-miscellaneous" >
173+ <title >Miscellaneous features</title >
174+
175+ <para >
176+ A client timeout for the query can be defined.
177+ </para >
178+ <programlisting ><![CDATA[ IList<Cat> cats =
179+ session.Query<Cat>()
180+ .Where(c => c.Color == "black")
181+ // Allows 10 seconds only.
182+ .TimeOut(10)
183+ .ToList();]]> </programlisting >
184+
185+ <para >
186+ A string <literal >Like</literal > extension methods allows expressing SQL <literal >like</literal > conditions.
187+ </para >
188+ <programlisting ><![CDATA[ IList<DomesticCat> cats =
189+ session.Query<DomesticCat>()
190+ .Where(c => c.Name.Like("Lou%"))
191+ .ToList();]]> </programlisting >
192+ <para >
193+ This <literal >Like</literal > extension method is a Linq to NHibernate method only. Trying to call it in another
194+ context is not supported.
195+ </para >
196+
197+ <para >
198+ NHibernate Linq provider feature a <literal >LinqExtensionMethod</literal > attribute. It allows using an
199+ arbitrary built-in or user defined SQL functions. It should be applied on an method having the same
200+ arguments than the SQL function.
201+ </para >
202+ <programlisting ><![CDATA[ public static class CustomLinqExtensions
203+ {
204+ [LinqExtensionMethod()]
205+ public static string SubStr(this string input, int start, int length)
206+ {
207+ // No need to implement it in .Net, unless you wish to call it
208+ // outside IQueryable context too.
209+ throw new NotImplementedException("This call should be translated " +
210+ "to SQL and run db side, but it has been run with .Net runtime");
211+ }
212+ }]]> </programlisting >
213+ <para >
214+ Then it can be used in a Linq to NHibernate query.
215+ </para >
216+ <programlisting ><![CDATA[ IList<DomesticCat> cats =
217+ session.Query<DomesticCat>()
218+ .Where(c => c.Name.SubStr(2, 2) == "li")
219+ .ToList();]]> </programlisting >
220+ <para >
221+ The function name is inferred from the method name. If needed, another name can be provided.
222+ </para >
223+ <programlisting ><![CDATA[ public static class CustomLinqExtensions
224+ {
225+ [LinqExtensionMethod("dbo.aCustomFunction")]
226+ public static string ACustomFunction(this string input, string otherInput)
227+ {
228+ throw new NotImplementedException();
229+ }
230+ }]]> </programlisting >
231+ <para >
232+ It is required that at least one of the parameters of the method call has its value originating
233+ from an entity. Otherwise, the Linq provider will try to evaluate the method call with .Net
234+ runtime.
235+ </para >
236+ </sect1 >
237+ </chapter >
0 commit comments