Skip to content

Commit 72c2db6

Browse files
NH-2444: Adding some documentation for Linq querying API.
1 parent f648b00 commit 72c2db6

File tree

3 files changed

+240
-1
lines changed

3 files changed

+240
-1
lines changed

doc/reference/master.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<!ENTITY query-hql SYSTEM "modules/query_hql.xml">
1818
<!ENTITY query-criteria SYSTEM "modules/query_criteria.xml">
1919
<!ENTITY query-queryover SYSTEM "modules/query_queryover.xml">
20+
<!ENTITY query-linq SYSTEM "modules/query_linq.xml">
2021
<!ENTITY query-sql SYSTEM "modules/query_sql.xml">
2122
<!ENTITY filters SYSTEM "modules/filters.xml">
2223
<!ENTITY performance SYSTEM "modules/performance.xml">
@@ -67,6 +68,7 @@
6768
&query-hql;
6869
&query-criteria;
6970
&query-queryover;
71+
&query-linq;
7072
&query-sql;
7173

7274
&filters;

doc/reference/modules/query_linq.xml

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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&lt;TEntity&gt;</literal> function yields an <literal>IQueryable&lt;TEntity&gt;</literal>,
32+
with which Linq extension methods or Linq syntax can be used. When executed, the <literal>IQueryable&lt;TEntity&gt;</literal>
33+
will be translated to a SQL query on the database.
34+
</para>
35+
<para>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>&nbsp;</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>

doc/reference/modules/query_queryover.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<para>
2828
Note: QueryOver is intended to remove the references to 'magic strings'
2929
from the ICriteria API while maintaining it's opaqueness. It is <emphasis role="underline">not</emphasis> a LINQ provider;
30-
NHibernate has a built-in Linq provider for this.
30+
NHibernate has a built-in <link linkend="querylinq">Linq provider</link> for this.
3131
</para>
3232

3333
<sect1 id="queryqueryover-querystructure">

0 commit comments

Comments
 (0)