2009-07-11 Michael Barker <mike@middlesoft.co.uk>
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Implementation / QueryProvider.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Collections;\r
29 using System.Collections.Generic;\r
30 using System.Linq;\r
31 using System.Linq.Expressions;\r
32 using DbLinq.Data.Linq.Sugar;\r
33 using DbLinq.Util;\r
34 \r
35 #if MONO_STRICT\r
36 using System.Data.Linq;\r
37 #else\r
38 using DbLinq.Data.Linq;\r
39 #endif\r
40 \r
41 namespace DbLinq.Data.Linq.Implementation\r
42 {\r
43     /// <summary>\r
44     /// QueryProvider is used by both DataContext and Table\r
45     /// to build queries\r
46     /// It is split is two parts (non-generic and generic) for copy reasons\r
47     /// </summary>\r
48     internal abstract class QueryProvider\r
49     {\r
50         /// <summary>\r
51         /// Gets or sets the expression chain.\r
52         /// </summary>\r
53         /// <value>The expression chain.</value>\r
54         public ExpressionChain ExpressionChain { get; set; }\r
55         /// <summary>\r
56         /// Gets or sets the type of the table.\r
57         /// </summary>\r
58         /// <value>The type of the table.</value>\r
59         public Type TableType { get; set; }\r
60         /// <summary>\r
61         /// Gets the query.\r
62         /// </summary>\r
63         /// <param name="expression">The expression.</param>\r
64         /// <returns></returns>\r
65         public abstract SelectQuery GetQuery(Expression expression);\r
66     }\r
67 \r
68     /// <summary>\r
69     /// QueryProvider, generic version\r
70     /// </summary>\r
71     /// <typeparam name="T"></typeparam>\r
72     internal class QueryProvider<T> : QueryProvider, IQueryProvider, IQueryable<T>, IOrderedQueryable<T>\r
73     {\r
74         /// <summary>\r
75         /// Holder current datancontext\r
76         /// </summary>\r
77         protected readonly DataContext _dataContext;\r
78 \r
79         /// <summary>\r
80         /// Initializes a new instance of the <see cref="QueryProvider&lt;T&gt;"/> class.\r
81         /// </summary>\r
82         /// <param name="dataContext">The data context.</param>\r
83         public QueryProvider(DataContext dataContext)\r
84         {\r
85             _dataContext = dataContext;\r
86             TableType = typeof(T);\r
87             ExpressionChain = new ExpressionChain();\r
88         }\r
89 \r
90         /// <summary>\r
91         /// Initializes a new instance of the <see cref="QueryProvider&lt;T&gt;"/> class.\r
92         /// </summary>\r
93         /// <param name="tableType">Type of the table.</param>\r
94         /// <param name="dataContext">The data context.</param>\r
95         /// <param name="expressionChain">The expression chain.</param>\r
96         /// <param name="expression">The expression.</param>\r
97         public QueryProvider(Type tableType, DataContext dataContext, ExpressionChain expressionChain, Expression expression)\r
98         {\r
99             _dataContext = dataContext;\r
100             TableType = tableType;\r
101             ExpressionChain = new ExpressionChain(expressionChain, expression);\r
102         }\r
103 \r
104         /// <summary>\r
105         /// Creates the query.\r
106         /// </summary>\r
107         /// <typeparam name="S"></typeparam>\r
108         /// <param name="t">The t.</param>\r
109         /// <param name="tableType">Type of the table.</param>\r
110         /// <param name="dataContext">The data context.</param>\r
111         /// <param name="expressionChain">The expression chain.</param>\r
112         /// <param name="expression">The expression.</param>\r
113         /// <returns></returns>\r
114         protected S CreateQuery<S>(Type t, Type tableType, DataContext dataContext, ExpressionChain expressionChain, Expression expression)\r
115         {\r
116             // no way to work differently\r
117             var typedQueryProviderType = typeof(QueryProvider<>).MakeGenericType(t);\r
118             var queryProvider = (S)Activator.CreateInstance(typedQueryProviderType, tableType, dataContext,\r
119                                                              expressionChain, expression);\r
120             return queryProvider;\r
121         }\r
122 \r
123         /// <summary>\r
124         /// Builds the query, given a LINQ expression\r
125         /// </summary>\r
126         /// <param name="expression"></param>\r
127         /// <returns></returns>\r
128         public IQueryable CreateQuery(Expression expression)\r
129         {\r
130             var type = expression.Type;\r
131             if (!type.IsGenericType)\r
132                 throw Error.BadArgument("S0066: Don't know how to handle non-generic type '{0}'", type);\r
133             var genericType = type.GetGenericTypeDefinition();\r
134             if (genericType == typeof(IQueryable<>) || genericType == typeof(IOrderedQueryable<>))\r
135                 type = type.GetGenericArguments()[0];\r
136             else\r
137                 Error.BadArgument("S0068: Don't know how to handle type '{0}'", type);\r
138             return CreateQuery<IQueryable>(type, TableType, _dataContext, ExpressionChain, expression);\r
139         }\r
140 \r
141         /// <summary>\r
142         /// Creates the query.\r
143         /// </summary>\r
144         /// <typeparam name="TElement">The type of the element.</typeparam>\r
145         /// <param name="expression">The expression.</param>\r
146         /// <returns></returns>\r
147         public IQueryable<TElement> CreateQuery<TElement>(Expression expression)\r
148         {\r
149             Profiler.At("START QueryProvider.CreateQuery<{0}>({1})", typeof(TElement).Name, expression.ToString());\r
150             var query = new QueryProvider<TElement>(TableType, _dataContext, ExpressionChain, expression);\r
151             Profiler.At("END QueryProvider.CreateQuery<{0}>(...)", typeof(TElement).Name);\r
152             return query;\r
153         }\r
154 \r
155         /// <summary>\r
156         /// Gets the query.\r
157         /// </summary>\r
158         /// <param name="expression">The expression.</param>\r
159         /// <returns></returns>\r
160         public override SelectQuery GetQuery(Expression expression)\r
161         {\r
162             var expressionChain = ExpressionChain;\r
163             if (expression != null)\r
164                 expressionChain = new ExpressionChain(expressionChain, expression);\r
165             return _dataContext.QueryBuilder.GetSelectQuery(expressionChain, new QueryContext(_dataContext));\r
166         }\r
167 \r
168         /// <summary>\r
169         /// Runs query\r
170         /// </summary>\r
171         /// <param name="expression"></param>\r
172         /// <returns></returns>\r
173         public object Execute(Expression expression)\r
174         {\r
175             return Execute<object>(expression);\r
176         }\r
177 \r
178         /// <summary>\r
179         /// Runs query\r
180         /// </summary>\r
181         /// <typeparam name="TResult"></typeparam>\r
182         /// <param name="expression"></param>\r
183         /// <returns></returns>\r
184         public TResult Execute<TResult>(Expression expression)\r
185         {\r
186             Profiler.At("START QueryProvider.Execute<{0}>(): Executing expression...", typeof(TResult).Name);\r
187             var query = GetQuery(expression);\r
188             var result = _dataContext.QueryRunner.SelectScalar<TResult>(query);\r
189             Profiler.At("END QueryProvider.Execute<{0}>(): Executing expression...", typeof(TResult).Name);\r
190             return result;\r
191         }\r
192 \r
193         /// <summary>\r
194         /// Enumerates all query items\r
195         /// </summary>\r
196         /// <returns></returns>\r
197         IEnumerator IEnumerable.GetEnumerator()\r
198         {\r
199             var enumerator = GetEnumerator();\r
200             return enumerator;\r
201         }\r
202 \r
203         /// <summary>\r
204         /// Enumerates all query items\r
205         /// </summary>\r
206         /// <returns></returns>\r
207         public IEnumerator<T> GetEnumerator()\r
208         {\r
209             Profiler.At("START QueryProvider.GetEnumerator<{0}>(): Executing expression...", typeof(T).Name);\r
210             var query = GetQuery(null);\r
211             var enumerator = _dataContext.QueryRunner.Select<T>(query).GetEnumerator();\r
212             Profiler.At("END QueryProvider.GetEnumerator<{0}>(): Executing expression...", typeof(T).Name);\r
213             return enumerator;\r
214         }\r
215 \r
216         /// <summary>\r
217         /// Returns this QueryProvider as an exception\r
218         /// </summary>\r
219         public Expression Expression\r
220         {\r
221             get { return Expression.Constant(this); }\r
222         }\r
223 \r
224         public Type ElementType\r
225         {\r
226             get { return (typeof(T)); }\r
227         }\r
228 \r
229         public IQueryProvider Provider\r
230         {\r
231             get { return this; }\r
232         }\r
233     }\r
234 }\r