views:

152

answers:

2

I have the following partial methods in my partial class that is attached to an object in my Liq to SQL model.

The first validation check in each method works fine for checking a valid entry in each. However the second if statement is used to check that the user has set at least one of the fields, but the methods are never actually called when use leaves these fields empty. I have some of my validation in DataAnnotation tags, but I can't use the required attribute because its an "Either or" scenario.

Is there a way to stick with my current validation pattern and get this sort of validation into the model? (Backup plan is to code it in the controller)

partial void OnTelChanging(string value)
        {
            if (!PhoneValidator.IsValidNumber(value))
                _errors.Add("Tel", "Telephone No must be a valid UK phone number.");


            if (String.IsNullOrEmpty(this.Mob) && String.IsNullOrEmpty(this.Tel))
            {
                _errors.Add("Tel", "You must enter at least one contact number.");
                _errors.Add("Mob", "");
            }
        }

        partial void OnMobChanging(string value)
        {
            if (!PhoneValidator.IsValidNumber(value))
                _errors.Add("Mob", "Mobile must be a valid UK phone number.");


            if (String.IsNullOrEmpty(this.Mob) && String.IsNullOrEmpty(this.Tel))
            {
                _errors.Add("Tel", "You must enter at least one contact number.");
                _errors.Add("Mob", "");
            }
        }





 [Bind(Include = "EthnicOriginID,CreatedByUserID,AddressID,CarefirstNumber,Title,Forename,Surname,Dob,Tel,Mob,GuardianName,Notes,Archived")]
    [MetadataType(typeof(CustomerMetaData))]
    public partial class Customer : IDataErrorInfo
    {
        private Dictionary<string, string> _errors = new Dictionary<string, string>();

        partial void OnTelChanging(string value)
        {
            if (!String.IsNullOrEmpty(value) && !PhoneValidator.IsValidNumber(value))
                _errors.Add("Tel", "Telephone No must be a valid UK phone number.");


            if (String.IsNullOrEmpty(this.Mob) && String.IsNullOrEmpty(this.Tel))
            {
                _errors.Add("Tel", "You must enter at least one contact number.");
                _errors.Add("Mob", "");
            }
        }

        partial void OnMobChanging(string value)
        {
            if (!String.IsNullOrEmpty(value) && !PhoneValidator.IsValidNumber(value))
                _errors.Add("Mob", "Mobile must be a valid UK phone number.");


            if (String.IsNullOrEmpty(this.Mob) && String.IsNullOrEmpty(this.Tel))
            {
                _errors.Add("Tel", "You must enter at least one contact number.");
                _errors.Add("Mob", "");
            }
        }

        #region ToString Override

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.Surname + ", " + this.Forename;
        }

        #endregion ToString Override

        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                return string.Empty;
            }
        }

        public string this[string columnName]
        {
            get
            {
                if (_errors.ContainsKey(columnName))
                    return _errors[columnName];
                return string.Empty;
            }
        }

        #endregion
    }

    public class CustomerMetaData
    {
        #region EthnicOriginID

        [DisplayName("Ethnic Origin")]
        public int EthnicOriginID { get; set; }

        #endregion EthnicOriginID

        #region CreatedByUserID

        [DisplayName("Created By User")]
        [Required]
        public int CreatedByUserID { get; set; }

        #endregion CreatedByUserID

        #region AddressID

        [DisplayName("Address")]
        public int AddressID { get; set; }

        #endregion AddressID

        #region CarefirstNumber

        [DataType(DataType.Text)]
        [DisplayName("Carefirst Number")]
        [StringLength(8)]
        public string CarefirstNumber { get; set; }

        #endregion CarefirstNumber

        #region Title

        [DataType(DataType.Text)]
        [DisplayName("Title")]
        [StringLength(10)]
        public string Title { get; set; }

        #endregion Title

        #region Forename

        [DataType(DataType.Text)]
        [DisplayName("Forename")]
        [StringLength(25)]
        [Required]
        public string Forename { get; set; }

        #endregion Forename

        #region Surname

        [DataType(DataType.Text)]
        [DisplayName("Surname")]
        [StringLength(25)]
        [Required]
        public string Surname { get; set; }

        #endregion Surname

        #region Dob

        [DataType(DataType.DateTime)]
        [DisplayName("Dob")]
        public DateTime Dob { get; set; }

        #endregion Dob

        #region Tel

        [DataType(DataType.Text)]
        [DisplayName("Telephone No.")]
        [StringLength(11)]
        public string Tel { get; set; }

        #endregion Tel

        #region Mob

        [DataType(DataType.Text)]
        [DisplayName("Mobile")]
        [StringLength(11)]
        public string Mob { get; set; }

        #endregion Mob

        #region GuardianName

        [DataType(DataType.Text)]
        [DisplayName("Guardian Name")]
        [StringLength(50)]
        public string GuardianName { get; set; }

        #endregion GuardianName

        #region Notes

        [DataType(DataType.Text)]
        [DisplayName("Notes")]
        [StringLength(300)]
        public string Notes { get; set; }

        #endregion Notes
    }
+2  A: 

Implement IDataErrorInfo. This will do what you want, and MVC picks it up automatically.

Craig Stuntz
Craig, I think Chris may already be implementing this. The issue is that the events don't fire because there is no change to the data and hence the methods never run.
Fermin
If he is binding at all, then MVC will run IDataErrorInfo.
Craig Stuntz
Hi Craig, i am doing as you suggest. The events don't seem to run if I leave the fields blank. I've put my actual class file in the original q.
littlechris
Are you binding and then checking ModelState.IsValid? There will not be an exception.
Craig Stuntz
My Controller Action:if (!ModelState.IsValid) return View(customer); this._customerRepository.Add(customer); this._customerRepository.Save(); return RedirectToAction("Details", new { id = customer.CustomerID });
littlechris
This pretty standard code i thought. I may be missing sometihng here, but I still think the problem lys with the fact that the OnFieldChagin method is not called as I don't actaully change the value of the field anywhere. It will run if I enter a value
littlechris
I've just re-examined the MVC source code, and indeed the DefaultModelBinder *always*looks at IDataErrorInfo when binding to a complex (object with properties) model, regardless of whether any individual property was changed. So if this is not running for you, then either you are not using the default binder or something else not shown in your question is going wrong.
Craig Stuntz
I don't think I've been clear in my post. My issue is not with the error being passed back using IDataERrorIfon, but the fact that the Linq to sql never calls the OnChaning event because the value never actually changes. It seems that I need somewhere else in my model code to run this sort of validation, I guess that what I should re-post. I appreciate your time on this and hopefully I'll get a solution soon! :)
littlechris
+1  A: 

The onChange event for these properties will never fire. You should check out this tutorial from ASP.NET on validating with a service layer.

You can use the service layer in conjunction with Data Annotation for any complex validation rules.

Fermin