views:

458

answers:

3

Does anybody have any links or advice on how to hook up validation that requires interacting with the database before updating or adding to the database? Every example I see shows how to validate properties e.g. "Is Required", "Is Email", "Is Numeric", etc, but how do you hook up validation for "Can't order out of stock item"? This xVal blog post touches on it but doesn't provide an example.

I've been following the NerdDinner tutorial which uses a Repository, but this is the bit I don't quite get... Say we had an OrderController with a Create method, and before creating an order we had to first check that the item is in stock. In the NerdDinner style the Controller uses the Repository to talk to the database, so how would our Order object (Model) be able to enforce this validation along with the property validation, as it can't talk to the database?

Thanks for any help

A: 

I would create an OrderService with a method PlaceOrder(Order order). The OrderService use the Repository to perform CRUD ops and to enforce business rules (stock check) and eventually thrown exception on rules violation you can catch and report to the user.

Andrea Balducci
I was hoping to keep this in the context on NerdDinner and not use services at the moment. Would like to keep things as simple as possible to get my head around how it is all hooked up. thanks
atwrok8
+2  A: 

In the NerdDinner tutorial, you can checkout the IsVaild and then the GetRuleViolation methods. Based on your business and database rules, you could use these to check the data you have before you insert it. You could even create an IsValidForInsert Method to check any insert specific rules you need to enforce.

In NerdDinner, the GetRuleViolation allows you to retrieve the violated rules and bubble them up to the interface as you choose.

    public bool IsValid
 {
  get { return (GetRuleViolations().Count() == 0); }
 }

 public IEnumerable<RuleViolation> GetRuleViolations()
 {


  if (CheckDbForViolation)
   yield return new RuleViolation("Database Violation", "SomeField");

  if (String.IsNullOrEmpty(Title))
   yield return new RuleViolation("Title is required", "Title");

  if (String.IsNullOrEmpty(Description))
   yield return new RuleViolation("Description is required", "Description");

  if (String.IsNullOrEmpty(HostedBy))
   yield return new RuleViolation("HostedBy is required", "HostedBy");

 ... etc ...


  yield break;
 }

    public bool CheckDbForViolation()

    {

    /// Do your database work here...

    }

You could take this further and split database code into the repository. The CheckDbForViolation would call the repo for the info and then determine if there was a violation or not. In fact if you are using a repository, I think that would be the preferable way of doing it.

Mitch Baker
I've read about the IsValid and GetRuleViolations but there is no mention of the the scenario "Can't order out of stock item". Where would this type of business rule be placed? thanks
atwrok8
One of your validation steps would be to go out and query the database to see if the item is in stock. Then either continue if you are good or fail and prevent the database write from happening.
Mitch Baker
I understand what the validation step is, but WHERE would the logic go? The Controller uses the Repository, so to query the database this logic would need to be in the Controller, however, I am of the understanding that business rules should remain in the Model? I'm starting to think the Controller shouldn't be using the Repository?! thanks
atwrok8
Sorry that I wasn't more clear on that... If you go into the Models/dinner.cs file you would add validation code that looks into the database as a method call in the GetRuleViolations method. I'd make it a method call just to keep the GetRuleViolations method nice, clean and easy to interpret. Does this make it more clear?
Mitch Baker
A: 

You do not really need any guidance from examples on how to do this. Ultimately you will have to be able to create such applications on your own which means being creative.

I've decided from the beginning do not use either built-in validation or membership API in order not to run into its limitations at some point of time.

For your situation: it's pretty much standard.

Imagine the execution flow as follows:

  1. Post form
  2. Validate input data format without talking to the database
  3. If (2) is pass, then you validate the input from the point of business rules/data integrity. Here you talk to the database
  4. If (3) passed then perform your operation whatever it is. If it somehow fails (maybe data integrity rules in the database prohibit the operation, say, you deleted a related object from the other browser window) then cancel it and notify the user of an operation error.

Try to keep controller methods as empty as possible. The validation and operation logic should reside in your models and business logic. The controller should basically attempt the one intended operation and based on the status returned just return one view or the other. Maybe a few more options, but not the whole load of checks for user roles, access rights, calling some web services etc. Keep it simple.

P.S. I sometimes get the impression that the built-in features intended to simplify simple things for majority of developers tend to create new barriers over the removed ones.

User
The execution flow you've listed is exactly what I would do, I'm just not sure in terms on the NerdDinner example, where the validation would be placed. It would make more sense to me if the Order object used the Repository and then the Controller spoke solely to the Order object. This way the Order object to handle both forms of validation asit can now talk to the database, Would that be a better option do you think? thanks
atwrok8
I'm not that familiar with NerdDinner example. If "Repository" represents some sort of database layer, while there is an intermediate business logic layer (your Order, for example, can be considered a business object), the it is clearly wrong for a controller to use Repository directly for whatever purpose. The communication should be: [Controller] <-> BLL <-> DAL. Sometimes you just use models instead of BLL, but it is not exactly right. Models are there just to pack the data for a view to display.
User