ASP.NET MVC Preview 5 and Form Posting Scenarios
page 6 of 9
by Scott Guthrie
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 39110/ 84

Adding Business Rules to a LINQ to SQL Entity

In the sample above I've been using LINQ to SQL to define my Product entity and perform my data access.  So far, the only level of domain rules/validation that I am using on my Product entity are those inferred by LINQ to SQL from the SQL Server metadata (nulls, data type and length, etc).  This will catch scenarios like above (where we are trying to assign bogus input to a Decimal).  However, they won't be able to model business issues that can't be easily declared using SQL metadata.  For example: disallowing the reorder level of a product to be greater than zero if it has been discontinued, or disallowing a product to be sold for less than what our supplier price is, etc.  For scenarios like these we need to add code to our model to express and integrate these business rules.

The wrong place to add this business rule logic is in the UI layer of our application.  Adding them there is bad for many reasons.  Among others it will almost certainly lead to duplicated code - since you'll end up copying the rules around from UI to UI and from form to form.  In addition to being time-consuming, there is an excellent chance doing so will lead to bugs when you change your business rule logic, and you forget to update it everywhere. 

A much better place to incorporate these business rules is at your model or domain level.  That way they can be used and applied regardless of what type of UI or form or service works with it.  Changes to the rules can be made once, and picked up everywhere without having to duplicate any logic.

There are several patterns and approaches we could take to integrate richer business rules to the Product model object we've been using above: we could define the rules within the object, or external from the object.  We could use declarative rules, a re-usable rules engine framework, or imperative code.  The key point is that ASP.NET MVC allows us to use any or all of these approaches (there aren't a bunch of features that require you to always do it one way - you instead have the flexibility to reflect them however you want, and the MVC features are extensible enough to integrate with almost anything).

For this blog post I'm going to use a relatively simple rules approach.  First I'm going to define a "RuleViolation" class like below that we can use to capture information about a business rule that is being violated within our model.  This class will expose an ErrorMessage string with details about the error, as well as expose the primary property name and property value associated with it that is causing the violation:

(note: For simplicity sake I'm only going to store only one property - in more complex applications this might instead be a list so that multiple properties could be specified).

I will then define an IRuleEntity interface that has a single method - GetRuleViolations() - which returns back a list of all current business rule violations with that entity:

I can then have my Product class implement this interface.  To keep the sample simple I'm embedding the rule definition and evaluation logic inside the method.  There are better patterns that you can use to enable reusable rules, as well as to handle more complex rules. If this sample grew I'd refactor the method so that the rules and their evaluation where defined elsewhere, but for now to keep this simple we'll just evaluate three business rules below like so:


Our application can now query the Product (or any other IRuleEntity) instance to check its current validation status, as well as retrieve back RuleViolation objects that can be used to help present UI that can guide an end-user of the application to help fix them.  It also allows us to easily unit test our business rules independent of the application UI.

For this particular sample I am going to choose to enforce that our Product object is never saved in the database in an invalid state (meaning all RuleViolations must be fixed before the Product object can be saved in the database).  We can do this with LINQ to SQL by adding an OnValidate partial method to the Product partial class.  This method will get called automatically by LINQ to SQL any time database persistence occurs.  Below I'm calling the GetRuleViolations() method we added above, and am raising an exception if there are unresolved errors.  This will abort the transaction and prevent the database from being updated:

And now in addition to having a friendly helper method that allows us to retrieve RuleViolations from a Product, we have enforcement that those RuleViolations must be fixed before our database is ever updated.

View Entire Article

User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 

Community Advice: ASP | SQL | XML | Regular Expressions | Windows

©Copyright 1998-2024  |  Page Processed at 2024-07-12 10:37:27 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search