It is common to want to validate that your entities are ‘valid’ before you save them to the database.
A naive form of validation might be to try to ensure that your entities are ‘valid’ before you add them to the context, but this doesn’t help you if you pass through ‘invalid’ states, i.e. as you build a graph, but much more importantly this naive approach fails completely for update.
What you really need is something to tell you what you are attempting to save when you call SaveChanges(), so you can perform validation at exactly the right time.
How do you do this?
The first thing we need is a Validate() method on our custom Context, that looks through the ObjectStateManager and validates all the entities.
You can do this by adding a Validate method to your context (in a partial class) that looks something like this:
public void Validate()
var stateEntries = ObjectStateManager.GetObjectStateEntries(
.Where(e => e.Entity is IValidingEntity);
foreach (var stateEntry in stateEntries)
var entity = stateEntry.Entity as IValidingEntity;
As you can see this code looks for all ObjectState entries that are Added, Modified or Deleted. We ignore Detached and Unchanged because nothing interesing can happen!
Then if those entities implement IValidatingEntity we call the validate method on the entity passing in the entity state (the validation logic could be different in different states, as you will see in my sample below).
The IValidatingEntity definition is very simple it looks like this:
public interface IValidingEntity
void Validate(EntityState state);
Now all we need to do is make the classes we want validated implement this interface. That’s very easy just add a partial class.
This example provides validation logic for the Account Transfer entity:
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”);
Now you can do you validation on the Context like this:
using (BankingContext ctx = new BankingContext())
… // Random activities
But it would be easy to forget to call Validate() so it would be better if we could get the validation to happen as part of SaveChanges();
Well it turns out that isn’t hard either, all you need to do is override the partial OnContextCreated() method, and in it hook up an event handler for OnSavingChanges() and in there call the Validate() method, like this:
partial void OnContextCreated()
this.SavingChanges += new EventHandler(BankingContext_SavingChanges);
void BankingContext_SavingChanges(object sender, EventArgs e)
And now whenever you call SaveChanges() validation happens automatically.
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!
With things as complicated as validation, there are always Caveats!
So please be aware that if as part of your validation logic you make some additional changes or additions, those new changes won’t be validated. This is because the list of changed entities is ‘established’ before those changes.
Generally this is not a problem though because you would hope your validation logic puts things into valid state 🙂
But please be aware of this limitation.