How to: Execute Business Logic During Property Changes (Entity Framework)
The Entity Framework enables you to execute your own custom business logic to perform custom actions when changes are made to generated properties. Entity Data Model (EDM) tools generate data classes that represent entities in an EDM. Although these generated classes should not be modified directly, for each generated property, the tools also generate a pair of partial methods named OnPropertyChanging and OnPropertyChanged, where Property is the property name. These methods are called by Object Services before and after a property is changed, and you can extend these methods in partial data classes to implement custom code. For more information about how to customize generated classes, see Customizing Objects (Entity Framework).
The example in this topic is based on the Adventure Works Sales Model. To run the code in this example, you must have already added the AdventureWorks Sales Model to your project and configured your project to use the Entity Framework. To do this, complete the procedures in How to: Manually Configure an Entity Framework Project and How to: Manually Define an Entity Data Model (Entity Framework).
To implement custom validation for property changes
In your project, define a custom partial class for each data class to validate.
In this partial class, define one or both of the following methods, where Property is the name of the property to validate:
On Property Changing - include code to execute before the change occurs, such as property validation. The value parameter is the value to which the property is changing. Implement this method to validate a property change before it occurs. To prevent the change from being made, you must throw an exception.
On Property Changed - include code to execute after the change occurs, such as logging the change.
Example
This example checks the value of SalesOrderHeader.Status to ensure that the order can be changed before a change is made to SalesOrderDetail.OrderQty and the pending change is logged to a file. This action is performed in the OnOrderQtyChanging partial method. If the change cannot be made, an exception is raised. Then, after the change is made successfully, the SalesOrderHeader.Status value is reset to 1 and the completed change is logged. These actions are performed in the OnOrderQtyChanged partial method.
Partial Public Class SalesOrderDetail
Inherits EntityObject
Private Sub OnOrderQtyChanging(ByVal value As Short)
' Only handle this change for existing SalesOrderHeader
' objects that are attached to an object context. If the item
' is detached then we cannot access or load the related order.
If EntityState <> EntityState.Detached Then
Try
' Ensure that the referenced SalesOrderHeader is loaded.
If Not SalesOrderHeaderReference.IsLoaded Then
SalesOrderHeaderReference.Load()
End If
Dim order As SalesOrderHeader = SalesOrderHeader
' Cancel the change if the order cannot be modified.
If SalesOrderHeader.Status > 3 Then
Throw New ApplicationException("The quantity cannot be changed " _
& "or the item cannot be added because the order has either " _
& "already been shipped or has been cancelled.")
End If
' Log the pending order change.
File.AppendAllText(LogFile, "Quantity of item '" _
& SalesOrderDetailID.ToString() & "' in order '" _
& order.SalesOrderID.ToString() _
& "' changing from '" & OrderQty.ToString() _
& "' to '" & value.ToString() & "'." & Environment.NewLine _
& "Change made by user: " & Environment.UserName _
& Environment.NewLine)
Catch ex As InvalidOperationException
Throw New ApplicationException("The quantity could not be changed " _
& " because the order information could not be retrieved. " _
& "The following error occurred:" & ex.Message)
End Try
End If
End Sub
Private Sub OnOrderQtyChanged()
' Only handle this change for existing SalesOrderHeader
' objects that are attached to an object context.
If EntityState <> EntityState.Detached Then
Try
' Ensure that the SalesOrderDetail is loaded.
If Not SalesOrderHeaderReference.IsLoaded Then
SalesOrderHeaderReference.Load()
End If
' Reset the status for the order related to this item.
SalesOrderHeader.Status = 1
' Log the completed order change.
File.AppendAllText(LogFile, "Quantity of item '" _
& SalesOrderDetailID.ToString() & "' in order '" _
& SalesOrderHeader.SalesOrderID.ToString() _
& "' successfully changed to '" & OrderQty.ToString() _
& "'." & Environment.NewLine _
& "Change made by user: " & Environment.UserName _
& Environment.NewLine)
Catch ex As InvalidOperationException
Throw New ApplicationException("An error occurred " _
& "the data could be in an inconsistent state. " _
& Environment.NewLine & ex.Message)
End Try
End If
End Sub
End Class
public partial class SalesOrderDetail : EntityObject
{
partial void OnOrderQtyChanging(short value)
{
// Only handle this change for existing SalesOrderHeader
// objects that are attached to an object context. If the item
// is detached then we cannot access or load the related order.
if (EntityState != EntityState.Detached)
{
try
{
// Ensure that the referenced SalesOrderHeader is loaded.
if (!this.SalesOrderHeaderReference.IsLoaded)
{
this.SalesOrderHeaderReference.Load();
}
// Cancel the change if the order cannot be modified.
if (this.SalesOrderHeader.Status > 3)
{
throw new ApplicationException("The quantity cannot be changed "
+ "or the item cannot be added because the order has either "
+ "already been shipped or has been cancelled.");
}
// Log the pending order change.
File.AppendAllText(LogFile, "Quantity of item '"
+ this.SalesOrderDetailID.ToString() + "' in order '"
+ this.SalesOrderHeader.SalesOrderID.ToString()
+ "' changing from '" + this.OrderQty.ToString()
+ "' to '" + value.ToString() + "'." + Environment.NewLine
+ "Change made by user: " + Environment.UserName
+ Environment.NewLine);
}
catch (InvalidOperationException ex)
{
throw new ApplicationException("The quantity could not be changed "
+ " because the order information could not be retrieved. "
+ "The following error occurred:" + ex.Message);
}
}
}
partial void OnOrderQtyChanged()
{
// Only handle this change for existing SalesOrderHeader
// objects that are attached to an object context.
if (EntityState != EntityState.Detached)
{
try
{
// Ensure that the SalesOrderDetail is loaded.
if (!SalesOrderHeaderReference.IsLoaded)
{
SalesOrderHeaderReference.Load();
}
// Reset the status for the order related to this item.
this.SalesOrderHeader.Status = 1;
// Log the completed order change.
File.AppendAllText(LogFile, "Quantity of item '"
+ SalesOrderDetailID.ToString() + "' in order '"
+ SalesOrderHeader.SalesOrderID.ToString()
+ "' successfully changed to '" + OrderQty.ToString()
+ "'." + Environment.NewLine
+ "Change made by user: " + Environment.UserName
+ Environment.NewLine);
}
catch (InvalidOperationException ex)
{
throw new ApplicationException("An error occurred; "
+ "the data could be in an inconsistent state. "
+ Environment.NewLine + ex.Message);
}
}
}
}