Ask a questionAsk a question
 

AnswerWhere to put business rules in EF

  • Monday, October 22, 2007 7:53 AMnicos Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Where do I put business rules for the generated classes in EF ?

     

    One approach is to register for the PropertyChanged event and throw an exception in the case a rule is violated, but the event args for that event gives me the changed property's name only (I would've like to see the old and new values and the changed object - now I have to get to those on my own).

     

    Is that the recommended way ?

    Is that the only way ?

     

    A bigger problem is that if I do put my business rule on the PropertyChanged event, then it is fired everytime the object is populated from the datasource.  Surely this can't be it ?

     

    Tx

     

    Nico Snyman

Answers

  • Monday, October 22, 2007 10:24 PMNoam Ben-Ami - MSFT1MSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    You are right, that isn't it: All of the generated classes, including the context class, are created as partial classes, specifically so you can extend them.
  • Tuesday, October 23, 2007 4:13 AMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    To give more details, in beta 2, while there are partial classes, we don't yet have partial methods for adding business logic directly into the property setters.  In beta 3 the generator will also declare partial methods making this part easier.  Unfortunately there isn't a system in place to allow you to easily distinguish between cases where the system is materializing an entity and user code is calling a setter.  That's something we've talked about adding but currently is unlikely to make v1.

     

    - Danny

     

  • Wednesday, October 31, 2007 11:10 PMJPAK Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I have been using interfaces in the Entities partial classes and apply the businees rules by overriding the Save methods of the context like this:

    Is there any problem with that?

     

    Code Block

    public new int SaveChanges(bool acceptChangesDuringSave)

    {

    DoBeforeSave();

    return base.SaveChanges(acceptChangesDuringSave);

    }

    public new int SaveChanges()

    {

    DoBeforeSave();

    return base.SaveChanges();

    }

    private void DoBeforeSave()

    {

    foreach (ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added))

    {

    if (entry.Entity is ISluggable)

    ((ISluggable)entry.Entity).SetSlug();

    if (entry.Entity is ISettings)

    ((ISettings)entry.Entity).UpdateSettingsInfo();

    if (entry.Entity is IContextEvents)

    {

    if (entry.State == EntityState.Added)

    ((IContextEvents)entry.Entity).BeforeInsert();

    else if (entry.State == EntityState.Modified)

    ((IContextEvents)entry.Entity).BeforeUpdate();

    }

    }

    }

     

     

    Each entity implements the interfaces its own way.

     

    I did not implement any validation rules yet because I would like the process to be aable to infer client side validation as well as server side in the upcoming MVC framework, so I wait to see how both will fit together.

     

     

  • Wednesday, November 07, 2007 7:01 PMMichael Pizzo - MSFTModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    As JPAK says, you can do this by overriding the SaveChanges methods today. Also, in Beta 3 we are adding a SavingChanges event on the ObjectContext.  This event gets fired prior to saving changes and could contain logic similar to what JPAK showed in his example, and would be suitable for cases where an application wants to add logic that fires prior to saving changes but doesn't have control over the classes.

     

