如何:在保存更改时执行业务逻辑(实体框架)

使用 实体框架 可以在将更改保存到数据库之前执行自定义业务逻辑。 SavingChanges 事件在处理 SaveChanges 操作之前引发。 处理该事件可以在将更改保存到数据库之前实现自定义业务逻辑。 从 .NET Framework 版本 4 开始,SaveChanges 方法为 virtual。 这意味着您可以直接重写此方法而不是订阅 SavingChanges 事件。

本主题中的示例演示如何重写 SaveChanges 方法,并处理 SavingChanges 事件,以便在将这些更改永久保存到数据库之前验证对象上下文中已更改的对象。

本主题中的示例基于 Adventure Works 销售模型。 若要运行本示例中的代码,必须已将 AdventureWorks 销售模型添加到您的项目中,并将项目配置为使用 实体框架 。 为此,请完成如何:手动配置实体框架项目如何:手动定义模型和映射文件(实体框架)中的过程。

第一个示例演示如何在自定义对象上下文类中重写 SaveChanges 方法。 第二个示例演示如何使用 OnContextCreatedObjectContext 的实例中的 SavingChanges 事件注册处理程序。 第三个示例演示如何在从 ObjectContext 派生的代理类中处理此事件。 第四个示例演示的代码使用第二个示例中的代理类对对象进行更改,然后调用 SaveChanges

示例

在本示例中,将重写 SaveChanges 方法以帮助您验证具有 AddedModifiedEntityState 的对象。 此示例基于如何:定义自定义对象上下文(实体框架)中定义的自定义对象上下文。

Public Overloads Overrides Function SaveChanges(ByVal options As SaveOptions) As Integer

    For Each entry As ObjectStateEntry In ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
        ' Validate the objects in the Added and Modified state 
        ' if the validation fails throw an exeption. 
    Next
    Return MyBase.SaveChanges(options)
End Function
public override int SaveChanges(SaveOptions options)
{

    foreach (ObjectStateEntry entry in
        ObjectStateManager.GetObjectStateEntries(
        EntityState.Added | EntityState.Modified))
    {
        // Validate the objects in the Added and Modified state
        // if the validation fails throw an exeption.
    }
    return base.SaveChanges(options);
}

在此示例中,OnContextCreated 方法定义为 AdventureWorksEntities 的分部方法。 SavingChanges 事件的处理程序在此分部方法中进行定义。 该事件处理程序验证在可以持久保存更改之前,调用代码是否未在 SalesOrderHeader.Comment 属性中添加任何不适当的文本。 如果字符串校验算法(未显示)发现任何问题,则引发异常。

Partial Public Class AdventureWorksEntities
    Private Sub OnContextCreated()
        ' Register the handler for the SavingChanges event. 
        AddHandler Me.SavingChanges, AddressOf context_SavingChanges
    End Sub
    ' SavingChanges event handler. 
    Private Shared Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
        ' Validate the state of each entity in the context 
        ' before SaveChanges can succeed. 
        For Each entry As ObjectStateEntry In DirectCast(sender, ObjectContext).ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
            ' Find an object state entry for a SalesOrderHeader object. 
            If Not entry.IsRelationship AndAlso (entry.Entity.GetType() Is GetType(SalesOrderHeader)) Then
                Dim orderToCheck As SalesOrderHeader = TryCast(entry.Entity, SalesOrderHeader)

                ' Call a helper method that performs string checking 
                ' on the Comment property. 
                Dim textNotAllowed As String = Validator.CheckStringForLanguage(orderToCheck.Comment)

                ' If the validation method returns a problem string, raise an error. 
                If textNotAllowed <> String.Empty Then
                    Throw New ArgumentException(String.Format("Changes cannot be " & _
                                                                "saved because the {0} '{1}' object contains a " & _
                                                                "string that is not allowed in the property '{2}'.", _
                                                                entry.State, "SalesOrderHeader", "Comment"))
                End If
            End If
        Next
    End Sub
End Class
public partial class AdventureWorksEntities
{
    partial void OnContextCreated()
    {
        // Register the handler for the SavingChanges event.
        this.SavingChanges
            += new EventHandler(context_SavingChanges);
    }
    // SavingChanges event handler.
    private static void context_SavingChanges(object sender, EventArgs e)
    {
        // Validate the state of each entity in the context
        // before SaveChanges can succeed.
        foreach (ObjectStateEntry entry in
            ((ObjectContext)sender).ObjectStateManager.GetObjectStateEntries(
            EntityState.Added | EntityState.Modified))
        {
            // Find an object state entry for a SalesOrderHeader object. 
            if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(SalesOrderHeader)))
            {
                SalesOrderHeader orderToCheck = entry.Entity as SalesOrderHeader;

                // Call a helper method that performs string checking 
                // on the Comment property.
                string textNotAllowed = Validator.CheckStringForLanguage(
                    orderToCheck.Comment);

                // If the validation method returns a problem string, raise an error.
                if (textNotAllowed != string.Empty)
                {
                    throw new ArgumentException(String.Format("Changes cannot be "
                        + "saved because the {0} '{1}' object contains a "
                        + "string that is not allowed in the property '{2}'.",
                        entry.State, "SalesOrderHeader", "Comment"));
                }
            }
        }
    }
}

