CSLA.NET 6.0.0
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
CslaDataProvider.cs
Go to the documentation of this file.
1#if !XAMARIN && !WINDOWS_UWP
2//-----------------------------------------------------------------------
3// <copyright file="CslaDataProvider.cs" company="Marimer LLC">
4// Copyright (c) Marimer LLC. All rights reserved.
5// Website: https://cslanet.com
6// </copyright>
7// <summary>Wraps and creates a CSLA .NET-style object </summary>
8//-----------------------------------------------------------------------
9using System;
10using System.Collections;
11using System.Collections.Generic;
12using System.Collections.ObjectModel;
13using System.ComponentModel;
14using System.Windows.Data;
15using System.Reflection;
16using Csla.Reflection;
17using Csla.Properties;
18using System.Threading.Tasks;
19
20namespace Csla.Xaml
21{
26 public class CslaDataProvider : DataSourceProvider
27 {
28
33 {
34 _commandManager = new CslaDataProviderCommandManager(this);
35 _factoryParameters = new ObservableCollection<object>();
36 _factoryParameters.CollectionChanged +=
37 new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_factoryParameters_CollectionChanged);
38 }
39
43 public event EventHandler<Csla.Core.SavedEventArgs> Saved;
52 protected virtual void OnSaved(object newObject, Exception error, object userState)
53 {
54 if (Saved != null)
55 Saved(this, new Csla.Core.SavedEventArgs(newObject, error, userState));
56 }
57
58 void _factoryParameters_CollectionChanged(
59 object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
60 {
61 BeginQuery();
62 }
63
64#region Properties
65
66 private Type _objectType = null;
67 private bool _manageLifetime;
68 private string _factoryMethod = string.Empty;
69 private ObservableCollection<object> _factoryParameters;
70 private bool _isAsynchronous;
71 private CslaDataProviderCommandManager _commandManager;
72 private bool _isBusy;
73
80 {
81 get
82 {
83 return _commandManager;
84 }
85 }
86
91 public Type ObjectType
92 {
93 get
94 {
95 return _objectType;
96 }
97 set
98 {
99 _objectType = value;
100 OnPropertyChanged(new PropertyChangedEventArgs("ObjectType"));
101 }
102 }
103
111 {
112 get
113 {
114 return _manageLifetime;
115 }
116 set
117 {
118 _manageLifetime = value;
119 OnPropertyChanged(new PropertyChangedEventArgs("ManageObjectLifetime"));
120 }
121 }
122
123 private object _dataChangedHandler;
124
134 public object DataChangedHandler
135 {
136 get
137 {
138 return _dataChangedHandler;
139 }
140 set
141 {
142 _dataChangedHandler = value;
143 var dialog = value as IErrorDialog;
144 if (dialog != null)
145 dialog.Register(this);
146 OnPropertyChanged(new PropertyChangedEventArgs("DataChangedHandler"));
147 }
148 }
149
156 public string FactoryMethod
157 {
158 get
159 {
160 return _factoryMethod;
161 }
162 set
163 {
164 _factoryMethod = value;
165 OnPropertyChanged(new PropertyChangedEventArgs("FactoryMethod"));
166 }
167 }
168
173 public IList FactoryParameters
174 {
175 get
176 {
177 return _factoryParameters;
178 }
179 }
180
186 public bool IsAsynchronous
187 {
188 get { return _isAsynchronous; }
189 set { _isAsynchronous = value; }
190 }
191
196 public object ObjectInstance
197 {
198 get { return Data; }
199 set
200 {
201 OnQueryFinished(value, null, null, null);
202 OnPropertyChanged(new PropertyChangedEventArgs("ObjectInstance"));
203 }
204 }
205
209 public bool IsBusy
210 {
211 get { return _isBusy; }
212 protected set
213 {
214 _isBusy = value;
215 OnPropertyChanged(new PropertyChangedEventArgs("IsBusy"));
216 }
217 }
218
223 public void Rebind()
224 {
225 object tmp = ObjectInstance;
226 ObjectInstance = null;
227 ObjectInstance = tmp;
228 }
229
230#endregion
231
232#region Query
233
234 private bool _firstRun = true;
235 private bool _init = false;
236 private bool _endInitCompete = false;
237 private bool _endInitError = false;
238
242 protected override void BeginInit()
243 {
244 _init = true;
245 base.BeginInit();
246 }
247
251 protected override void EndInit()
252 {
253 _init = false;
254 base.EndInit();
255 _endInitCompete = true;
256 }
257
263 protected override void BeginQuery()
264 {
265 if (_init)
266 return;
267
268 if (_firstRun)
269 {
270 _firstRun = false;
271 if (!IsInitialLoadEnabled)
272 return;
273 }
274
275 if (_endInitError)
276 {
277 // this handles a case where the WPF form initilizer
278 // invokes the data provider twice when an exception
279 // occurs - we really don't want to try the query twice
280 // or report the error twice
281 _endInitError = false;
282 OnQueryFinished(null);
283 return;
284 }
285
286 if (this.IsRefreshDeferred)
287 return;
288
289 QueryRequest request = new QueryRequest();
290 request.ObjectType = _objectType;
291 request.FactoryMethod = _factoryMethod;
292 request.FactoryParameters = _factoryParameters;
293 request.ManageObjectLifetime = _manageLifetime;
294
295 IsBusy = true;
296
297 if (IsAsynchronous)
298 System.Threading.ThreadPool.QueueUserWorkItem(DoQuery, request);
299 else
300 DoQuery(request);
301 }
302
309 public void Refresh<T>(Func<T> factory)
310 {
311 T result = default(T);
312 Exception exceptionResult = null;
313
314 // invoke factory method
315 try
316 {
317 result = factory();
318 }
319 catch (Csla.DataPortalException ex)
320 {
321 exceptionResult = ex.BusinessException;
322 }
323 catch (System.Reflection.TargetInvocationException ex)
324 {
325 if (ex.InnerException != null)
326 {
327 exceptionResult = ex.InnerException;
328 var dpe = exceptionResult as Csla.DataPortalException;
329 if (dpe != null && dpe.BusinessException != null)
330 exceptionResult = dpe.BusinessException;
331 }
332 else
333 exceptionResult = ex;
334 }
335 catch (Exception ex)
336 {
337 exceptionResult = ex;
338 }
339
340 if (ManageObjectLifetime && result != null)
341 {
342 Csla.Core.ISupportUndo undo = result as Csla.Core.ISupportUndo;
343 if (undo != null)
344 undo.BeginEdit();
345 }
346
347 if (!_endInitCompete && exceptionResult != null)
348 _endInitError = true;
349
350 // return result to base class
351 OnQueryFinished(result, exceptionResult, (o) => { IsBusy = false; return null; }, null);
352 }
353
361 public async void Refresh<T>(Func<Task<T>> factory)
362 {
363 T result = default(T);
364 Exception exceptionResult = null;
365
366 // invoke factory method
367 try
368 {
369 result = await factory();
370 }
371 catch (Csla.DataPortalException ex)
372 {
373 exceptionResult = ex.BusinessException;
374 }
375 catch (System.Reflection.TargetInvocationException ex)
376 {
377 if (ex.InnerException != null)
378 {
379 exceptionResult = ex.InnerException;
380 var dpe = exceptionResult as Csla.DataPortalException;
381 if (dpe != null && dpe.BusinessException != null)
382 exceptionResult = dpe.BusinessException;
383 }
384 else
385 exceptionResult = ex;
386 }
387 catch (Exception ex)
388 {
389 exceptionResult = ex;
390 }
391
392 if (ManageObjectLifetime && result != null)
393 {
394 Csla.Core.ISupportUndo undo = result as Csla.Core.ISupportUndo;
395 if (undo != null)
396 undo.BeginEdit();
397 }
398
399 if (!_endInitCompete && exceptionResult != null)
400 _endInitError = true;
401
402 // return result to base class
403 OnQueryFinished(result, exceptionResult, (o) => { IsBusy = false; return null; }, null);
404 }
405
406 private void DoQuery(object state)
407 {
408 QueryRequest request = (QueryRequest)state;
409 object result = null;
410 Exception exceptionResult = null;
411 object[] parameters = new List<object>(request.FactoryParameters).ToArray();
412
413 try
414 {
415 // get factory method info
416 BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy;
417 System.Reflection.MethodInfo factory = request.ObjectType.GetMethod(
418 request.FactoryMethod, flags, null,
419 MethodCaller.GetParameterTypes(parameters), null);
420
421 if (factory == null)
422 {
423 // strongly typed factory couldn't be found
424 // so find one with the correct number of
425 // parameters
426 int parameterCount = parameters.Length;
427 System.Reflection.MethodInfo[] methods = request.ObjectType.GetMethods(flags);
428 foreach (System.Reflection.MethodInfo method in methods)
429 if (method.Name == request.FactoryMethod && method.GetParameters().Length == parameterCount)
430 {
431 factory = method;
432 break;
433 }
434 }
435
436 if (factory == null)
437 {
438 // no matching factory could be found
439 // so throw exception
440 throw new InvalidOperationException(
441 string.Format(Resources.NoSuchFactoryMethod, request.FactoryMethod));
442 }
443
444 // invoke factory method
445 try
446 {
447 result = factory.Invoke(null, parameters);
448 }
449 catch (Csla.DataPortalException ex)
450 {
451 exceptionResult = ex.BusinessException;
452 }
453 catch (System.Reflection.TargetInvocationException ex)
454 {
455 if (ex.InnerException != null)
456 {
457 exceptionResult = ex.InnerException;
458 var dpe = exceptionResult as Csla.DataPortalException;
459 if (dpe != null && dpe.BusinessException != null)
460 exceptionResult = dpe.BusinessException;
461 }
462 else
463 exceptionResult = ex;
464 }
465 catch (Exception ex)
466 {
467 exceptionResult = ex;
468 }
469 }
470 catch (Exception ex)
471 {
472 exceptionResult = ex;
473 }
474
475 if (request.ManageObjectLifetime && result != null)
476 {
477 Csla.Core.ISupportUndo undo = result as Csla.Core.ISupportUndo;
478 if (undo != null)
479 undo.BeginEdit();
480 }
481
482 if (!_endInitCompete && exceptionResult != null)
483 _endInitError = true;
484
485 // return result to base class
486 OnQueryFinished(result, exceptionResult, (o) => { IsBusy = false; return null; }, null);
487 }
488
489
490 #region QueryRequest Class
491
492 private class QueryRequest
493 {
494 private Type _objectType;
495
496 public Type ObjectType
497 {
498 get { return _objectType; }
499 set { _objectType = value; }
500 }
501
502 public Func<object[], Task<object>> Factory { get; set; }
503
504 private string _factoryMethod;
505
506 public string FactoryMethod
507 {
508 get { return _factoryMethod; }
509 set { _factoryMethod = value; }
510 }
511
512 private ObservableCollection<object> _factoryParameters;
513
514 public ObservableCollection<object> FactoryParameters
515 {
516 get { return _factoryParameters; }
517 set { _factoryParameters =
518 new ObservableCollection<object>(new List<object>(value)); }
519 }
520 private bool _manageLifetime;
521
522 public bool ManageObjectLifetime
523 {
524 get { return _manageLifetime; }
525 set { _manageLifetime = value; }
526 }
527
528 }
529
530#endregion
531
532#endregion
533
534#region Cancel/Update/New/Remove
535
544 public void Cancel()
545 {
546 Csla.Core.ISupportUndo undo = this.Data as Csla.Core.ISupportUndo;
547 if (undo != null && _manageLifetime)
548 {
549 IsBusy = true;
550 undo.CancelEdit();
551 undo.BeginEdit();
552 IsBusy = false;
553 }
554 }
555
576 public void Save()
577 {
578 // only do something if the object implements
579 // ISavable
580 Csla.Core.ISavable savable = this.Data as Csla.Core.ISavable;
581 if (savable != null)
582 {
583 object result = savable;
584 Exception exceptionResult = null;
585 try
586 {
587 IsBusy = true;
588
589 // clone the object if possible
590 ICloneable clonable = savable as ICloneable;
591 if (clonable != null)
592 savable = (Csla.Core.ISavable)clonable.Clone();
593
594 // apply edits in memory
595 Csla.Core.ISupportUndo undo = savable as Csla.Core.ISupportUndo;
596 if (undo != null && _manageLifetime)
597 undo.ApplyEdit();
598
599
600 // save the clone
601 result = savable.Save();
602
603 if (!ReferenceEquals(savable, this.Data) && !Csla.ApplicationContext.AutoCloneOnUpdate)
604 {
605 // raise Saved event from original object
606 Core.ISavable original = this.Data as Core.ISavable;
607 if (original != null)
608 original.SaveComplete(result);
609 }
610
611 // start editing the resulting object
612 undo = result as Csla.Core.ISupportUndo;
613 if (undo != null && _manageLifetime)
614 undo.BeginEdit();
615 }
616 catch (Exception ex)
617 {
618 exceptionResult = ex;
619 }
620 // clear previous object
621 OnQueryFinished(null, exceptionResult, null, null);
622 // return result to base class
623 OnQueryFinished(result, null, null, null);
624 IsBusy = false;
625 OnSaved(result, exceptionResult, null);
626 }
627 }
628
629
634 public object AddNew()
635 {
636 // only do something if the object implements
637 // IBindingList
638 IBindingList list = this.Data as IBindingList;
639 if (list != null && list.AllowNew)
640 return list.AddNew();
641 else
642 return null;
643
644 }
645
655 public void RemoveItem(object sender, ExecuteEventArgs e)
656 {
657 var item = e.MethodParameter;
658 // only do something if the object implements
659 // IBindingList
660 IBindingList list;
662 if (bb != null)
663 list = bb.Parent as IBindingList;
664 else
665 list = this.Data as IBindingList;
666 if (list != null && list.AllowRemove)
667 list.Remove(item);
668 }
669
670#endregion
671
672 }
673}
674#endif
Provides consistent context information between the client and server DataPortal objects.
static bool AutoCloneOnUpdate
Gets a value indicating whether objects should be automatically cloned by the data portal Update() me...
This is the non-generic base class from which most business objects will be derived.
Core.IParent Parent
Provide access to the parent reference for use in child object code.
Event arguments containing a reference to the new object that was returned as a result of the Save() ...
This exception is returned for any errors occurring during the server-side DataPortal invocation.
Exception BusinessException
Gets the original server-side exception.
A strongly-typed resource class, for looking up localized strings, etc.
static string NoSuchFactoryMethod
Looks up a localized string similar to No such factory method:{0}.
Implements support for RoutedCommands that can be executed by the CslaDataProvider control.
Wraps and creates a CSLA .NET-style object that you can use as a binding source.
void Refresh< T >(Func< T > factory)
Refresh the ObjectInstance by calling the supplied factory.
object AddNew()
Adds a new item to the object if the object implements IBindingList and AllowNew is true.
override void BeginQuery()
Overridden.
object ObjectInstance
Gets or sets a reference to the data object.
bool ManageObjectLifetime
Gets or sets a value indicating whether the data control should manage the lifetime of the business o...
bool IsAsynchronous
Gets or sets a value that indicates whether to perform object creation in a worker thread or in the a...
void Rebind()
Triggers WPF data binding to rebind to the data object.
IList FactoryParameters
Get the list of parameters to pass to the factory method.
override void BeginInit()
Indicates that the control is about to initialize.
Type ObjectType
Gets or sets the type of object to create an instance of.
string FactoryMethod
Gets or sets the name of the static (Shared in Visual Basic) factory method that should be called to ...
CslaDataProviderCommandManager CommandManager
Gets an object that can be used to execute Save and Undo commands on this CslaDataProvider through XA...
void Cancel()
Cancels changes to the business object, returning it to its previous state.
virtual void OnSaved(object newObject, Exception error, object userState)
Raise the Saved event when the object has been saved.
override void EndInit()
Indicates that the control has initialized.
CslaDataProvider()
Creates an instance of the object.
void RemoveItem(object sender, ExecuteEventArgs e)
Removes an item from the list if the object implements IBindingList and AllowRemove is true.
object DataChangedHandler
Gets or sets a reference to an object that will handle the DataChanged event raised by this data prov...
void Save()
Accepts changes to the business object, and commits them by calling the object's Save() method.
EventHandler< Csla.Core.SavedEventArgs > Saved
Event raised when the object has been saved.
bool IsBusy
Gets a value indicating if this object is busy.
Arguments passed to a method invoked by the Execute trigger action.
Specifies that the object can save itself.
Definition: ISavableT.cs:19
Define the common methods used by the UI to interact with n-level undo.
Definition: ISupportUndo.cs:25
void ApplyEdit()
Commits the current edit process.
void BeginEdit()
Starts a nested edit on the object.
Interface defining the interaction between a CslaDataSource and an error dialog control.
Definition: IErrorDialog.cs:21
void Register(object source)
Method called by the CslaDataProvider when the error dialog should register any events it wishes to h...