"user may do X is user owns object Y": Implement logic in Model Validation or Controller logic? - asp.net-mvc-3

Consider, for example's sake, the logic "A user may only edit or delete a comment that the user has authored".
My Controller Actions will repeat the logic of checking whether the currently logged in user can affect the comment. Example
[Authorize]
public ActionResult DeleteComment(int comment_id)
{
var comment = CommentsRepository.getCommentById(comment_id);
if(comment == null)
// Cannot find comment, return bad input
return new HttpStatusCodeResult(400);
if(comment.author != User.Identity.Name)
// User not allowed to delete this comment, return Forbidden
return new HttpStatusCodeResult(403);
// Error checking passed, continue with delete action
return new HttpStatusCodeResult(200);
}
Of course, I can bundle that logic up in a method so that I'm not copy / pasting that snippet; however, taking that code out of the controller and putting it in a ValidationAttribute keeps my Action smaller and easier to write tests for. Example
public class MustBeCommentAuthorAttribute : ValidationAttribute
{
// Import attribute for Dependency Injection
[Import]
ICommentRepository CommentRepository { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
int comment_id = (int)value;
var comment = CommentsRepository.getCommentById(comment_id);
if(comment == null)
return new ValidationResult("No comment with that ID");
if(comment.author != HttpContext.Current.User.Identity.Name)
return new ValidationResult("Cannot edit this comment");
// No errors
return ValidationResult.Success;
}
}
public class DeleteCommentModel
{
[MustBeCommentAuthor]
public int comment_id { get; set; }
}
Is Model Validation the right tool for this job? I like taking that concern out of the controller Action; but in this case, it may complicate things further. This is especially true when you consider that this Action is part of a RESTful API and needs to return a different HTTP Status Code depending on the Validation errors in the ModelState.
Is there "best practice" in this case?

Personally, I think that it looks nice, but you are getting carried away with annotations. I think that this does not belong in your presentation layer and it should be handled by your service layer.
I would have something on the lines of:
[Authorize]
public ActionResult DeleteComment(int comment_id)
{
try
{
var result = CommentsService.GetComment(comment_id, Auth.Username);
// Show success to the user
}
catch(Exception e)
{
// Handle by displaying relevant message to the user
}
}

Related

Can't get TryValidateModel() to work

I have a signup action that attempts checks if the model state is valid, attempts a sign in, if fail revalidates the model. I am unable to revalidate the model using TryValidateModel. I have modified some functions to force the model to be invalid.
Here is my sign up action:
[HttpPost]
public IActionResult SignUp(SignUpModel signUp, string returnURL)
{
if (ModelState.IsValid)
{
if (loginManager.SignUp(signUp)) return Ok("Account created");
TryValidateModel(signUp); // breakpoint 1
}
return View(signUp); // breakpoint 3
}
This is the SignUp() function that forces the invalid state:
public bool SignUp(SignUpModel signUp)
{
signUp.ForceInvalid();
return false;
}
This is the forced function inside SignUpModel
internal void ForceInvalid() => Validation.IsDuplicateEmail = true;
Here is my validation attribute class used for email:
class EmailExistsAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (validationContext.ObjectInstance is SignUpModel signUp && signUp.Validation.IsDuplicateEmail)
new ValidationResult("Email already exists"); // breakpoint 2
return ValidationResult.Success;
}
}
I have used the attribute like this:
[EmailExists]
public string Email { get; set; }
The code breaks at the breakpoints in the order: 1 2 3. Since the code breaks at 2, the model state should be invalid. However at breakpoint 3, the ModelState.IsValid is still true and it has no errors. I have even tried ModelState.Clear() even though it doesn't make sense in this context. The output is still the same.

MVC3 delete action-name all