在本示例中,AdventureWorksProxy 类的实例用于更改 SalesOrderHeader 对象的 Comment 属性。 在 AdventureWorksProxy 类提供的 ObjectContext 实例上调用 SaveChanges 时,将运行上一示例中的验证代码。

' Create an instance of the proxy class that returns an object context. 
Dim context As New AdventureWorksProxy()
' Get the first order from the context. 
Dim order As SalesOrderHeader = context.Context.SalesOrderHeaders.First()

' Add some text that we want to catch before saving changes. 
order.Comment = "some text"

Try
    ' Save changes using the proxy class. 
    Dim changes As Integer = context.Context.SaveChanges()
Catch ex As InvalidOperationException
    ' Handle the exception returned by the proxy class 
    ' validation if a problem string is found. 
    Console.WriteLine(ex.ToString())
// Create an instance of the proxy class that returns an object context.
AdventureWorksProxy context = new AdventureWorksProxy();
// Get the first order from the context.
SalesOrderHeader order =
    context.Context.SalesOrderHeaders.First();

// Add some text that we want to catch before saving changes.
order.Comment = "some text";

try
{
    // Save changes using the proxy class.
    int changes = context.Context.SaveChanges();
}
catch (InvalidOperationException ex)
{
    // Handle the exception returned by the proxy class
    // validation if a problem string is found.
    Console.WriteLine(ex.ToString());
}

此示例在从 ObjectContext 派生的代理类中对对象进行更改,然后调用 SaveChanges。 此示例用于调用在上一个示例中演示的事件处理。

Public Class AdventureWorksProxy
    ' Define the object context to be provided. 
    Private contextProxy As New AdventureWorksEntities()

    Public Sub New()
        ' When the object is initialized, register the 
        ' handler for the SavingChanges event. 
        AddHandler contextProxy.SavingChanges, AddressOf context_SavingChanges
    End Sub

    ' Method that provides an object context. 
    Public ReadOnly Property Context() As AdventureWorksEntities
        Get
            Return contextProxy
        End Get
    End Property

    ' SavingChanges event handler. 
    Private Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
        ' Ensure that we are passed an ObjectContext 
        Dim context As ObjectContext = TryCast(sender, ObjectContext)
        If context IsNot Nothing Then

            ' Validate the state of each entity in the context 
            ' before SaveChanges can succeed. 
            For Each entry As ObjectStateEntry In context.ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
                ' Find an object state entry for a SalesOrderHeader object. 
                If Not entry.IsRelationship AndAlso (entry.Entity.GetType() Is GetType(SalesOrderHeader)) Then
                    Dim orderToCheck As SalesOrderHeader = TryCast(entry.Entity, SalesOrderHeader)

                    ' Call a helper method that performs string checking 
                    ' on the Comment property. 
                    Dim textNotAllowed As String = Validator.CheckStringForLanguage(orderToCheck.Comment)

                    ' If the validation method returns a problem string, raise an error. 
                    If textNotAllowed <> String.Empty Then
                        Throw New ArgumentException(String.Format("Changes cannot be " & _
                                                                    "saved because the {0} '{1}' object contains a " & _
                                                                    "string that is not allowed in the property '{2}'.", _
                                                                    entry.State, "SalesOrderHeader", "Comment"))
                    End If
                End If
            Next
        End If
    End Sub
End Class
public class AdventureWorksProxy
{
    // Define the object context to be provided.
    private AdventureWorksEntities contextProxy =
        new AdventureWorksEntities();

    public AdventureWorksProxy()
    {
        // When the object is initialized, register the 
        // handler for the SavingChanges event.
        contextProxy.SavingChanges
            += new EventHandler(context_SavingChanges);
    }

    // Method that provides an object context.
    public AdventureWorksEntities Context
    {
        get
        {
            return contextProxy;
        }
    }

    // SavingChanges event handler.
    private void context_SavingChanges(object sender, EventArgs e)
    {
        // Ensure that we are passed an ObjectContext
        ObjectContext context = sender as ObjectContext;
        if (context != null)
        {

            // Validate the state of each entity in the context
            // before SaveChanges can succeed.
            foreach (ObjectStateEntry entry in
                context.ObjectStateManager.GetObjectStateEntries(
                EntityState.Added | EntityState.Modified))
            {
                // Find an object state entry for a SalesOrderHeader object. 
                if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(SalesOrderHeader)))
                {
                    SalesOrderHeader orderToCheck = entry.Entity as SalesOrderHeader;

                    // Call a helper method that performs string checking 
                    // on the Comment property.
                    string textNotAllowed = Validator.CheckStringForLanguage(
                        orderToCheck.Comment);

                    // If the validation method returns a problem string, raise an error.
                    if (textNotAllowed != string.Empty)
                    {
                        throw new ArgumentException(String.Format("Changes cannot be "
                            + "saved because the {0} '{1}' object contains a "
                            + "string that is not allowed in the property '{2}'.",
                            entry.State, "SalesOrderHeader", "Comment"));
                    }
                }
            }
        }
    }
}

另请参见

任务

如何:当对象状态发生更改时执行业务逻辑
如何:在标量属性更改过程中执行业务逻辑(实体框架)
如何:在关联更改过程中执行业务逻辑

概念

使用对象(实体框架)