fixed tests
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / ObjectDataSourceView.cs
1 //
2 // System.Web.UI.WebControls.ObjectDataSourceView
3 //
4 // Authors:
5 //  Lluis Sanchez Gual (lluis@novell.com)
6 //
7 // (C) 2005 Novell, Inc. (http://www.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 #if NET_2_0
32 using System;
33 using System.Reflection;
34 using System.Collections;
35 using System.Collections.Specialized;
36 using System.ComponentModel;
37 using System.IO;
38 using System.Data;
39 using System.Web.Compilation;
40
41 namespace System.Web.UI.WebControls
42 {
43
44         public class ObjectDataSourceView : DataSourceView, IStateManager
45         {
46                 ObjectDataSource owner;
47                 HttpContext context;
48                 Type objectType;
49                 Type dataObjectType;
50
51                 bool convertNullToDBNull = false;
52                 bool enablePaging = false;
53                 string dataObjectTypeName = null;
54                 string filterExpression = null;
55                 string maximumRowsParameterName = null;
56                 string oldValuesParameterFormatString = null;
57                 string deleteMethod = null;
58                 string insertMethod = null;
59                 string selectCountMethod = null;
60                 string selectMethod = null;
61                 string sortParameterName = null;
62                 string startRowIndexParameterName = null;
63                 string typeName = null;
64                 string updateMethod = null;
65                 private static string privateBinPath = null;
66
67                 bool isTrackingViewState = false;
68                 ParameterCollection selectParameters;
69                 ParameterCollection updateParameters;
70                 ParameterCollection deleteParameters;
71                 ParameterCollection insertParameters;
72                 ParameterCollection filterParameters;
73                 
74                 private static readonly object DeletedEvent = new object();
75                 private static readonly object DeletingEvent = new object();
76                 private static readonly object FilteringEvent = new object();
77                 private static readonly object InsertedEvent = new object();
78                 private static readonly object InsertingEvent = new object();
79                 private static readonly object ObjectCreatedEvent = new object();
80                 private static readonly object ObjectCreatingEvent = new object();
81                 private static readonly object ObjectDisposingEvent = new object();
82                 //              private static readonly object ResolvingMethodEvent = new object();
83                 private static readonly object SelectedEvent = new object();
84                 private static readonly object SelectingEvent = new object();
85                 private static readonly object UpdatedEvent = new object();
86                 private static readonly object UpdatingEvent = new object();
87                 
88                 public ObjectDataSourceView (ObjectDataSource owner, string name, HttpContext context): base (owner, name)
89                 {
90                         this.owner = owner;
91                         this.context = context;
92                 }
93                 
94                 public event ObjectDataSourceStatusEventHandler Deleted {
95                         add { Events.AddHandler (DeletedEvent, value); }
96                         remove { Events.RemoveHandler (DeletedEvent, value); }
97                 }
98                 
99                 public event ObjectDataSourceMethodEventHandler Deleting {
100                         add { Events.AddHandler (DeletingEvent, value); }
101                         remove { Events.RemoveHandler (DeletingEvent, value); }
102                 }
103                 
104                 public event ObjectDataSourceFilteringEventHandler Filtering {
105                         add { Events.AddHandler (FilteringEvent, value); }
106                         remove { Events.RemoveHandler (FilteringEvent, value); }
107                 }
108                 
109                 public event ObjectDataSourceStatusEventHandler Inserted {
110                         add { Events.AddHandler (InsertedEvent, value); }
111                         remove { Events.RemoveHandler (InsertedEvent, value); }
112                 }
113                 
114                 public event ObjectDataSourceMethodEventHandler Inserting {
115                         add { Events.AddHandler (InsertingEvent, value); }
116                         remove { Events.RemoveHandler (InsertingEvent, value); }
117                 }
118                 
119                 public event ObjectDataSourceObjectEventHandler ObjectCreated {
120                         add { Events.AddHandler (ObjectCreatedEvent, value); }
121                         remove { Events.RemoveHandler (ObjectCreatedEvent, value); }
122                 }
123                 
124                 public event ObjectDataSourceObjectEventHandler ObjectCreating {
125                         add { Events.AddHandler (ObjectCreatingEvent, value); }
126                         remove { Events.RemoveHandler (ObjectCreatingEvent, value); }
127                 }
128                 
129                 public event ObjectDataSourceDisposingEventHandler ObjectDisposing {
130                         add { Events.AddHandler (ObjectDisposingEvent, value); }
131                         remove { Events.RemoveHandler (ObjectDisposingEvent, value); }
132                 }
133                 
134                 /*              public event ObjectDataSourceResolvingMethodEventHandler ResolvingMethod {
135                                 add { Events.AddHandler (ResolvingMethodEvent, value); }
136                                 remove { Events.RemoveHandler (ResolvingMethodEvent, value); }
137                                 }
138                 */
139                 public event ObjectDataSourceStatusEventHandler Selected {
140                         add { Events.AddHandler (SelectedEvent, value); }
141                         remove { Events.RemoveHandler (SelectedEvent, value); }
142                 }
143                 
144                 public event ObjectDataSourceSelectingEventHandler Selecting {
145                         add { Events.AddHandler (SelectingEvent, value); }
146                         remove { Events.RemoveHandler (SelectingEvent, value); }
147                 }
148                 
149                 public event ObjectDataSourceStatusEventHandler Updated {
150                         add { Events.AddHandler (UpdatedEvent, value); }
151                         remove { Events.RemoveHandler (UpdatedEvent, value); }
152                 }
153                 
154                 public event ObjectDataSourceMethodEventHandler Updating {
155                         add { Events.AddHandler (UpdatingEvent, value); }
156                         remove { Events.RemoveHandler (UpdatingEvent, value); }
157                 }
158                 
159                 protected virtual void OnDeleted (ObjectDataSourceStatusEventArgs e)
160                 {
161                         if (Events != null) {
162                                 ObjectDataSourceStatusEventHandler eh = (ObjectDataSourceStatusEventHandler) Events [DeletedEvent];
163                                 if (eh != null) eh (this, e);
164                         }
165                 }
166                 
167                 protected virtual void OnDeleting (ObjectDataSourceMethodEventArgs e)
168                 {
169                         if (Events != null) {
170                                 ObjectDataSourceMethodEventHandler eh = (ObjectDataSourceMethodEventHandler) Events [DeletingEvent];
171                                 if (eh != null) eh (this, e);
172                         }
173                 }
174                 
175                 protected virtual void OnFiltering (ObjectDataSourceFilteringEventArgs e)
176                 {
177                         if (Events != null) {
178                                 ObjectDataSourceFilteringEventHandler eh = (ObjectDataSourceFilteringEventHandler) Events [FilteringEvent];
179                                 if (eh != null) eh (this, e);
180                         }
181                 }
182                 
183                 protected virtual void OnInserted (ObjectDataSourceStatusEventArgs e)
184                 {
185                         if (Events != null) {
186                                 ObjectDataSourceStatusEventHandler eh = (ObjectDataSourceStatusEventHandler) Events [InsertedEvent];
187                                 if (eh != null) eh (this, e);
188                         }
189                 }
190                 
191                 protected virtual void OnInserting (ObjectDataSourceMethodEventArgs e)
192                 {
193                         if (Events != null) {
194                                 ObjectDataSourceMethodEventHandler eh = (ObjectDataSourceMethodEventHandler) Events [InsertingEvent];
195                                 if (eh != null) eh (this, e);
196                         }
197                 }
198                 
199                 protected virtual void OnObjectCreated (ObjectDataSourceEventArgs e)
200                 {
201                         if (Events != null) {
202                                 ObjectDataSourceObjectEventHandler eh = (ObjectDataSourceObjectEventHandler) Events [ObjectCreatedEvent];
203                                 if (eh != null) eh (this, e);
204                         }
205                 }
206                 
207                 protected virtual void OnObjectCreating (ObjectDataSourceEventArgs e)
208                 {
209                         if (Events != null) {
210                                 ObjectDataSourceObjectEventHandler eh = (ObjectDataSourceObjectEventHandler) Events [ObjectCreatingEvent];
211                                 if (eh != null) eh (this, e);
212                         }
213                 }
214                 
215                 protected virtual void OnObjectDisposing (ObjectDataSourceDisposingEventArgs e)
216                 {
217                         if (Events != null) {
218                                 ObjectDataSourceDisposingEventHandler eh = (ObjectDataSourceDisposingEventHandler) Events [ObjectDisposingEvent];
219                                 if (eh != null) eh (this, e);
220                         }
221                 }
222                 
223                 /*              protected virtual void OnResolvingMethod (ObjectDataSourceResolvingMethodEventArgs e)
224                                 {
225                                 if (Events != null) {
226                                 ObjectDataSourceResolvingMethodEventHandler eh = (ObjectDataSourceResolvingMethodEventHandler) Events [ResolvingMethodEvent];
227                                 if (eh != null) eh (this, e);
228                                 }
229                                 }
230                 */
231                 
232                 protected virtual void OnSelected (ObjectDataSourceStatusEventArgs e)
233                 {
234                         if (Events != null) {
235                                 ObjectDataSourceStatusEventHandler eh = (ObjectDataSourceStatusEventHandler) Events [SelectedEvent];
236                                 if (eh != null) eh (this, e);
237                         }
238                 }
239                 
240                 protected virtual void OnSelecting (ObjectDataSourceSelectingEventArgs e)
241                 {
242                         if (Events != null) {
243                                 ObjectDataSourceSelectingEventHandler eh = (ObjectDataSourceSelectingEventHandler) Events [SelectingEvent];
244                                 if (eh != null) eh (this, e);
245                         }
246                 }
247                 
248                 protected virtual void OnUpdated (ObjectDataSourceStatusEventArgs e)
249                 {
250                         if (Events != null) {
251                                 ObjectDataSourceStatusEventHandler eh = (ObjectDataSourceStatusEventHandler) Events [UpdatedEvent];
252                                 if (eh != null) eh (this, e);
253                         }
254                 }
255                 
256                 protected virtual void OnUpdating (ObjectDataSourceMethodEventArgs e)
257                 {
258                         if (Events != null) {
259                                 ObjectDataSourceMethodEventHandler eh = (ObjectDataSourceMethodEventHandler) Events [UpdatingEvent];
260                                 if (eh != null) eh (this, e);
261                         }
262                 }
263                 
264                 public override bool CanDelete {
265                         get { return DeleteMethod.Length > 0; }
266                 }
267                 
268                 public override bool CanInsert {
269                         get { return InsertMethod.Length > 0; }
270                 }
271                 
272                 public override bool CanPage {
273                         get { return EnablePaging; }
274                 }
275                 
276                 public override bool CanRetrieveTotalRowCount {
277                         get {
278                                 if( SelectCountMethod.Length > 0)
279                                         return true;
280
281                                 return !EnablePaging;
282                         }
283                 }
284                 
285                 public override bool CanSort {
286                         get { return true; }
287                 }
288                 
289                 public override bool CanUpdate {
290                         get { return UpdateMethod.Length > 0; }
291                 }
292
293                 // LAME SPEC: MSDN says value should be stored in ViewState but tests show otherwise.
294                 private ConflictOptions conflictDetection = ConflictOptions.OverwriteChanges;
295                 public ConflictOptions ConflictDetection {
296                         get { return conflictDetection; }
297                         set { conflictDetection = value; }
298                 }
299
300                 public bool ConvertNullToDBNull {
301                         get { return convertNullToDBNull; }
302                         set { convertNullToDBNull = value; }
303                 }
304
305                 public string DataObjectTypeName {
306                         get {
307                                 return dataObjectTypeName != null ? dataObjectTypeName : string.Empty;
308                         }
309                         set {
310                                 dataObjectTypeName = value;
311                         }
312                 }
313
314                 public string DeleteMethod {
315                         get {
316                                 return deleteMethod != null ? deleteMethod : string.Empty;
317                         }
318                         set {
319                                 deleteMethod = value;
320                         }
321                 }
322                 
323                 public ParameterCollection DeleteParameters {
324                         get {
325                                 if (deleteParameters == null) {
326                                         deleteParameters = new ParameterCollection ();
327                                         deleteParameters.ParametersChanged += new EventHandler (OnParametersChanged); 
328                                 }
329                                 return deleteParameters;
330                         }
331                 }
332
333                 public bool EnablePaging {
334                         get {
335                                 return enablePaging;
336                         }
337                         set {
338                                 enablePaging = value;
339                         }
340                 }
341
342                 public string FilterExpression {
343                         get {
344                                 return filterExpression != null ? filterExpression : string.Empty;
345                         }
346                         set {
347                                 filterExpression = value;
348                         }
349                 }
350
351                 public ParameterCollection FilterParameters {
352                         get {
353                                 if (filterParameters == null) {
354                                         filterParameters = new ParameterCollection ();
355                                         filterParameters.ParametersChanged += new EventHandler (OnParametersChanged); 
356                                         if (IsTrackingViewState)
357                                                 ((IStateManager)filterParameters).TrackViewState ();
358                                 }
359                                 return filterParameters;
360                         }
361                 }
362
363                 public string InsertMethod {
364                         get {
365                                 return insertMethod != null ? insertMethod : string.Empty;
366                         }
367                         set {
368                                 insertMethod = value;
369                         }
370                 }
371
372                 public ParameterCollection InsertParameters {
373                         get {
374                                 if (insertParameters == null) {
375                                         insertParameters = new ParameterCollection ();
376                                         insertParameters.ParametersChanged += new EventHandler (OnParametersChanged); 
377                                 }
378                                 return insertParameters;
379                         }
380                 }
381
382                 public string MaximumRowsParameterName {
383                         get {
384                                 return maximumRowsParameterName != null ? maximumRowsParameterName : "maximumRows";
385                         }
386                         set {
387                                 maximumRowsParameterName = value;
388                         }
389                 }
390
391                 public string OldValuesParameterFormatString {
392                         get {
393                                 return oldValuesParameterFormatString != null ? oldValuesParameterFormatString : "{0}";
394                         }
395                         set {
396                                 oldValuesParameterFormatString = value;
397                         }
398                 }
399
400                 public string SelectCountMethod {
401                         get {
402                                 return selectCountMethod != null ? selectCountMethod : string.Empty;
403                         }
404                         set {
405                                 selectCountMethod = value;
406                         }
407                 }
408
409                 public string SelectMethod {
410                         get {
411                                 return selectMethod != null ? selectMethod : string.Empty;
412                         }
413                         set {
414                                 selectMethod = value;
415                         }
416                 }
417
418                 public ParameterCollection SelectParameters {
419                         get {
420                                 if (selectParameters == null) {
421                                         selectParameters = new ParameterCollection ();
422                                         selectParameters.ParametersChanged += new EventHandler (OnParametersChanged); 
423                                         if (IsTrackingViewState)
424                                                 ((IStateManager)selectParameters).TrackViewState ();
425                                 }
426                                 return selectParameters;
427                         }
428                 }
429
430                 public string SortParameterName {
431                         get {
432                                 return sortParameterName != null ? sortParameterName : string.Empty;
433                         }
434                         set {
435                                 sortParameterName = value;
436                         }
437                 }
438
439                 public string StartRowIndexParameterName {
440                         get {
441                                 return startRowIndexParameterName != null ? startRowIndexParameterName : "startRowIndex";
442                         }
443                         set {
444                                 startRowIndexParameterName = value;
445                         }
446                 }
447
448                 public string TypeName {
449                         get {
450                                 return typeName != null ? typeName : string.Empty;
451                         }
452                         set {
453                                 typeName = value;
454                                 objectType = null;
455                         }
456                 }
457
458                 public string UpdateMethod {
459                         get {
460                                 return updateMethod != null ? updateMethod : string.Empty;
461                         }
462                         set {
463                                 updateMethod = value;
464                         }
465                 }
466
467                 public ParameterCollection UpdateParameters {
468                         get {
469                                 if (updateParameters == null) {
470                                         updateParameters = new ParameterCollection ();
471                                         updateParameters.ParametersChanged += new EventHandler (OnParametersChanged); 
472                                 }
473                                 return updateParameters;
474                         }
475                 }
476
477                 private static string PrivateBinPath {
478                         get {
479                                 if (privateBinPath != null)
480                                         return privateBinPath;
481
482                                 AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
483                                 privateBinPath = Path.Combine (setup.ApplicationBase, setup.PrivateBinPath);
484
485                                 return privateBinPath;
486                         }
487                 }
488
489                 private Type LoadType (string typeName)
490                 {
491                         Type type = Type.GetType (typeName);
492                         if (type != null)
493                                 return type;
494
495 #if !TARGET_JVM
496                         IList tla;
497                         if ((tla = BuildManager.TopLevelAssemblies) != null) {
498                                 foreach (Assembly asm in tla) {
499                                         if (asm == null)
500                                                 continue;
501                                         type = asm.GetType (typeName);
502                                         if (type != null)
503                                                 return type;
504                                 }
505                         }
506 #endif
507                         if (!Directory.Exists (PrivateBinPath))
508                                 return null;
509
510                         string [] binDlls = Directory.GetFiles (PrivateBinPath, "*.dll");
511                         foreach (string s in binDlls) {
512                                 Assembly binA = Assembly.LoadFrom (s);
513                                 type = binA.GetType (typeName);
514                                 if (type == null)
515                                         continue;
516
517                                 return type;
518                         }
519
520                         return null;
521                 }
522     
523                 Type ObjectType {
524                         get {
525                                 if (objectType == null) {
526                                         objectType = LoadType (TypeName);
527                                         if (objectType == null)
528                                                 throw new InvalidOperationException ("Type not found: " + TypeName);
529                                 }
530                                 return objectType;
531                         }
532                 }
533                 
534                 Type DataObjectType {
535                         get {
536                                 if (dataObjectType == null) {
537                                         dataObjectType = LoadType (DataObjectTypeName);
538                                         if (objectType == null)
539                                                 throw new InvalidOperationException ("Type not found: " + DataObjectTypeName);
540                                 }
541                                 return dataObjectType;
542                         }
543                 }
544                 
545                 public IEnumerable Select (DataSourceSelectArguments arguments)
546                 {
547                         return ExecuteSelect (arguments);
548                 }
549                 
550                 public int Update (IDictionary keys, IDictionary values, IDictionary oldValues)
551                 {
552                         return ExecuteUpdate (keys, values, oldValues);
553                 }
554                 
555                 public int Delete (IDictionary keys, IDictionary oldValues)
556                 {
557                         return ExecuteDelete (keys, oldValues);
558                 }
559                 
560                 public int Insert (IDictionary values)
561                 {
562                         return ExecuteInsert (values);
563                 }
564                 
565                 protected override int ExecuteInsert (IDictionary values)
566                 {
567                         if (!CanInsert)
568                                 throw new NotSupportedException ("Insert operation not supported.");
569                                 
570                         IOrderedDictionary paramValues;
571                         MethodInfo method;
572                         
573                         if (DataObjectTypeName.Length == 0) {
574                                 paramValues = MergeParameterValues (InsertParameters, values, null, true);
575                                 method = GetObjectMethod (InsertMethod, paramValues, DataObjectMethodType.Insert);
576                         } else {
577                                 method = ResolveDataObjectMethod (InsertMethod, values, null, out paramValues);
578                         }
579                         
580                         ObjectDataSourceMethodEventArgs args = new ObjectDataSourceMethodEventArgs (paramValues);
581                         OnInserting (args);
582                         if (args.Cancel)
583                                 return -1;
584                         
585                         ObjectDataSourceStatusEventArgs rargs = InvokeMethod (method, paramValues);
586                         OnInserted (rargs);
587                         
588                         if (rargs.Exception != null && !rargs.ExceptionHandled)
589                                 throw rargs.Exception;
590
591                         if (owner.EnableCaching)
592                                 owner.Cache.Expire ();
593                         
594                         OnDataSourceViewChanged (EventArgs.Empty);
595
596                         return -1;
597                 }
598
599                 protected override int ExecuteDelete (IDictionary keys, IDictionary oldValues)
600                 {
601                         if (!CanDelete)
602                                 throw new NotSupportedException ("Delete operation not supported.");
603                                 
604                         if (ConflictDetection == ConflictOptions.CompareAllValues && (oldValues == null || oldValues.Count == 0))
605                                 throw new InvalidOperationException ("ConflictDetection is set to CompareAllValues and oldValues collection is null or empty.");
606
607                         IDictionary oldDataValues;
608                         oldDataValues = BuildOldValuesList (keys, oldValues, false);
609                                         
610                         IOrderedDictionary paramValues;
611                         MethodInfo method;
612                         
613                         if (DataObjectTypeName.Length == 0) {
614                                 paramValues = MergeParameterValues (DeleteParameters, null, oldDataValues, true);
615                                 method = GetObjectMethod (DeleteMethod, paramValues, DataObjectMethodType.Delete);
616                         } else {
617                                 method = ResolveDataObjectMethod (DeleteMethod, oldDataValues, null, out paramValues);
618                         }
619                         
620                         ObjectDataSourceMethodEventArgs args = new ObjectDataSourceMethodEventArgs (paramValues);
621                         OnDeleting (args);
622                         if (args.Cancel)
623                                 return -1;
624                         
625                         ObjectDataSourceStatusEventArgs rargs = InvokeMethod (method, paramValues);
626                         
627                         OnDeleted (rargs);
628                         
629                         if (rargs.Exception != null && !rargs.ExceptionHandled)
630                                 throw rargs.Exception;
631
632                         if (owner.EnableCaching)
633                                 owner.Cache.Expire ();
634                         
635                         OnDataSourceViewChanged (EventArgs.Empty);
636
637                         return -1;
638                 }
639
640                 protected override int ExecuteUpdate (IDictionary keys, IDictionary values, IDictionary oldValues)
641                 {
642                         IOrderedDictionary paramValues;
643                         MethodInfo method;
644
645                         IDictionary oldDataValues;
646                         oldDataValues = BuildOldValuesList (keys, oldValues, true);
647
648                         if (DataObjectTypeName.Length == 0)
649                         {
650                                 IDictionary dataValues;
651                                 dataValues = values;
652                                 paramValues = MergeParameterValues (UpdateParameters, dataValues, oldDataValues, false);
653                                 method = GetObjectMethod (UpdateMethod, paramValues, DataObjectMethodType.Update);
654                         }
655                         else
656                         {
657                                 if (ConflictDetection != ConflictOptions.CompareAllValues) {
658                                         oldDataValues = null;
659                                 }
660                                 IDictionary dataValues = new Hashtable ();
661                                 if (keys != null) {
662                                         foreach (DictionaryEntry de in keys)
663                                                 dataValues [de.Key] = de.Value;
664                                 }
665                                 if (values != null) {
666                                         foreach (DictionaryEntry de in values)
667                                                 dataValues [de.Key] = de.Value;
668                                 }
669
670                                 method = ResolveDataObjectMethod (UpdateMethod, dataValues, oldDataValues, out paramValues);
671                         }                       
672
673                         ObjectDataSourceMethodEventArgs args = new ObjectDataSourceMethodEventArgs (paramValues);
674                         OnUpdating (args);
675                         if (args.Cancel)
676                                 return -1;
677                         
678                         ObjectDataSourceStatusEventArgs rargs = InvokeMethod (method, paramValues);
679                         OnUpdated (rargs);
680                         
681                         if (rargs.Exception != null && !rargs.ExceptionHandled)
682                                 throw rargs.Exception;
683
684                         if (owner.EnableCaching)
685                                 owner.Cache.Expire ();
686                         
687                         OnDataSourceViewChanged (EventArgs.Empty);
688
689                         return -1;
690                 }
691
692                 private IDictionary BuildOldValuesList (IDictionary keys, IDictionary oldValues, bool keysWin) 
693                 {
694                         IDictionary oldDataValues;
695                         if (ConflictDetection == ConflictOptions.CompareAllValues) {
696                                 oldDataValues = new Hashtable ();
697                                 if (keys != null && !keysWin) {
698                                         foreach (DictionaryEntry de in keys)
699                                                 oldDataValues [de.Key] = de.Value;
700                                 }
701                                 if (oldValues != null) {
702                                         foreach (DictionaryEntry de in oldValues)
703                                                 oldDataValues [de.Key] = de.Value;
704                                 }
705                                 if (keys != null && keysWin) {
706                                         foreach (DictionaryEntry de in keys)
707                                                 oldDataValues [de.Key] = de.Value;
708                                 }
709                         }
710                         else {
711                                 oldDataValues = keys;
712                         }
713
714                         return oldDataValues;
715                 }
716
717                 protected internal override IEnumerable ExecuteSelect (DataSourceSelectArguments arguments)
718                 {
719                         arguments.RaiseUnsupportedCapabilitiesError (this);
720
721                         IOrderedDictionary paramValues = MergeParameterValues (SelectParameters, null, null, true);
722                         ObjectDataSourceSelectingEventArgs args = new ObjectDataSourceSelectingEventArgs (paramValues, arguments, false);
723
724                         object result = null;
725
726                         if (owner.EnableCaching)
727                                 result = owner.Cache.GetCachedObject (SelectMethod, SelectParameters);
728
729                         if (result == null) {
730                                 OnSelecting (args);
731                                 if (args.Cancel)
732                                         return new ArrayList ();
733
734                                 if (CanRetrieveTotalRowCount && arguments.RetrieveTotalRowCount)
735                                         arguments.TotalRowCount = QueryTotalRowCount (paramValues, arguments);
736
737                                 if (CanPage) {
738                                         if (StartRowIndexParameterName.Length == 0)
739                                                 throw new InvalidOperationException ("Paging is enabled, but the StartRowIndexParameterName property is not set.");
740                                         if (MaximumRowsParameterName.Length == 0)
741                                                 throw new InvalidOperationException ("Paging is enabled, but the MaximumRowsParameterName property is not set.");
742                                         paramValues [StartRowIndexParameterName] = arguments.StartRowIndex;
743                                         paramValues [MaximumRowsParameterName] = arguments.MaximumRows;
744                                 }
745
746                                 if (SortParameterName.Length > 0)
747                                         paramValues [SortParameterName] = arguments.SortExpression;
748
749                                 result = InvokeSelect (SelectMethod, paramValues);
750
751                                 if (owner.EnableCaching)
752                                         owner.Cache.SetCachedObject (SelectMethod, SelectParameters, result);
753                         }
754
755                         if (FilterExpression.Length > 0 && !(result is DataGrid || result is DataView || result is DataTable))
756                                 throw new NotSupportedException ("The FilterExpression property was set and the Select method does not return a DataSet, DataTable, or DataView.");
757
758                         if (owner.EnableCaching && result is IDataReader)
759                                 throw new NotSupportedException ("Data source does not support caching objects that implement IDataReader");
760                         
761                         if (result is DataSet) {
762                                 DataSet dset = (DataSet) result;
763                                 if (dset.Tables.Count == 0)
764                                         throw new InvalidOperationException ("The select method returnet a DataSet which doesn't contain any table.");
765                                 result = dset.Tables [0];
766                         }
767                         
768                         if (result is DataTable) {
769                                 DataView dview = new DataView ((DataTable)result);
770                                 if (arguments.SortExpression != null && arguments.SortExpression.Length > 0) {
771                                         dview.Sort = arguments.SortExpression;
772                                 }
773                                 if (FilterExpression.Length > 0) {
774                                         IOrderedDictionary fparams = FilterParameters.GetValues (context, owner);
775                                         ObjectDataSourceFilteringEventArgs fargs = new ObjectDataSourceFilteringEventArgs (fparams);
776                                         OnFiltering (fargs);
777                                         if (!fargs.Cancel) {
778                                                 object[] formatValues = new object [fparams.Count];
779                                                 for (int n=0; n<formatValues.Length; n++) {
780                                                         formatValues [n] = fparams [n];
781                                                         if (formatValues [n] == null) return dview;
782                                                 }
783                                                 dview.RowFilter = string.Format (FilterExpression, formatValues);
784                                         }
785                                 }
786                                 return dview;
787                         }
788                         
789                         if (result is IEnumerable)
790                                 return (IEnumerable) result;
791                         else
792                                 return new object[] { result };
793                 }
794                 
795                 int QueryTotalRowCount (IOrderedDictionary mergedParameters, DataSourceSelectArguments arguments)
796                 {
797                         ObjectDataSourceSelectingEventArgs countArgs = new ObjectDataSourceSelectingEventArgs (mergedParameters, arguments, true);
798                         OnSelecting (countArgs);
799                         if (countArgs.Cancel)
800                                 return 0;
801                         
802                         object count = InvokeSelect (SelectCountMethod, mergedParameters);
803                         return (int) Convert.ChangeType (count, typeof(int));
804                 }
805                 
806                 object InvokeSelect (string methodName, IOrderedDictionary paramValues)
807                 {
808                         MethodInfo method = GetObjectMethod (methodName, paramValues, DataObjectMethodType.Select);
809                         ObjectDataSourceStatusEventArgs rargs = InvokeMethod (method, paramValues);
810                         OnSelected (rargs);
811                         
812                         if (rargs.Exception != null && !rargs.ExceptionHandled)
813                                 throw rargs.Exception;
814
815                         return rargs.ReturnValue;
816                 }
817                 
818                 ObjectDataSourceStatusEventArgs InvokeMethod (MethodInfo method, IOrderedDictionary paramValues)
819                 {
820                         object instance = null;
821
822                         if (!method.IsStatic)
823                                 instance = CreateObjectInstance ();
824
825                         ParameterInfo[] pars = method.GetParameters ();
826                         
827                         ArrayList outParamInfos;
828                         object[] methodArgs = GetParameterArray (pars, paramValues, out outParamInfos); 
829                         
830                         if (methodArgs == null)
831                                 throw CreateMethodException (method.Name, paramValues);
832                                         
833                         object result = null;
834                         Hashtable outParams = null;
835                         
836                         try {
837                                 result = method.Invoke (instance, methodArgs);
838                                 if (outParamInfos != null) {
839                                         outParams = new Hashtable ();
840                                         foreach (ParameterInfo op in outParamInfos)
841                                                 outParams [op.Name] = methodArgs [op.Position];
842                                 }
843                                 return new ObjectDataSourceStatusEventArgs (result, outParams, null);
844                         }
845                         catch (Exception ex) {
846                                 return new ObjectDataSourceStatusEventArgs (result, outParams, ex);
847                         }
848                         finally {
849                                 if (instance != null)
850                                         DisposeObjectInstance (instance);
851                         }
852                 }
853                 
854                 MethodInfo GetObjectMethod (string methodName, IOrderedDictionary parameters, DataObjectMethodType methodType)
855                 {
856                         MemberInfo[] methods = ObjectType.GetMember (methodName, MemberTypes.Method, BindingFlags.Instance | 
857                                                                                  BindingFlags.Static | 
858                                                                                  BindingFlags.Public | 
859                                                                                  BindingFlags.IgnoreCase |
860                                                                                  BindingFlags.FlattenHierarchy);
861                         if (methods.Length > 1) {
862                                 // MSDN: The ObjectDataSource resolves method overloads by method name and number
863                                 // of parameters; the names and types of the parameters are not considered.
864                                 // LAMESPEC: the tests show otherwise
865                                 DataObjectMethodAttribute methodAttribute = null;
866                                 MethodInfo methodInfo = null;
867                                 bool hasConflict = false;
868                                 foreach (MethodInfo me in methods) { // we look for methods only
869                                         ParameterInfo [] pinfos = me.GetParameters ();
870                                         if (pinfos.Length == parameters.Count) {
871                                                 object [] attrs = me.GetCustomAttributes (typeof (DataObjectMethodAttribute), true);
872                                                 DataObjectMethodAttribute domAttr = (attrs != null && attrs.Length > 0) ? (DataObjectMethodAttribute) attrs [0] : null;
873                                                 if (domAttr != null && domAttr.MethodType != methodType)
874                                                         continue;
875
876                                                 bool paramsMatch = true;
877                                                 foreach (ParameterInfo pinfo in pinfos) {
878                                                         if (!parameters.Contains (pinfo.Name)) {
879                                                                 paramsMatch = false;
880                                                                 break;
881                                                         }
882                                                 }
883
884                                                 if (!paramsMatch)
885                                                         continue;
886
887                                                 if (domAttr != null) {
888                                                         if (methodAttribute != null) {
889                                                                 if (methodAttribute.IsDefault) {
890                                                                         if (domAttr.IsDefault) {
891                                                                                 methodInfo = null;
892                                                                                 break; //fail due to a conflict
893                                                                         }
894                                                                         else
895                                                                                 continue; //existing matches better
896                                                                 }
897                                                                 else {
898                                                                         methodInfo = null; //we override
899                                                                         hasConflict = !domAttr.IsDefault;
900                                                                 }
901                                                         }
902                                                         else
903                                                                 methodInfo = null; //we override
904                                                 }
905
906                                                 if (methodInfo == null) {
907                                                         methodAttribute = domAttr;
908                                                         methodInfo = me;
909                                                         continue;
910                                                 }
911
912                                                 hasConflict = true;
913                                         }
914                                 }
915
916                                 if (!hasConflict && methodInfo != null)
917                                         return methodInfo;
918                         }
919                         else if (methods.Length == 1) {
920                                 MethodInfo me = methods[0] as MethodInfo;
921                                 if (me != null && me.GetParameters().Length == parameters.Count)
922                                         return me;
923                         }
924                         
925                         throw CreateMethodException (methodName, parameters);
926                 }
927                 
928                 MethodInfo ResolveDataObjectMethod (string methodName, IDictionary values, IDictionary oldValues, out IOrderedDictionary paramValues)
929                 {
930                         MethodInfo method;
931                         if (oldValues != null)
932                                 method = ObjectType.GetMethod (methodName, new Type[] { DataObjectType, DataObjectType });
933                         else
934                                 method = ObjectType.GetMethod (methodName, new Type[] { DataObjectType });
935                         
936                         if (method == null)
937                                 throw new InvalidOperationException ("ObjectDataSource " + owner.ID + " could not find a method named '" + methodName + "' with parameters of type '" + DataObjectType + "' in '" + ObjectType + "'.");
938
939                         paramValues = new OrderedDictionary (StringComparer.InvariantCultureIgnoreCase);
940                         ParameterInfo[] ps = method.GetParameters ();
941                         
942                         if (oldValues != null) {
943                                 if (FormatOldParameter (ps[0].Name) == ps[1].Name) {
944                                         paramValues [ps[0].Name] = CreateDataObject (values);
945                                         paramValues [ps[1].Name] = CreateDataObject (oldValues);
946                                 } else if (FormatOldParameter (ps[1].Name) == ps[0].Name) {
947                                         paramValues [ps[0].Name] = CreateDataObject (oldValues);
948                                         paramValues [ps[1].Name] = CreateDataObject (values);
949                                 } else
950                                         throw new InvalidOperationException ("Method '" + methodName + "' does not have any parameter that fits the value of OldValuesParameterFormatString.");  
951                         } else {
952                                 paramValues [ps[0].Name] = CreateDataObject (values);
953                         }
954                         return method;
955                 }
956                 
957                 Exception CreateMethodException (string methodName, IOrderedDictionary parameters)
958                 {
959                         string s = "";
960                         foreach (string p in parameters.Keys) {
961                                 s += p + ", ";
962                         }
963                         return new InvalidOperationException ("ObjectDataSource " + owner.ID + " could not find a method named '" + methodName + "' with parameters " + s + "in type '" + ObjectType + "'.");
964                 }
965                 
966                 object CreateDataObject (IDictionary values)
967                 {
968                         object ob = Activator.CreateInstance (DataObjectType);
969                         foreach (DictionaryEntry de in values) {
970                                 PropertyInfo p = DataObjectType.GetProperty ((string)de.Key);
971                                 if (p == null) throw new InvalidOperationException ("Property " + de.Key + " not found in type '" +DataObjectType + "'.");
972                                 p.SetValue (ob, ConvertParameter (p.PropertyType, de.Value), null);
973                         }
974                         return ob;
975                 }
976                 
977                 object CreateObjectInstance ()
978                 {
979                         ObjectDataSourceEventArgs args = new ObjectDataSourceEventArgs (null);
980                         OnObjectCreating (args);
981                         
982                         if (args.ObjectInstance != null)
983                                 return args.ObjectInstance;
984                                 
985                         object ob = Activator.CreateInstance (ObjectType);
986                         
987                         args.ObjectInstance = ob;
988                         OnObjectCreated (args);
989                         
990                         return args.ObjectInstance;
991                 }
992                 
993                 void DisposeObjectInstance (object obj)
994                 {
995                         ObjectDataSourceDisposingEventArgs args = new ObjectDataSourceDisposingEventArgs (obj);
996                         OnObjectDisposing (args);
997                         
998                         if (!args.Cancel) {
999                                 IDisposable disp = obj as IDisposable;
1000                                 if (disp != null) disp.Dispose ();
1001                         }
1002                 }
1003
1004                 object FindValueByName (string name, IDictionary values, bool format)
1005                 {
1006                         if (values == null)
1007                                 return null;
1008
1009                         foreach (DictionaryEntry de in values) {
1010                                 string valueName = format == true ? FormatOldParameter (de.Key.ToString ()) : de.Key.ToString ();
1011                                 if (name == valueName)
1012                                         return values [de.Key];
1013                         }
1014                         foreach (DictionaryEntry de in values) {
1015                                 string valueName = format == true ? FormatOldParameter (de.Key.ToString ()) : de.Key.ToString ();
1016                                 valueName = valueName.ToLower ();
1017                                 if (name.ToLower () == valueName)
1018                                         return values [de.Key];
1019                         }
1020                         return null;
1021                 }
1022
1023                 /// <summary>
1024                 /// Merge the current data item fields with view parameter default values
1025                 /// </summary>
1026                 /// <param name="viewParams">default parameters</param>
1027                 /// <param name="values">new parameters for update and insert</param>
1028                 /// <param name="oldValues">old parameters for update and delete</param>
1029                 /// <param name="allwaysAddNewValues">true for insert, as current item is
1030                 /// irrelevant for insert</param>
1031                 /// <returns>merged values</returns>
1032                 IOrderedDictionary MergeParameterValues (ParameterCollection viewParams, IDictionary values, IDictionary oldValues, bool allwaysAddNewValues)
1033                 {
1034                         OrderedDictionary mergedValues = new OrderedDictionary (StringComparer.InvariantCultureIgnoreCase);
1035                         foreach (Parameter p in viewParams) {
1036                                 bool oldAdded = false;
1037                                 if (oldValues != null) {
1038                                         object value = FindValueByName (p.Name, oldValues, true);
1039                                         if (value != null) {
1040                                                 object dataValue = p.ConvertValue (value);
1041                                                 mergedValues [p.Name] = dataValue;
1042                                                 oldAdded = true;
1043                                         }
1044                                 }
1045
1046                                 bool newAdded = false;
1047                                 if (values != null) {
1048                                         object value = FindValueByName (p.Name, values, false);
1049                                         if (value != null) {
1050                                                 object dataValue = p.ConvertValue (value);
1051                                                 mergedValues [p.Name] = dataValue;
1052                                                 newAdded = true;
1053                                         }
1054                                 }
1055                                 if ((!newAdded && !oldAdded) || allwaysAddNewValues) {
1056                                         object val = p.GetValue (context, owner);
1057                                         mergedValues [p.Name] = val;
1058                                 }
1059                         }
1060
1061                         if (values != null) {
1062                                 foreach (DictionaryEntry de in values)
1063                                         if (FindValueByName ((string) de.Key, mergedValues, false) == null)
1064                                                 mergedValues [de.Key] = de.Value;
1065                         }
1066
1067                         if (oldValues != null) {
1068                                 foreach (DictionaryEntry de in oldValues)
1069                                         if (FindValueByName ((string) de.Key, mergedValues, true) == null)
1070                                                 mergedValues [FormatOldParameter ((string) de.Key)] = de.Value;
1071                         }
1072
1073                         return mergedValues;
1074                 }
1075                 
1076                 object[] GetParameterArray (ParameterInfo[] methodParams, IOrderedDictionary viewParams, out ArrayList outParamInfos)
1077                 {
1078                         // FIXME: make this case insensitive
1079
1080                         outParamInfos = null;
1081                         object[] values = new object [methodParams.Length];
1082                         foreach (ParameterInfo mp in methodParams) {
1083                         
1084                                 // Parameter names must match
1085                                 if (!viewParams.Contains (mp.Name)) return null;
1086                                         
1087                                 values [mp.Position] = ConvertParameter (mp.ParameterType, viewParams [mp.Name]);
1088                                 if (mp.ParameterType.IsByRef) {
1089                                         if (outParamInfos == null) outParamInfos = new ArrayList ();
1090                                         outParamInfos.Add (mp);
1091                                 }
1092                         }
1093                         return values;
1094                 }
1095                 
1096                 object ConvertParameter (Type targetType, object value)
1097                 {
1098                         return ConvertParameter (Type.GetTypeCode (targetType), value);
1099                 }
1100                 
1101                 object ConvertParameter (TypeCode targetType, object value)
1102                 {
1103                         if (value == null) {
1104                                 if (targetType != TypeCode.Object && targetType != TypeCode.String)
1105                                         value = 0;
1106                                 else if (ConvertNullToDBNull)
1107                                         return DBNull.Value;
1108                         }
1109                         if (targetType == TypeCode.Object || targetType == TypeCode.Empty)
1110                                 return value;
1111                         else
1112                                 return Convert.ChangeType (value, targetType);
1113                 }
1114                 
1115                 string FormatOldParameter (string name)
1116                 {
1117                         string f = OldValuesParameterFormatString;
1118                         if (f.Length > 0)
1119                                 return String.Format (f, name);
1120                         else
1121                                 return name;
1122                 }
1123                 
1124                 void OnParametersChanged (object sender, EventArgs args)
1125                 {
1126                         OnDataSourceViewChanged (EventArgs.Empty);
1127                 }
1128                 
1129                 protected virtual void LoadViewState (object savedState)
1130                 {
1131                         object[] state = (savedState == null) ? new object [5] : (object[]) savedState;
1132                         ((IStateManager)SelectParameters).LoadViewState (state[0]); 
1133                         ((IStateManager)UpdateParameters).LoadViewState (state[1]); 
1134                         ((IStateManager)DeleteParameters).LoadViewState (state[2]); 
1135                         ((IStateManager)InsertParameters).LoadViewState (state[3]); 
1136                         ((IStateManager)FilterParameters).LoadViewState (state[4]); 
1137                 }
1138
1139                 protected virtual object SaveViewState()
1140                 {
1141                         object[] state = new object [5];
1142                         
1143                         if (selectParameters != null)
1144                                 state [0] = ((IStateManager)selectParameters).SaveViewState ();
1145                         if (updateParameters != null)
1146                                 state [1] = ((IStateManager)updateParameters).SaveViewState ();
1147                         if (deleteParameters != null)
1148                                 state [2] = ((IStateManager)deleteParameters).SaveViewState ();
1149                         if (insertParameters != null)
1150                                 state [3] = ((IStateManager)insertParameters).SaveViewState ();
1151                         if (filterParameters != null)
1152                                 state [4] = ((IStateManager)filterParameters).SaveViewState ();
1153                         
1154                         foreach (object ob in state)
1155                                 if (ob != null) return state;
1156                         
1157                         return null;
1158                 }
1159                 
1160                 protected virtual void TrackViewState()
1161                 {
1162                         isTrackingViewState = true;
1163
1164                         if (selectParameters != null) ((IStateManager)selectParameters).TrackViewState ();
1165                         if (filterParameters != null) ((IStateManager)filterParameters).TrackViewState ();
1166                 }
1167                 
1168                 protected bool IsTrackingViewState
1169                 {
1170                         get { return isTrackingViewState; }
1171                 }
1172                 
1173                 
1174                 bool IStateManager.IsTrackingViewState
1175                 {
1176                         get { return IsTrackingViewState; }
1177                 }
1178                 
1179                 void IStateManager.TrackViewState()
1180                 {
1181                         TrackViewState ();
1182                 }
1183                 
1184                 void IStateManager.LoadViewState (object savedState)
1185                 {
1186                         LoadViewState (savedState);
1187                 }
1188
1189                 object IStateManager.SaveViewState()
1190                 {
1191                         return SaveViewState ();
1192                 }
1193         }
1194 }
1195 #endif
1196
1197
1198
1199