I have 3 action name delete on 3 different controllers which are on "registration,profile and questions" they all have action delete methods. How can I from my registration-delete method call out profile-delete and questions-delete . That way when a user wants to delete their account all they have to do is go on registration-delete instead of going on registration,profile and questions delete methods. I want in 1 [HttpPost, ActionName("Delete")](registration) to call out the other 2 ActionName("Delete") methods as i prefer people to delete everything in one place is this possible? assuming that each user shares a unique ID that is the same all across. The comment in the code im just using to illustrate any help would be great
registration-delete below
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed()
{
var ss = User.Identity.Name;
var getid = (from s in db.registration where ss == s.email select s.RegistrationID).FirstOrDefault();
registration registration = db.registration.Find(getid);
//This delete's the registration
db.buyers.Remove(registration);
// How can i call-out profile-delete actionname here and questions-delete like
//if (question-delete != null){
// include ActionResult deleteconfirmed("question-delete" }
db.SaveChanges();
return RedirectToAction("logout");
}
If I understand your question, you are asking to call other controller's action method from the currently executing action method? You typically would not do that. The first rule of Controller's action methods is: no action method should be more than 10 lines of code. Basically, an action method is really supposed to be a simple method to collect a view, or call an action in your domain, and return.
In other words, the SRP pattern:
http://codebetter.com/karlseguin/2008/12/05/get-solid-single-responsibility-principle/
Instead, you would organize your Domain-logic (what you describe is considered domain model logic, not controller logic) for this repetitive code such as deleting questions here, but when user is deleted also delete questions, etc.
// an example of IOC injection of your IUserService
private IUserService
public RegistrationController(IUserService userService)
{
_userService = userService;
}
[HttpPost]
public ActionResult Delete()
{
// insert modelstate and/or validation logic
//
if (User.Identity.IsAuthenticated == false)
{
return RedirectToAction("index", "Home");
}
// good practice to never bubble up exceptions to users
try
{
if (_userService.DeleteByEmail(User.Identity.Name) == false)
{
ModalState.AddModelError(String.Empty, "Not allowed?");
return View();
}
// save it all in a single atomic operation here to keep it all in
// a single Transaction Scope that will rollback properly if there is
// an error.
//
db.SaveChanges();
}
catch (Exception)
{
ModalState.AddModelError(String.Empty, "An error occurred...");
return View();
}
// all ok!
return RedirectToAction("logout");
}
Notice how clean this action method is. It just gets down to business with a single line of code or two, and a whole bunch of exit paths to handle the user's experience properly in many different situations.
Now, your domain logic can be encapsulated into a service (or provider, or alike):
namespace MyWebsite.Models
{
public class UserService : IUserService
{
// Todo: convert to some IoC lovin'
//
public Boolean DeleteByEmail(String email)
{
var user =
(from user in db.Registrations
where
user.Email == email
select s).FirstOrDefault();
if (user == null)
return false;
// call into other services to delete them
ProfileDataService.DeleteByUserId(user.UserId);
QuestionsService.DeleteByUserId(user.UserId);
// finally, delete the user
db.Registrations.Delete(user);
// note, I am NOT calling db.SaveChanges() here. Look
// back at the controller method for the atomic operation.
//
return true;
}
}
}
This can be implemented 100s of different ways. The point being is to abstract that logic out to a common code base, or "Domain". I chose to put that logic in your current Website namespace under Models as a shortcut in this example.
As for your other Delete() methods on your other controllers, you would just call into a QuestionsService.DeleteByUserId() and ProfileDataService.DeleteByUserId() to handle those. You can even share those services across the domain, as I showed above.

EF, POCO, DbContext and Validating Deletions