All Replies

  • Monday, October 22, 2007 10:24 PMNoam Ben-Ami - MSFT1MSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    You are right, that isn't it: All of the generated classes, including the context class, are created as partial classes, specifically so you can extend them.
  • Tuesday, October 23, 2007 4:13 AMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    To give more details, in beta 2, while there are partial classes, we don't yet have partial methods for adding business logic directly into the property setters.  In beta 3 the generator will also declare partial methods making this part easier.  Unfortunately there isn't a system in place to allow you to easily distinguish between cases where the system is materializing an entity and user code is calling a setter.  That's something we've talked about adding but currently is unlikely to make v1.

     

    - Danny

     

  • Thursday, October 25, 2007 1:51 PMdcd Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    i guess this is a typical situation to use the decorator pattern.

    dcd
  • Thursday, October 25, 2007 7:27 PMNoam Ben-Ami - MSFT1MSFT, ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    While you could certainly use a decorator pattern, the fact that the classes are marked as partial means that it is extremely easy to add behavior to them directly, if so desired. The choice is yours and, of course, depends on the exact nature of the behavior you want to add, and on the application itself.

  • Wednesday, October 31, 2007 10:44 PMEsrever Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    IMHO there may be very many situations where Business rules are not this simple that you can solve it in the Set-accassor.
    (everything is pseudo code and may not compile...)

    class Product
    {
    public StartDate {get;set;}
    public EndDate {get;set;}
    }

    if you Validate on the set accessor you may come in some unlucky Situations because you have everytime to ensure all rules. In Fact, all you want is that the Object is in consistent state before you are saving it.

    I would like to solve that problem with an interface:

    interface ISelfValidator
    {
    BrokenRuleCollection Validate();
    }


    class Product : ISelfValidator
    {
    public StartDate {get;set;}
    public EndDate {get;set;}

    public BrokenRuleCollection Validate()
    {
    BrokenRuleCollection result = new BrokenRuleCollection();

    if ( StartDate > EndDate )
    result.Add(new BrokenRule("The Begin of the Product must be after then the End") );

    return result;
    }
    }


    of course something like this would be even more flexible:

    interface ISelfValidator
    {
    bool HasBrokenRules {get;}
    BrokenRulesCollection BrokenRules { get;}
    event EventHandler BrokenRulesChanged;
    }


  • Wednesday, October 31, 2007 11:10 PMJPAK Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I have been using interfaces in the Entities partial classes and apply the businees rules by overriding the Save methods of the context like this:

    Is there any problem with that?

     

    Code Block

    public new int SaveChanges(bool acceptChangesDuringSave)

    {

    DoBeforeSave();

    return base.SaveChanges(acceptChangesDuringSave);

    }

    public new int SaveChanges()

    {

    DoBeforeSave();

    return base.SaveChanges();

    }

    private void DoBeforeSave()

    {

    foreach (ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added))

    {

    if (entry.Entity is ISluggable)

    ((ISluggable)entry.Entity).SetSlug();

    if (entry.Entity is ISettings)

    ((ISettings)entry.Entity).UpdateSettingsInfo();

    if (entry.Entity is IContextEvents)

    {

    if (entry.State == EntityState.Added)

    ((IContextEvents)entry.Entity).BeforeInsert();

    else if (entry.State == EntityState.Modified)

    ((IContextEvents)entry.Entity).BeforeUpdate();

    }

    }

    }

     

     

    Each entity implements the interfaces its own way.

     

    I did not implement any validation rules yet because I would like the process to be aable to infer client side validation as well as server side in the upcoming MVC framework, so I wait to see how both will fit together.

     

     

  • Friday, November 02, 2007 5:04 AMRelaxin Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Why not create a Entity state enum;

    The enitity state enum could have setting such as Initializing, Loading, Saving, Destroying,etc...

    Then create property wrappers around each state value
    i.e
    bool IsLoading()
    {
      get {return ((EntityState & esLoading) == esLoading)};
    }

    bool IsSaving()
    {
    ...
    }

    .
    .
    .

    Seems like a very simple way to determine if BusinessRules would get fired and it comes in handy in other areas as well.




  • Tuesday, November 06, 2007 9:54 PMEsrever Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    thank you for the info JPAK.

    this is indeed a very good way to solve that problem.

  • Wednesday, November 07, 2007 7:01 PMMichael Pizzo - MSFTModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    As JPAK says, you can do this by overriding the SaveChanges methods today. Also, in Beta 3 we are adding a SavingChanges event on the ObjectContext.  This event gets fired prior to saving changes and could contain logic similar to what JPAK showed in his example, and would be suitable for cases where an application wants to add logic that fires prior to saving changes but doesn't have control over the classes.