EF学习杂记27:如何实现BeforeSave Validation

在实际数据库存取过程中,我们经常要做的事情是想在把对象存入数据库之前进行有效性验证。一种天真的想法是,我们默认的认为要存入数据库中的对象都是合法有效的,但很多情况并非如此,这就需要我们在调用SaveChanges()函数之前,在一个正确的是件,对对象的合法有效性进行核实。

如何做?

首先你需要做的是利用分布类的方式为我们的Context增加一个Validate函数来对所有的实体进行合法性的验证,利用ObjectStateManager:

public void Validate()

        {

            var stateEntries = ObjectStateManager.GetObjectStateEntries(

        EntityState.Added |

                                      EntityState.Modified |

                                      EntityState.Deleted)

                              .Where(e => e.Entity is IValidingEntity);



            foreach (var stateEntry in stateEntries)

            {

                var entity = stateEntry.Entity as IValidingEntity;

                entity.Validate(stateEntry.State);

            }

        }

从上面的代码中,我们可以看出系统寻找出了所有状态为Added,Modified和Deleted的实体对象,同时忽略它所不关心的了Detached和Unchange状态的对象。

然后系统为所有实现了IValidatingEntity 接口的实体对象执行Validate方法,这里要传入实体状态值。(Validate函数可以根据实体的状态不同执行不同的验证逻辑,这一点下面会讲到)IValidatingEntity的定义非常简单,如下面的代码:

public interface IValidingEntity

{

    void Validate(EntityState state);

}



下面需要做的就是为我们需要进行验证的类实现接口函数,方式非常简单,就是在分布类中些相关的验证逻辑。

下面的这个例子为实体 Account Transfer写验证函数:

public partial class AccountTransfer: IValidatingEntity

{

  public void Validate(EntityState state)

  {

    if (state == EntityState.Added)

    {

         if (this.Amount <= 0) 

              throw new InvalidOperationException(“°Transfers must have a non-zero positive value”±);

         if (this.SourceAccount == null)

              throw new InvalidOperationException(“°Transfers require a source account”±);



         if (this.TargetAccount == null)

              throw new InvalidOperationException(“°Transfers require a target account”±);

         if (this.SourceAccount == this.TargetAccount)

              throw new InvalidOperationException(“°Transfers must be between two different accounts”±);

    }

    else if (state == EntityState.Modified)

    {

         throw new InvalidOperationException(“°Modifying a transfer is not allowed, make a correcting transfer instead”±);

    }

    else if (state == EntityState.Deleted)

    {

         throw new InvalidOperationException(“°Deleting a transfer is not allowed, make a correcting transfer instead”±); 

    }

  }

}

现在你可以象下面这样在Context中来对上面的类进行相关的验证操作:

using (BankingContext ctx = new BankingContext())

{

     // Random activities

     ctx.Validate();

     ctx.SaveChanges();

}

但这里很容易忘记调用Validate函数,所以这里我们可以想办法把这个操作直接写入SaveChange函数中,以确保验证被执行。

要实现上面的功能要不算难,你所需要做的就是重写分布方法partial OnContextCreated(),在其中的事件句柄OnSavingChanges()内执行Validate方法:

partial void OnContextCreated()
{
    this.SavingChanges += new EventHandler(BankingContext_SavingChanges);
}

void BankingContext_SavingChanges(object sender, EventArgs e)
{
    Validate();
}

现在,每当你执行方法SaveChanges(),系统都会自动执行Validate方法,这个代码在3.5和4.0中都能很好的执行,尤其在4.0中。

And now whenever you call SaveChanges() validation happens automatically.

Cool huh.

This code works just fine in either 3.5 or 4.0, but you can definitely make it ‘better’ in 4.0.

In fact Danny Simmons demonstrate this at TechEd by customizing a T4 templates so all this code gets built straight into the Context and Entities. He made the Validate method on the Entities call a partial method, so by default calling Validate does nothing, but if you need validation all you need to do is add an implementation of the partial method to a partial class.

You could even write a T4 template for 3.5 that does much the same thing too.

Anyway there are lots of options here, so have fun!

警告:

由于事物的复杂性,这里面会存在一些潜在的问题,主要是:作为验证逻辑的一部分,如果你需要更改其中的部分逻辑,将有部分原有的实体将不符合相关的验证逻辑。淡然了,如果是根据实体的状态来进行验证,这或许问题不大。

你可能感兴趣的:(validation)