762 lines
23 KiB
C#
762 lines
23 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.Serialization;
|
|
using Csla.Properties;
|
|
|
|
namespace Csla
|
|
{
|
|
/// <summary>
|
|
/// This is the base class from which most business collections
|
|
/// or lists will be derived.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the business object being defined.</typeparam>
|
|
/// <typeparam name="C">Type of the child objects contained in the list.</typeparam>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
|
"Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
|
|
[Serializable()]
|
|
public abstract class BusinessListBase<T, C> :
|
|
Core.ExtendedBindingList<C>,
|
|
Core.IEditableCollection, ICloneable, Core.ISavable, Core.IParent
|
|
where T : BusinessListBase<T, C>
|
|
where C : Core.IEditableBusinessObject
|
|
{
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Creates an instance of the object.
|
|
/// </summary>
|
|
protected BusinessListBase()
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Initialize
|
|
|
|
/// <summary>
|
|
/// Override this method to set up event handlers so user
|
|
/// code in a partial class can respond to events raised by
|
|
/// generated code.
|
|
/// </summary>
|
|
protected virtual void Initialize()
|
|
{ /* allows subclass to initialize events before any other activity occurs */ }
|
|
|
|
#endregion
|
|
|
|
#region IsDirty, IsValid
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this object's data has been changed.
|
|
/// </summary>
|
|
public bool IsDirty
|
|
{
|
|
get
|
|
{
|
|
// any non-new deletions make us dirty
|
|
foreach (C item in DeletedList)
|
|
if (!item.IsNew)
|
|
return true;
|
|
|
|
// run through all the child objects
|
|
// and if any are dirty then then
|
|
// collection is dirty
|
|
foreach (C child in this)
|
|
if (child.IsDirty)
|
|
return true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether this object is currently in
|
|
/// a valid state (has no broken validation rules).
|
|
/// </summary>
|
|
public virtual bool IsValid
|
|
{
|
|
get
|
|
{
|
|
// run through all the child objects
|
|
// and if any are invalid then the
|
|
// collection is invalid
|
|
foreach (C child in this)
|
|
if (!child.IsValid)
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Begin/Cancel/ApplyEdit
|
|
|
|
/// <summary>
|
|
/// Starts a nested edit on the object.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// When this method is called the object takes a snapshot of
|
|
/// its current state (the values of its variables). This snapshot
|
|
/// can be restored by calling <see cref="CancelEdit" />
|
|
/// or committed by calling <see cref="ApplyEdit" />.
|
|
/// </para><para>
|
|
/// This is a nested operation. Each call to BeginEdit adds a new
|
|
/// snapshot of the object's state to a stack. You should ensure that
|
|
/// for each call to BeginEdit there is a corresponding call to either
|
|
/// CancelEdit or ApplyEdit to remove that snapshot from the stack.
|
|
/// </para><para>
|
|
/// See Chapters 2 and 3 for details on n-level undo and state stacking.
|
|
/// </para><para>
|
|
/// This method triggers the copying of all child object states.
|
|
/// </para>
|
|
/// </remarks>
|
|
public void BeginEdit()
|
|
{
|
|
if (this.IsChild)
|
|
throw new NotSupportedException(Resources.NoBeginEditChildException);
|
|
|
|
CopyState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancels the current edit process, restoring the object's state to
|
|
/// its previous values.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Calling this method causes the most recently taken snapshot of the
|
|
/// object's state to be restored. This resets the object's values
|
|
/// to the point of the last <see cref="BeginEdit" />
|
|
/// call.
|
|
/// <para>
|
|
/// This method triggers an undo in all child objects.
|
|
/// </para>
|
|
/// </remarks>
|
|
public void CancelEdit()
|
|
{
|
|
if (this.IsChild)
|
|
throw new NotSupportedException(Resources.NoCancelEditChildException);
|
|
|
|
UndoChanges();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Commits the current edit process.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Calling this method causes the most recently taken snapshot of the
|
|
/// object's state to be discarded, thus committing any changes made
|
|
/// to the object's state since the last
|
|
/// <see cref="BeginEdit" /> call.
|
|
/// <para>
|
|
/// This method triggers an <see cref="Core.BusinessBase.ApplyEdit"/>
|
|
/// in all child objects.
|
|
/// </para>
|
|
/// </remarks>
|
|
public void ApplyEdit()
|
|
{
|
|
if (this.IsChild)
|
|
throw new NotSupportedException(Resources.NoApplyEditChildException);
|
|
|
|
AcceptChanges();
|
|
}
|
|
|
|
void Core.IParent.ApplyEditChild(Core.IEditableBusinessObject child)
|
|
{
|
|
EditChildComplete(child);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this method to be notified when a child object's
|
|
/// <see cref="Core.BusinessBase.ApplyEdit" /> method has
|
|
/// completed.
|
|
/// </summary>
|
|
/// <param name="child">The child object that was edited.</param>
|
|
protected virtual void EditChildComplete(Core.IEditableBusinessObject child)
|
|
{
|
|
|
|
// do nothing, we don't really care
|
|
// when a child has its edits applied
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region N-level undo
|
|
|
|
void Core.IUndoableObject.CopyState()
|
|
{
|
|
CopyState();
|
|
}
|
|
|
|
void Core.IUndoableObject.UndoChanges()
|
|
{
|
|
UndoChanges();
|
|
}
|
|
|
|
void Core.IUndoableObject.AcceptChanges()
|
|
{
|
|
AcceptChanges();
|
|
}
|
|
|
|
private void CopyState()
|
|
{
|
|
// we are going a level deeper in editing
|
|
_editLevel += 1;
|
|
|
|
// cascade the call to all child objects
|
|
foreach (C child in this)
|
|
child.CopyState();
|
|
|
|
// cascade the call to all deleted child objects
|
|
foreach (C child in DeletedList)
|
|
child.CopyState();
|
|
}
|
|
|
|
private void UndoChanges()
|
|
{
|
|
C child;
|
|
|
|
// we are coming up one edit level
|
|
_editLevel -= 1;
|
|
if (_editLevel < 0) _editLevel = 0;
|
|
|
|
// Cancel edit on all current items
|
|
for (int index = Count - 1; index >= 0; index--)
|
|
{
|
|
child = this[index];
|
|
child.UndoChanges();
|
|
// if item is below its point of addition, remove
|
|
if (child.EditLevelAdded > _editLevel)
|
|
{
|
|
bool oldAllowRemove = this.AllowRemove;
|
|
try
|
|
{
|
|
this.AllowRemove = true;
|
|
RemoveAt(index);
|
|
}
|
|
finally
|
|
{
|
|
this.AllowRemove = oldAllowRemove;
|
|
}
|
|
}
|
|
}
|
|
|
|
// cancel edit on all deleted items
|
|
for (int index = DeletedList.Count - 1; index >= 0; index--)
|
|
{
|
|
child = DeletedList[index];
|
|
child.UndoChanges();
|
|
if (child.EditLevelAdded > _editLevel)
|
|
{
|
|
// if item is below its point of addition, remove
|
|
DeletedList.RemoveAt(index);
|
|
}
|
|
else
|
|
{
|
|
// if item is no longer deleted move back to main list
|
|
if (!child.IsDeleted) UnDeleteChild(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AcceptChanges()
|
|
{
|
|
// we are coming up one edit level
|
|
_editLevel -= 1;
|
|
if (_editLevel < 0) _editLevel = 0;
|
|
|
|
// cascade the call to all child objects
|
|
foreach (C child in this)
|
|
{
|
|
child.AcceptChanges();
|
|
// if item is below its point of addition, lower point of addition
|
|
if (child.EditLevelAdded > _editLevel) child.EditLevelAdded = _editLevel;
|
|
}
|
|
|
|
// cascade the call to all deleted child objects
|
|
for (int index = DeletedList.Count - 1; index >= 0; index--)
|
|
{
|
|
C child = DeletedList[index];
|
|
child.AcceptChanges();
|
|
// if item is below its point of addition, remove
|
|
if (child.EditLevelAdded > _editLevel)
|
|
DeletedList.RemoveAt(index);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Delete and Undelete child
|
|
|
|
private List<C> _deletedList;
|
|
|
|
/// <summary>
|
|
/// A collection containing all child objects marked
|
|
/// for deletion.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
|
"Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected List<C> DeletedList
|
|
{
|
|
get
|
|
{
|
|
if (_deletedList == null)
|
|
_deletedList = new List<C>();
|
|
return _deletedList;
|
|
}
|
|
}
|
|
|
|
private void DeleteChild(C child)
|
|
{
|
|
// mark the object as deleted
|
|
child.DeleteChild();
|
|
// and add it to the deleted collection for storage
|
|
DeletedList.Add(child);
|
|
}
|
|
|
|
private void UnDeleteChild(C child)
|
|
{
|
|
// we are inserting an _existing_ object so
|
|
// we need to preserve the object's editleveladded value
|
|
// because it will be changed by the normal add process
|
|
int saveLevel = child.EditLevelAdded;
|
|
Add(child);
|
|
child.EditLevelAdded = saveLevel;
|
|
|
|
// since the object is no longer deleted, remove it from
|
|
// the deleted collection
|
|
DeletedList.Remove(child);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns <see langword="true"/> if the internal deleted list
|
|
/// contains the specified child object.
|
|
/// </summary>
|
|
/// <param name="item">Child object to check.</param>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public bool ContainsDeleted(C item)
|
|
{
|
|
return DeletedList.Contains(item);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Insert, Remove, Clear
|
|
|
|
/// <summary>
|
|
/// This method is called by a child object when it
|
|
/// wants to be removed from the collection.
|
|
/// </summary>
|
|
/// <param name="child">The child object to remove.</param>
|
|
void Core.IEditableCollection.RemoveChild(Csla.Core.IEditableBusinessObject child)
|
|
{
|
|
Remove((C)child);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is called by a child object when it
|
|
/// wants to be removed from the collection.
|
|
/// </summary>
|
|
/// <param name="child">The child object to remove.</param>
|
|
void Core.IParent.RemoveChild(Csla.Core.IEditableBusinessObject child)
|
|
{
|
|
Remove((C)child);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the edit level of the child object as it is added.
|
|
/// </summary>
|
|
/// <param name="index">Index of the item to insert.</param>
|
|
/// <param name="item">Item to insert.</param>
|
|
protected override void InsertItem(int index, C item)
|
|
{
|
|
// when an object is inserted we assume it is
|
|
// a new object and so the edit level when it was
|
|
// added must be set
|
|
item.EditLevelAdded = _editLevel;
|
|
item.SetParent(this);
|
|
base.InsertItem(index, item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Marks the child object for deletion and moves it to
|
|
/// the collection of deleted objects.
|
|
/// </summary>
|
|
/// <param name="index">Index of the item to remove.</param>
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
// when an object is 'removed' it is really
|
|
// being deleted, so do the deletion work
|
|
C child = this[index];
|
|
base.RemoveItem(index);
|
|
CopyToDeletedList(child);
|
|
}
|
|
|
|
private void CopyToDeletedList(C child)
|
|
{
|
|
DeleteChild(child);
|
|
INotifyPropertyChanged c = child as INotifyPropertyChanged;
|
|
if (c != null)
|
|
c.PropertyChanged -= new PropertyChangedEventHandler(Child_PropertyChanged);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the collection, moving all active
|
|
/// items to the deleted list.
|
|
/// </summary>
|
|
protected override void ClearItems()
|
|
{
|
|
while (base.Count > 0) RemoveItem(0);
|
|
base.ClearItems();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replaces the item at the specified index with
|
|
/// the specified item, first moving the original
|
|
/// item to the deleted list.
|
|
/// </summary>
|
|
/// <param name="index">The zero-based index of the item to replace.</param>
|
|
/// <param name="item">
|
|
/// The new value for the item at the specified index.
|
|
/// The value can be null for reference types.
|
|
/// </param>
|
|
/// <remarks></remarks>
|
|
protected override void SetItem(int index, C item)
|
|
{
|
|
// copy the original object to the deleted list,
|
|
// marking as deleted, etc.
|
|
C child = default(C);
|
|
if (!ReferenceEquals(this[index], item))
|
|
child = this[index];
|
|
// replace the original object with this new
|
|
// object
|
|
base.SetItem(index, item);
|
|
if (child != null)
|
|
CopyToDeletedList(child);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Edit level tracking
|
|
|
|
// keep track of how many edit levels we have
|
|
private int _editLevel;
|
|
|
|
/// <summary>
|
|
/// Returns the current edit level of the object.
|
|
/// </summary>
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
protected int EditLevel
|
|
{
|
|
get { return _editLevel; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IsChild
|
|
|
|
[NotUndoable()]
|
|
private bool _isChild = false;
|
|
|
|
/// <summary>
|
|
/// Indicates whether this collection object is a child object.
|
|
/// </summary>
|
|
/// <returns>True if this is a child object.</returns>
|
|
protected bool IsChild
|
|
{
|
|
get { return _isChild; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Marks the object as being a child object.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// By default all business objects are 'parent' objects. This means
|
|
/// that they can be directly retrieved and updated into the database.
|
|
/// </para><para>
|
|
/// We often also need child objects. These are objects which are contained
|
|
/// within other objects. For instance, a parent Invoice object will contain
|
|
/// child LineItem objects.
|
|
/// </para><para>
|
|
/// To create a child object, the MarkAsChild method must be called as the
|
|
/// object is created. Please see Chapter 7 for details on the use of the
|
|
/// MarkAsChild method.
|
|
/// </para>
|
|
/// </remarks>
|
|
protected void MarkAsChild()
|
|
{
|
|
_isChild = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICloneable
|
|
|
|
object ICloneable.Clone()
|
|
{
|
|
return GetClone();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a clone of the object.
|
|
/// </summary>
|
|
/// <returns>A new object containing the exact data of the original object.</returns>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected virtual object GetClone()
|
|
{
|
|
return Core.ObjectCloner.Clone(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a clone of the object.
|
|
/// </summary>
|
|
/// <returns>A new object containing the exact data of the original object.</returns>
|
|
public T Clone()
|
|
{
|
|
return (T)GetClone();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cascade Child events
|
|
|
|
private void Child_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
for (int index = 0; index < Count; index++)
|
|
{
|
|
if (ReferenceEquals(this[index], sender))
|
|
{
|
|
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Serialization Notification
|
|
|
|
[OnDeserialized()]
|
|
private void OnDeserializedHandler(StreamingContext context)
|
|
{
|
|
OnDeserialized(context);
|
|
foreach (Core.IEditableBusinessObject child in this)
|
|
{
|
|
child.SetParent(this);
|
|
INotifyPropertyChanged c = child as INotifyPropertyChanged;
|
|
if (c != null)
|
|
c.PropertyChanged += new PropertyChangedEventHandler(Child_PropertyChanged);
|
|
}
|
|
foreach (Core.IEditableBusinessObject child in DeletedList)
|
|
child.SetParent(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is called on a newly deserialized object
|
|
/// after deserialization is complete.
|
|
/// </summary>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected virtual void OnDeserialized(StreamingContext context)
|
|
{
|
|
// do nothing - this is here so a subclass
|
|
// could override if needed
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Data Access
|
|
|
|
/// <summary>
|
|
/// Saves the object to the database.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// Calling this method starts the save operation, causing the all child
|
|
/// objects to be inserted, updated or deleted within the database based on the
|
|
/// each object's current state.
|
|
/// </para><para>
|
|
/// All this is contingent on <see cref="IsDirty" />. If
|
|
/// this value is <see langword="false"/>, no data operation occurs.
|
|
/// It is also contingent on <see cref="IsValid" />. If this value is
|
|
/// <see langword="false"/> an exception will be thrown to
|
|
/// indicate that the UI attempted to save an invalid object.
|
|
/// </para><para>
|
|
/// It is important to note that this method returns a new version of the
|
|
/// business collection that contains any data updated during the save operation.
|
|
/// You MUST update all object references to use this new version of the
|
|
/// business collection in order to have access to the correct object data.
|
|
/// </para><para>
|
|
/// You can override this method to add your own custom behaviors to the save
|
|
/// operation. For instance, you may add some security checks to make sure
|
|
/// the user can save the object. If all security checks pass, you would then
|
|
/// invoke the base Save method via <c>MyBase.Save()</c>.
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <returns>A new object containing the saved values.</returns>
|
|
public virtual T Save()
|
|
{
|
|
T result;
|
|
if (this.IsChild)
|
|
throw new NotSupportedException(Resources.NoSaveChildException);
|
|
|
|
if (_editLevel > 0)
|
|
throw new Validation.ValidationException(Resources.NoSaveEditingException);
|
|
|
|
if (!IsValid)
|
|
throw new Validation.ValidationException(Resources.NoSaveInvalidException);
|
|
|
|
if (IsDirty)
|
|
result = (T)DataPortal.Update(this);
|
|
else
|
|
result = (T)this;
|
|
OnSaved(result);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this method to load a new business object with default
|
|
/// values from the database.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
protected virtual void DataPortal_Create()
|
|
{
|
|
throw new NotSupportedException(Resources.CreateNotSupportedException);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this method to allow retrieval of an existing business
|
|
/// object based on data in the database.
|
|
/// </summary>
|
|
/// <param name="criteria">An object containing criteria values to identify the object.</param>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
protected virtual void DataPortal_Fetch(object criteria)
|
|
{
|
|
throw new NotSupportedException(Resources.FetchNotSupportedException);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this method to allow update of a business
|
|
/// object.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
protected virtual void DataPortal_Update()
|
|
{
|
|
throw new NotSupportedException(Resources.UpdateNotSupportedException);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override this method to allow immediate deletion of a business object.
|
|
/// </summary>
|
|
/// <param name="criteria">An object containing criteria values to identify the object.</param>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
protected virtual void DataPortal_Delete(object criteria)
|
|
{
|
|
throw new NotSupportedException(Resources.DeleteNotSupportedException);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the server-side DataPortal prior to calling the
|
|
/// requested DataPortal_xyz method.
|
|
/// </summary>
|
|
/// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected virtual void DataPortal_OnDataPortalInvoke(DataPortalEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the server-side DataPortal after calling the
|
|
/// requested DataPortal_xyz method.
|
|
/// </summary>
|
|
/// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected virtual void DataPortal_OnDataPortalInvokeComplete(DataPortalEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the server-side DataPortal if an exception
|
|
/// occurs during data access.
|
|
/// </summary>
|
|
/// <param name="e">The DataPortalContext object passed to the DataPortal.</param>
|
|
/// <param name="ex">The Exception thrown during data access.</param>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores", MessageId = "Member")]
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected virtual void DataPortal_OnDataPortalException(DataPortalEventArgs e, Exception ex)
|
|
{
|
|
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ISavable Members
|
|
|
|
object Csla.Core.ISavable.Save()
|
|
{
|
|
return Save();
|
|
}
|
|
|
|
[NonSerialized()]
|
|
[NotUndoable]
|
|
private EventHandler<Csla.Core.SavedEventArgs> _nonSerializableSavedHandlers;
|
|
[NotUndoable]
|
|
private EventHandler<Csla.Core.SavedEventArgs> _serializableSavedHandlers;
|
|
|
|
/// <summary>
|
|
/// Event raised when an object has been saved.
|
|
/// </summary>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design",
|
|
"CA1062:ValidateArgumentsOfPublicMethods")]
|
|
public event EventHandler<Csla.Core.SavedEventArgs> Saved
|
|
{
|
|
add
|
|
{
|
|
if (value.Method.IsPublic &&
|
|
(value.Method.DeclaringType.IsSerializable ||
|
|
value.Method.IsStatic))
|
|
_serializableSavedHandlers = (EventHandler<Csla.Core.SavedEventArgs>)
|
|
System.Delegate.Combine(_serializableSavedHandlers, value);
|
|
else
|
|
_nonSerializableSavedHandlers = (EventHandler<Csla.Core.SavedEventArgs>)
|
|
System.Delegate.Combine(_nonSerializableSavedHandlers, value);
|
|
}
|
|
remove
|
|
{
|
|
if (value.Method.IsPublic &&
|
|
(value.Method.DeclaringType.IsSerializable ||
|
|
value.Method.IsStatic))
|
|
_serializableSavedHandlers = (EventHandler<Csla.Core.SavedEventArgs>)
|
|
System.Delegate.Remove(_serializableSavedHandlers, value);
|
|
else
|
|
_nonSerializableSavedHandlers = (EventHandler<Csla.Core.SavedEventArgs>)
|
|
System.Delegate.Remove(_nonSerializableSavedHandlers, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raises the <see cref="Saved"/> event, indicating that the
|
|
/// object has been saved, and providing a reference
|
|
/// to the new object instance.
|
|
/// </summary>
|
|
/// <param name="newObject">The new object instance.</param>
|
|
[System.ComponentModel.EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
protected void OnSaved(T newObject)
|
|
{
|
|
Csla.Core.SavedEventArgs args = new Csla.Core.SavedEventArgs(newObject);
|
|
if (_nonSerializableSavedHandlers != null)
|
|
_nonSerializableSavedHandlers.Invoke(this, args);
|
|
if (_serializableSavedHandlers != null)
|
|
_serializableSavedHandlers.Invoke(this, args);
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
} |