Merge pull request #1156 from felfert/master
[mono.git] / mcs / class / System.Web.Extensions / System.Web.UI.WebControls / LinqDataSourceView.cs
1 //
2 // LinqDataSourceView.cs
3 //
4 // Author:
5 //   Atsushi Enomoto  <atsushi@ximian.com>
6 //
7 // Copyright (C) 2008 Novell, Inc  http://novell.com
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 // FIXME: in general we should create something like
32 // System.Web.Query,Dynamic.DynamicClass to execute DataContext operations.
33
34 using System;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.ComponentModel;
38 using System.Data.Linq;
39 using System.Reflection;
40 using System.Security.Permissions;
41 using System.Web.DynamicData;
42 using System.Web.UI;
43
44 namespace System.Web.UI.WebControls
45 {
46         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
47         [AspNetHostingPermission (SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
48         public class LinqDataSourceView : DataSourceView, IStateManager
49         {
50                 public LinqDataSourceView (LinqDataSource owner, string name, HttpContext context)
51                         : base (owner, name)
52                 {
53                         source = owner;
54                 }
55
56                 LinqDataSource source;
57
58                 bool tracking;
59                 ParameterCollection delete_parameters,
60                         insert_parameters,
61                         select_new_parameters,
62                         update_parameters;
63                 ParameterCollection group_by_parameters,
64                         order_by_parameters,
65                         order_group_by_parameters,
66                         where_parameters;
67
68                 IEnumerable<ParameterCollection> AllParameters {
69                         get {
70                                 yield return delete_parameters;
71                                 yield return insert_parameters;
72                                 yield return select_new_parameters;
73                                 yield return update_parameters;
74                                 yield return group_by_parameters;
75                                 yield return order_by_parameters;
76                                 yield return order_group_by_parameters;
77                                 yield return where_parameters;
78                         }
79                 }
80
81                 [MonoTODO]
82                 public bool AutoGenerateOrderByClause { get; set; }
83                 [MonoTODO]
84                 public bool AutoGenerateWhereClause { get; set; }
85                 [MonoTODO]
86                 public bool AutoPage { get; set; }
87                 [MonoTODO]
88                 public bool AutoSort { get; set; }
89
90                 public override bool CanDelete {
91                         get { return EnableDelete; }
92                 }
93
94                 public override bool CanInsert {
95                         get { return EnableInsert; }
96                 }
97
98                 public override bool CanPage {
99                         get { return true; }
100                 }
101
102                 public override bool CanRetrieveTotalRowCount {
103                         get { return true; }
104                 }
105
106                 public override bool CanSort {
107                         get { return true; }
108                 }
109
110                 public override bool CanUpdate {
111                         get { return EnableUpdate; }
112                 }
113
114                 protected virtual Type ContextType {
115                         get { return ((IDynamicDataSource) source).ContextType; }
116                 }
117
118                 public virtual string ContextTypeName {
119                         get { return source.ContextTypeName; }
120                         set { source.ContextTypeName = value; }
121                 }
122
123                 [MonoTODO]
124                 public bool EnableDelete { get; set; }
125                 [MonoTODO]
126                 public bool EnableInsert { get; set; }
127                 [MonoTODO]
128                 public bool EnableObjectTracking { get; set; }
129                 [MonoTODO]
130                 public bool EnableUpdate { get; set; }
131
132                 [MonoTODO]
133                 public string TableName { get; set; }
134
135                 [MonoTODO]
136                 public string GroupBy { get; set; }
137
138                 [MonoTODO]
139                 public string OrderBy { get; set; }
140
141                 [MonoTODO]
142                 public string OrderGroupsBy { get; set; }
143
144                 [MonoTODO]
145                 public string SelectNew { get; set; }
146
147                 [MonoTODO]
148                 public string Where { get; set; }
149
150                 public ParameterCollection DeleteParameters {
151                         get { return GetParameterCollection (ref delete_parameters, false, false); }
152                 }
153
154                 public ParameterCollection InsertParameters {
155                         get { return GetParameterCollection (ref insert_parameters, false, false); }
156                 }
157
158                 public ParameterCollection UpdateParameters {
159                         get { return GetParameterCollection (ref update_parameters, true, true); }
160                 }
161
162                 public ParameterCollection SelectNewParameters {
163                         get { return GetParameterCollection (ref select_new_parameters, false, false); }
164                 }
165
166                 public ParameterCollection WhereParameters {
167                         get { return GetParameterCollection (ref where_parameters, true, true); }
168                 }
169
170                 public ParameterCollection GroupByParameters {
171                         get { return GetParameterCollection (ref group_by_parameters, true, true); }
172                 }
173
174                 public ParameterCollection OrderByParameters {
175                         get { return GetParameterCollection (ref order_by_parameters, true, true); }
176                 }
177
178                 public ParameterCollection OrderGroupsByParameters {
179                         get { return GetParameterCollection (ref order_group_by_parameters, true, true); }
180                 }
181
182                 ParameterCollection GetParameterCollection (ref ParameterCollection output, bool propagateTrackViewState, bool subscribeChanged)
183                 {
184                         if (output != null)
185                                 return output;
186                         
187                         output = new ParameterCollection ();
188                         if (subscribeChanged)
189                                 output.ParametersChanged += new EventHandler (ParametersChanged);
190                         
191                         if (IsTrackingViewState && propagateTrackViewState)
192                                 ((IStateManager) output).TrackViewState ();
193                         
194                         return output;
195                 }
196
197                 void ParametersChanged (object source, EventArgs args)
198                 {
199                         OnDataSourceViewChanged (EventArgs.Empty);
200                 }
201
202                 object data_context;
203
204                 object GetDataContext ()
205                 {
206                         if (data_context == null)
207                                 data_context = CreateContext (ContextType);
208                         return data_context;
209                 }
210
211                 public int Delete (IDictionary keys, IDictionary oldValues)
212                 {
213                         return ExecuteDelete (keys, oldValues);
214                 }
215
216                 public int Insert (IDictionary values)
217                 {
218                         return ExecuteInsert (values);
219                 }
220
221                 public IEnumerable Select (DataSourceSelectArguments arguments)
222                 {
223                         return ExecuteSelect (arguments);
224                 }
225
226                 public int Update (IDictionary keys, IDictionary values, IDictionary oldValues)
227                 {
228                         return ExecuteUpdate (keys, values, oldValues);
229                 }
230
231                 [MonoTODO]
232                 protected override int ExecuteDelete (IDictionary keys, IDictionary oldValues)
233                 {
234                         throw new NotImplementedException ();
235                 }
236
237                 protected override int ExecuteInsert (IDictionary values)
238                 {
239                         var dc = (DataContext) GetDataContext ();
240                         foreach (var mt in dc.Mapping.GetTables ()) {
241                                 if (mt.TableName != TableName)
242                                         continue;
243
244                                 var t = mt.RowType.Type;
245                                 ITable table = dc.GetTable (t);
246                                 object entity = Activator.CreateInstance (t);
247                                 // FIXME: merge InsertParameters
248                                 foreach (DictionaryEntry p in values)
249                                         t.GetProperty ((string) p.Key).SetValue (entity, p.Value, null);
250
251                                 InsertDataObject (dc, table, entity);
252                                 return 1;
253                         }
254                         throw new InvalidOperationException (String.Format ("Table '{0}' was not found on the data context '{1}'", TableName, ContextType));
255                 }
256
257                 [MonoTODO]
258                 protected override int ExecuteUpdate (IDictionary keys, IDictionary values, IDictionary oldValues)
259                 {
260                         throw new NotImplementedException ();
261                 }
262
263                 [MonoTODO]
264                 protected internal override IEnumerable ExecuteSelect (DataSourceSelectArguments arguments)
265                 {
266                         int max = arguments.MaximumRows;
267                         max = max == 0 ? int.MaxValue : max;
268                         int total = arguments.TotalRowCount;
269                         total = total < 0 ? int.MaxValue : total;
270                         int end = total == int.MaxValue ? total : arguments.StartRowIndex + total;
271
272                         DataContext dc = Activator.CreateInstance (ContextType, true) as DataContext;
273                         MemberInfo mi = GetTableMemberInfo (dc.GetType ());
274
275                         // am totally not sure it is fine.
276                         IEnumerable results;
277                         if (source.Select != null) {
278                                 // FIXME: merge SelectParameters.
279                                 results = dc.ExecuteQuery (mi is FieldInfo ? ((FieldInfo) mi).FieldType : ((PropertyInfo) mi).PropertyType, source.Select, new object [0]);
280                         } else {
281                                 results = (IEnumerable) (mi is FieldInfo ? ((FieldInfo) mi).GetValue (dc) : ((PropertyInfo) mi).GetValue (dc, null));
282                         }
283
284                         int i = 0;
285                         foreach (var e in results) {
286                                 if (i++ < arguments.StartRowIndex)
287                                         continue; // skip rows before StartRowIndex.
288                                 if (i < end)
289                                         yield return e;
290                         }
291                 }
292
293                 protected virtual void DeleteDataObject (object dataContext, object table, object oldDataObject)
294                 {
295                         ITable itable = ((DataContext) dataContext).GetTable (table.GetType ());
296                         itable.DeleteOnSubmit (oldDataObject);
297                 }
298
299                 protected virtual void InsertDataObject (object dataContext, object table, object newDataObject)
300                 {
301                         ITable itable = ((DataContext) dataContext).GetTable (table.GetType ());
302                         itable.InsertOnSubmit (newDataObject);
303                 }
304
305                 [MonoTODO]
306                 protected virtual void ResetDataObject (object table, object dataObject)
307                 {
308                         var dc = GetDataContext ();
309                         ITable itable = ((DataContext) dc).GetTable (table.GetType ());
310                         UpdateDataObject (dc, table, dataObject, itable.GetOriginalEntityState (dataObject));
311                 }
312
313                 protected virtual void UpdateDataObject (object dataContext, object table, object oldDataObject, object newDataObject)
314                 {
315                         DeleteDataObject (dataContext, table, oldDataObject);
316                         InsertDataObject (dataContext, table, newDataObject);
317                 }
318
319                 protected virtual object CreateContext (Type contextType)
320                 {
321                         OnContextCreating (new LinqDataSourceContextEventArgs ());
322                         var o = Activator.CreateInstance (contextType);
323                         OnContextCreated (new LinqDataSourceStatusEventArgs (o));
324                         return o;
325                 }
326
327                 [MonoTODO]
328                 protected virtual Type GetDataObjectType (Type tableType)
329                 {
330                         throw new NotImplementedException ();
331                 }
332
333                 MemberInfo table_member;
334
335                 protected virtual MemberInfo GetTableMemberInfo (Type contextType)
336                 {
337                         if (contextType == null)
338                                 throw new ArgumentNullException ("contextType");
339                         if (String.IsNullOrEmpty (TableName))
340                                 throw new InvalidOperationException (String.Format ("The TableName property of LinqDataSource '{0}' must specify a table property or field on the data context type.", source.ID));
341
342                         if (table_member != null && table_member.DeclaringType == contextType)
343                                 return table_member;
344
345                         var marr = contextType.GetMember (TableName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty);
346                         if (marr == null || marr.Length == 0)
347                                 throw new InvalidOperationException (String.Format ("Could not find a property or field called '{0}' on the data context type '{1}' of LinqDataSource '{2}'", TableName, contextType, source.ID));
348
349                         table_member = marr [0];
350                         return table_member;
351                 }
352
353                 #region Validation
354
355                 [MonoTODO]
356                 protected virtual void ValidateContextType (Type contextType, bool selecting)
357                 {
358                         throw new NotImplementedException ();
359                 }
360                 [MonoTODO]
361                 protected virtual void ValidateDeleteSupported (IDictionary keys, IDictionary oldValues)
362                 {
363                         throw new NotImplementedException ();
364                 }
365                 [MonoTODO]
366                 protected virtual void ValidateInsertSupported (IDictionary values)
367                 {
368                         throw new NotImplementedException ();
369                 }
370                 [MonoTODO]
371                 protected virtual void ValidateOrderByParameter (string name, string value)
372                 {
373                         throw new NotImplementedException ();
374                 }
375                 [MonoTODO]
376                 protected virtual void ValidateParameterName (string name)
377                 {
378                         throw new NotImplementedException ();
379                 }
380                 [MonoTODO]
381                 protected virtual void ValidateTableType (Type tableType, bool selecting)
382                 {
383                         throw new NotImplementedException ();
384                 }
385                 [MonoTODO]
386                 protected virtual void ValidateUpdateSupported (IDictionary keys, IDictionary values, IDictionary oldValues)
387                 {
388                         throw new NotImplementedException ();
389                 }
390
391                 #endregion
392
393                 #region ViewState
394
395                 bool IStateManager.IsTrackingViewState {
396                         get { return IsTrackingViewState; }
397                 }
398
399                 protected bool IsTrackingViewState {
400                         get { return tracking; }
401                 }
402
403                 [MonoTODO]
404                 public bool StoreOriginalValuesInViewState {
405                         get { throw new NotImplementedException (); }
406                         set { throw new NotImplementedException (); }
407                 }
408
409                 void IStateManager.LoadViewState (object savedState)
410                 {
411                         LoadViewState (savedState);
412                 }
413
414                 object IStateManager.SaveViewState ()
415                 {
416                         return SaveViewState ();
417                 }
418
419                 void IStateManager.TrackViewState ()
420                 {
421                         TrackViewState ();
422                 }
423
424                 protected virtual void LoadViewState (object savedState)
425                 {
426                         object [] vs = savedState as object [];
427                         if (vs == null)
428                                 return;
429                         int i = 0;
430                         foreach (var p in AllParameters) {
431                                 if (vs [i] != null)
432                                         ((IStateManager) p).LoadViewState (vs [i]);
433                                 i++;
434                         }
435                 }
436
437                 protected virtual object SaveViewState ()
438                 {
439                         object [] vs = new object [8];
440                         int i = 0;
441                         foreach (var p in AllParameters) {
442                                 if (p != null)
443                                         vs [i] = ((IStateManager) p).SaveViewState ();
444                                 i++;
445                         }
446
447                         foreach (object o in vs)
448                                 if (o != null)
449                                         return vs;
450                         return null;
451                 }
452
453                 protected virtual void TrackViewState ()
454                 {
455                         tracking = true;
456
457                         foreach (var p in AllParameters)
458                                 if (p != null)
459                                         ((IStateManager) p).TrackViewState ();
460                 }
461
462                 #endregion
463
464                 #region Events and Overridable Event Handler Invocations
465
466                 [MonoTODO]
467                 public event EventHandler<LinqDataSourceStatusEventArgs> ContextCreated;
468                 [MonoTODO]
469                 public event EventHandler<LinqDataSourceContextEventArgs> ContextCreating;
470                 [MonoTODO]
471                 public event EventHandler<LinqDataSourceDisposeEventArgs> ContextDisposing;
472                 [MonoTODO]
473                 public event EventHandler<LinqDataSourceStatusEventArgs> Deleted;
474                 [MonoTODO]
475                 public event EventHandler<LinqDataSourceDeleteEventArgs> Deleting;
476                 [MonoTODO]
477                 internal event EventHandler<DynamicValidatorEventArgs> Exception;
478                 [MonoTODO]
479                 public event EventHandler<LinqDataSourceStatusEventArgs> Inserted;
480                 [MonoTODO]
481                 public event EventHandler<LinqDataSourceInsertEventArgs> Inserting;
482                 [MonoTODO]
483                 public event EventHandler<LinqDataSourceStatusEventArgs> Selected;
484                 [MonoTODO]
485                 public event EventHandler<LinqDataSourceSelectEventArgs> Selecting;
486                 [MonoTODO]
487                 public event EventHandler<LinqDataSourceStatusEventArgs> Updated;
488                 [MonoTODO]
489                 public event EventHandler<LinqDataSourceUpdateEventArgs> Updating;
490
491                 protected virtual void OnContextCreated (LinqDataSourceStatusEventArgs e)
492                 {
493                         if (ContextCreated != null)
494                                 ContextCreated (this, e);
495                 }
496
497                 protected virtual void OnContextCreating (LinqDataSourceContextEventArgs e)
498                 {
499                         if (ContextCreating != null)
500                                 ContextCreating (this, e);
501                 }
502
503                 protected virtual void OnContextDisposing (LinqDataSourceDisposeEventArgs e)
504                 {
505                         if (ContextDisposing != null)
506                                 ContextDisposing (this, e);
507                 }
508
509                 protected virtual void OnDeleted (LinqDataSourceStatusEventArgs e)
510                 {
511                         if (Deleted != null)
512                                 Deleted (this, e);
513                 }
514
515                 protected virtual void OnDeleting (LinqDataSourceDeleteEventArgs e)
516                 {
517                         if (Deleting != null)
518                                 Deleting (this, e);
519                 }
520
521                 protected virtual void OnException (DynamicValidatorEventArgs e)
522                 {
523                         if (Exception != null)
524                                 Exception (this, e);
525                 }
526
527                 protected virtual void OnInserted (LinqDataSourceStatusEventArgs e)
528                 {
529                         if (Inserted != null)
530                                 Inserted (this, e);
531                 }
532
533                 protected virtual void OnInserting (LinqDataSourceInsertEventArgs e)
534                 {
535                         if (Inserting != null)
536                                 Inserting (this, e);
537                 }
538
539                 protected virtual void OnSelected (LinqDataSourceStatusEventArgs e)
540                 {
541                         if (Selected != null)
542                                 Selected (this, e);
543                 }
544
545                 protected virtual void OnSelecting (LinqDataSourceSelectEventArgs e)
546                 {
547                         if (Selecting != null)
548                                 Selecting (this, e);
549                 }
550
551                 protected virtual void OnUpdated (LinqDataSourceStatusEventArgs e)
552                 {
553                         if (Updated != null)
554                                 Updated (this, e);
555                 }
556
557                 protected virtual void OnUpdating (LinqDataSourceUpdateEventArgs e)
558                 {
559                         if (Updating != null)
560                                 Updating (this, e);
561                 }
562
563                 #endregion
564         }
565 }