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