I'm fairly new to the world of MVC and EF, and I've come quite a ways on my own, but one thing I haven't been able to find online is how people validate for "do not delete" conditions.
I'm using EF4.1 database-first POCO classes generated with the DbContext T4 template. In my partial class files I've already decorated all of my classes with the "IValidatableObject" interface that gets called on changes for my business rules that go beyond the standard MetaData attribute type of validations.
What I need now is a validation that works via the same mechanism (and is, therefore, transparent to the UI and the controller) for checking if deletions are OK. My thought was to create an interface like so:
public interface IDeletionValidation
{
DbEntityValidationResult ValidateDeletion(DbEntityValidationResult validationResults);
}
...and then do this in an override to ValidateEntity in the DbContext...
public partial class MyEntityContext
{
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
DbEntityValidationResult val = base.ValidateEntity(entityEntry, items);
if (entityEntry.State == EntityState.Deleted)
{
IDeletionValidation delValidationEntity = entityEntry.Entity as IDeletionValidation;
if (delValidationEntity != null)
val = delValidationEntity.ValidateDeletion(val);
}
return val;
}
...and then I could implement the IDeletionValidation interface on those classes that need to have a validation done before they can be safely deleted.
An example (not working, see caveat in comments) of the ValidateDeletion code would be...
public partial class SalesOrder : IDeletionValidation, IValidatableObject
{
public DbEntityValidationResult ValidateDeletion(DbEntityValidationResult validations)
{
// A paid SalesOrder cannot be deleted, only voided
// NOTE: this code won't work, it's coming from my head and note from the actual source, I forget
// what class I'd need to add to the DbEntityValidationResult collection for this type of validation!
if (PaidAmount != 0)
validations.Add(new ValidationResult("A paid SalesOrder cannot be deleted, only voided"));
return validations;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
// Verify that the exempt reason is filled in if the sales tax flag is blank
if (!IsTaxable && string.IsNullOrEmpty(TaxExemptReason))
validations.Add(new ValidationResult("The Tax Exempt Reason cannot be blank for non-taxable orders"));
return validations;
}
....
}
Am I on the right track? Is there a better way?
Thanks,
CList
EDIT --- Summary of the one-interface method proposed by Pawel (below)
I think the one-interface way presented below and my way above is a little bit of a chocolate vs. vanilla argument in terms of how you want to do it. Performance should be about the same for large numbers of updates / deletes, and you may want to have your delete validation be a separate interface that doesn't apply to all of your validated classes, but if you want all of your validations in one place here it is...
Mod your DBContext
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return entityEntry.Sate == EntityState.Deleted ||
base.ShouldValidateEntity(entityEntry);
}
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var myItems = new Dictionary<object, object>();
myItems.Add("IsDelete", (entityEntry.State == EntityState.Deleted));
// You could also pass the whole context to the validation routines if you need to, which might be helpful if the
// validations need to do additional lookups on other DbSets
// myItems.Add("Context", this);
return base.ValidateEntity(entityEntry, myItems);
}
Put the deletion-validation in your entity's Validate
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
bool isDelete = validationContext.Items.ContainsKey("IsDelete")
? (bool)validationContext.Items["IsDelete"]
: false;
if (isDelete)
{
if (PaidAmount != 0)
validations.Add(new ValidationResult("You cannot delete a paid Sales Order Line", new string[] { "PaidAmount" }));
return validations;
}
// Update / Add validations!!
// Verify that the exempt reason is filled in if the sales tax flag is blank
if (!IsTaxable && string.IsNullOrEmpty(TaxExemptReason))
validations.Add(new ValidationResult("The Tax Exempt Reason cannot be blank for non-taxable orders"));
return validations;
}
...and in the interest of brevity and only putting all of the check-if-delete code in one place, you could even create an extension method on the ValidationContext class (if you're into that sort of thing) like so...
public static class MyExtensions
{
public static bool IsDelete(this System.ComponentModel.DataAnnotations.ValidationContext validationContext)
{
return validationContext.Items.ContainsKey("IsDelete")
? (bool)validationContext.Items["IsDelete"]
: false;
}
}
...which gives us this for our validation code...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validations = new List<ValidationResult>();
if (validationContext.IsDelete())
{
....
I am not really sure why you need a separate interface just for deleted entities. You could pass the entity state (or the EntityEntry object, or the context) to your IValidatableObject.Validate() method by using the items dictionary you pass to the base.ValidateEntity() method. Take a look at the "Custom Validation Sample: Uniqness" section in this blog post http://blogs.msdn.com/b/adonet/archive/2011/05/27/ef-4-1-validation.aspx. This way you could do everything using just one interface - IValidatableObject.
In addition to that - by default EF validates only Added and Modified entities. If you want to validate entities that are in the Deleted state you need to override DbContext.ShouldValidateEntity() method with something like this:
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{
return entityEntry.Sate == EntityState.Deleted ||
base.ShouldValidateEntity(entityEntry);
}

ASP MVC 3 testing controller calling ModelState.IsValid always returns true

I have an ASP MVC 3 application and in my Model I have implemented IValidatableObject.
When my controller posts for a create or edit, I obviously only want to save the model if it is valid.
I see many blogs and posts and answers that say something like
if(!ModelState.IsValid)
{
return View();
}
My question. Why is it that ModelState.IsValid is always true in a unit test on the Controller?
Example:
[Test]
public void InValidModelsAreNotAdded()
{
var invalidModel = new MyModel() { SomeField = "some data", SomeOtherField = "" };
var result = _controller.Submit(invalidModel);
_repository.AssertWasNotCalled(r => r.Add(Arg.Is.Anything));
}
Model code:
public class MyModel : IValidatableObject
{
public string SomeField { get; set; }
public string SomeOtherField { get; set; }
public IEnumerable Validate(ValidationContext validationContext)
{
if(string.IsNullOrWhiteSpace(SomeOtherField))
{
yield return
new ValidationResult("Oops invalid.", new[] {"SomeOtherField"});
}
}
}
The AssertWasNotCalled always fails this test.
I stepped through the test and noticed that the ModelState.IsValid is true for this test. It is as if the IValidatableObject.Validate is not being invoked. It seems to work when I run the project, but thats not much of a way to test drive an application.
Also, I realize I could use the [Required] attribute for my example, but my real code has much more complex validation to it.
Thoughts?
It's true because you haven't called anything which sets it false.
This normally happens during binding, but since you just pass the model directly in the test you skip that altogether.
If you're trying to test validation, do that directly. If you're trying to test the error path in your controller, your test's arrange can call _controller.ModelState.AddModelError( //...
Well, insted of simulate the model binding behavior you can do that:
public class YourController : Controller
{
//some code
public ViewResult someAction(Model model)
{
try
{
ValidateModel(model);
}
catch
{
// deal with errors
}
}
//some code
}
ValidateModel with "try catch" blocks are much more readable for me. But you can still use "if" blocks with the method TryValidateModel
Hope that helps!!

What is the MVC version of this code?

i'm trying to wrap my head around how to enterprise up my code: taking a simple routine and splitting it up into 5 or 6 methods in 3 or 4 classes.
i quickly came up three simple examples of code how i currently write it. Could someone please convert these into an MVC/MVP obfuscated version?
Example 1: The last name is mandatory. Color the text box red if nothing is entered. Color it green if stuff is entered:
private void txtLastname_TextChanged(object sender, EventArgs e)
{
//Lastname mandatory.
//Color pinkish if nothing entered. Greenish if entered.
if (txtLastname.Text.Trim() == "")
{
//Lastname is required, color pinkish
txtLastname.BackColor = ControlBad;
}
else
{
//Lastname entered, remove the coloring
txtLastname.BackColor = ControlGood;
}
}
Example 2: The first name is optional, but try to get it. We'll add a bluish tint to this "try to get" field:
private void txtFirstname_TextChanged(object sender, EventArgs e)
{
//Firstname can be blank.
//Hint them that they should *try* to get it with a bluish color.
//If they do enter stuff: it better be not all spaces.
if (txtFirstname.Text == "")
{
//Nothing there, hint it blue
txtFirstname.BackColor = ControlRequired;
}
else if (txtFirstname.Text.Trim() == "")
{
//They entered spaces - bad user!
txtFirstname.BackColor = ControlBad;
}
else
{
//Entered stuff, remove coloring
txtFirstname.BackColor = SystemColors.Window;
}
}
Example 3 The age is totally optional. If an age is entered, it better be valid:
private void txtAge_TextChanged(object sender, EventArgs e)
{
//Age is optional, but if entered it better be valid
int nAge = 0;
if (Int32.TryParse(txtAge.Text, out nAge))
{
//Valid integer entered
if (nAge < 0)
{
//Negative age? i don't think so
txtAge.BackColor = ControlBad;
}
else
{
//Valid age entered, remove coloring
txtAge.BackColor = SystemColors.Window;
}
}
else
{
//Whatever is in there: it's *not* a valid integer,
if (txtAge.Text == "")
{
//Blank is okay
txtAge.BackColor = SystemColors.Window;
}
else
{
//Not a valid age, bad user
txtAge.BackColor = ControlBad;
}
}
}
Every time i see MVC code, it looks almost like random splitting of code into different methods, classes, and files. i've not been able to determine a reason or pattern to their madness. Without any understanding of they why it's being one some way, it makes no sense. And using the words model, view, controller and presenter, like i'm supposed to know what that means, doesn't help.
The model is your data.
The view shows data on screen.
The controller is used to carry out
the users actions
And oranges taste orangy.
Here's my attempt at splitting things up in order to make the code more difficult to follow. Is this anywhere close to MVC?
private void txtFirstname_TextChanged(object sender, EventArgs e)
{
FirstnameTextChangedHandler(sender, e);
}
private void FirstnameTextChangedHandler(sender, e)
{
string firstname = GetFirstname();
Color firstnameTextBoxColor = GetFirstnameTextBoxColor(firstname);
SetFirstNameTextBoxColor(firstnameTextBoxColor);
}
private string GetFirstname()
{
return txtFirstname.Text;
}
private Color GetFirstnameTextBoxColor(string firstname)
{
//Firstname can be blank.
//Hint them that they should *try* to get it with a bluish color.
//If they do enter stuff: it better be not all spaces.
if (firstname == "")
{
//Nothing there, hint it blue
return GetControlRequiredColor();
}
else if (firstname.Trim() == "")
{
//They entered spaces - bad user!
return GetControlBadColor();
}
else
{
//Entered stuff, remove coloring
return GetControlDefaultColor();
}
}
private Color GetControlRequiredColor()
{
return ControlRequired;
}
private Color GetControlBadColor()
{
return ControlBad;
}
private Color GetControlGoodColor()
{
return ControlGood;
}
//am i doin it rite
i've obfuscated the code, but it's still altogether. The next step in the MVC obfuscation, i gather, is to hide the code in 3 or 4 different files.
It's that next step that i don't understand. What is the logical separation of which functions are moved into what other classes? Can someone translate my 3 simple examples above into full fledged MVC obfuscation?
Edit: Not ASP/ASP.NET/Online. Pretend it's on a desktop, handheld, surface, kiosk. And pretend it's language agnostic.
The purpose of MVC/MVP patterns is not obfuscation, but separation of concerns. Obfuscation is to (conceal the) intended meaning in communication, making communication confusing, intentionally ambiguous, and more difficult to interpret: ref. The use of patterns is to make the code cleaner and more understandable. I suggest you start out by reading the wikipedia entries on MVC and MVP.
Both patterns are ways of structuring your code so that your application is broken up into elements that carry out specific purposes that have clearly defined interaction boundaries. Rather than having code that specifically addresses business concerns, input/output handling, and presentation throughout the various classes of the application, these concerns are separated and isolated in the various architectural components. These architectural elements are insulated from one another by the interaction boundaries (interfaces) making them more independent of one another and easier to modify without affect the application as a whole.
The main idea I have when implementing MVC for Windows Forms is that I want to have unit tests for my model and my controller. In order to achieve that, my controller should not know anything about the views using it, and so any notifications that should be handled on UI level are implemented as events. In your example, my controller would look something like this:
class Controller
{
// This is the model we are operating on
private Model model_;
public enum Status
{
Normal,
Required,
Good,
Bad
}
public delegate void FirstNameStatusChangedDelegate(Status newStatus);
public event FirstNameStatusChangedDelegate FirstNameStatusChangedEvent;
public string FirstName
{
get { return model_.FirstName; }
set
{
if (value == "")
RaiseFirstNameStatusChanged(Status.Required);
else if ( value.Trim() == "" )
RaiseFirstNameStatusChanged(Status.Bad);
else
{
model_.FirstName = value;
RaiseFirstNameStatusChanged(Status.Normal);
}
}
}
private void RaiseFirstNameStatusChanged(Status newStatus)
{
if ( FirstNameStatusChangedEvent != null )
FirstNameStatusChangedEvent(newStatus);
}
}
And the view would provide handlers for the FirstNameStatusChanged event:
class View : Form
{
private Controller controller_;
private static readonly Dictionary<Controller.Status, Color> statusColors_ = new Dictionary<Controller.Status, Color>
{
{Controller.Status.Normal, SystemColors.Window},
{Controller.Status.Required, ControlRequired},
{Controller.Status.Good, ControlGood},
{Controller.Status.Bad, ControlRed}
};
public View(Controller controller)
{
InitializeComponent();
controller_ = controller;
contoller_.FirstNameStatusChangedEvent += OnFirstNameStatusChanged;
}
private void txtFirstname_TextChanged(object sender, EventArgs e)
{ controller_.FirstName = txtFirstName.Text; }
private void OnFirstNameStatusChanged(Controller.Status newStatus)
{ txtFirstName.BackColor = statusColors_[newStatus]; }
}
Most of what you doing in your code belongs to the Controller class since it describes the the logic. Your View should just describe UI and give easy access to UI components. Model class should describe your data model.
The idea is simple: Controller does everything, but it has to know about the View and the Model. For example as View is initialized, Controller sets up all the logic ( kinda what you already doing). As Model is assigned to the Controller - it sets the values into appropriate UI controls and does the same to retrieve data and return is as Model.
So basically you give your data model class to the controller, it does the editing and returns your data as model class again.
It would be very hard to follow MVC in classic ASP.NET if possible, so I will reply based on MVP.
On your first example, you are trying to do a validation. Validating a surname is the responsibility of Presenter. Showing the field red is the responsibility of View. So, your view class would be like this:
private void Page_Load()
{
this._presenter = new Presenter();
}
private void txtLastname_TextChanged(object sender, EventArgs e)
{
txtLastName.BackColor = presenter.IsLastnameValid(txtLastName.Text) ?
ControlGood : ControlBad;
}
And your presenter class would be something like this:
public Presenter()
{
public bool IsLastNameValid(string lastname)
{
return string.IsNullOrEmpty(lastname);
}
}
Last name is your model here.
Please note that I prepared this classes only for showing how would you form an MVP structure. In real world, there are lots of better ways to do validation. Normally you would use this approach for your business instead of validation.
Ian,
If you want the controls to validate immediately, you need to use javascript or jQuery. This is also true for classic ASP.NET. Since you are using Code Behind methods, I assume that your validation waits for a postback.
The following examples are from the NerdDinner project. NerdDinner is an open source project that serves as an example of ASP.NET MVC architecture. The authors have graciously provided a tutorial with it, available at http://nerddinnerbook.s3.amazonaws.com/Intro.htm
When a form is submitted in ASP.NET MVC, it enters the corresponding controller as a FormCollection object:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
try
{
UpdateModel(dinner);
dinnerRepository.Save();
}
catch
{
ModelState.AddModelErrors(dinner.GetRuleViolations())
}
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
UpdateModel takes the form values and attempts to stuff them into the dinner object. The dinner object looks like this:
public partial class Dinner {
public bool IsValid {
get { return (GetRuleViolations().Count() == 0); }
}
public IEnumerable<RuleViolation> GetRuleViolations() {
yield break;
}
public IEnumerable<RuleViolation> GetRuleViolations() {
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");
if (String.IsNullOrEmpty(Address))
yield return new RuleViolation("Address is required", "Address");
if (String.IsNullOrEmpty(Country))
yield return new RuleViolation("Country is required", "Address");
if (String.IsNullOrEmpty(ContactPhone))
yield return new RuleViolation("Phone# is required", "ContactPhone");
if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
yield return new RuleViolation("Phone# does not match country", "ContactPhone");
yield break;
}
partial void OnValidate(ChangeAction action) {
if (!IsValid)
throw new ApplicationException("Rule violations prevent saving");
}
}
Notice the IsValid method and the RuleViolations enumerator. If everything is set up properly, all you have to do is define your validations in here, and ASP.NET MVC will take care of the rest for you.
The final validated result looks like this:
I encourage you to get the NerdDinner application and tutorial at http://nerddinner.codeplex.com/

Resources