Validate This EF4 Validation Approach? - c#

I'm very new to EF4. I've posted a couple of times I think regarding inheritance, validation but my overall aim is to reduce the amount of code I write as much as possible. I'm not interested (yet) in POCOs, masses of ObjectContext fiddling: I want the benefit of EF and minimum of coding.
So, the thorny issue of validation. Take a look at this simplified example and (aside from DRY Buddies and dodgy usings aliases), is this looking like a half-decent approach?
namespace Model
{
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using DataAnnotations = System.ComponentModel.DataAnnotations;
using Validation = Microsoft.Practices.EnterpriseLibrary.Validation;
[HasSelfValidation]
[DataAnnotations.MetadataType(typeof(PersonValidator))]
public partial class Person
{
[SelfValidation]
public Validation.ValidationResults Validate()
{
var validationResults = Validation.Validation.Validate(this);
if (string.IsNullOrEmpty(this.LastName) || this.LastName.Length > 4)
{
validationResults.AddResult(new Validation.ValidationResult("This is a test error message for a custom validation error.", this, null, null, null));
}
return validationResults;
}
}
[HasSelfValidation]
public class PersonValidator
{
[NotNullValidator(MessageTemplate = "First Name must be supplied.")]
[ContainsCharactersValidator("Rcd", ContainsCharacters.All, MessageTemplate = "{1} must contains characters \"{3}\" ({4}).")]
[StringLengthValidator(5, 50, MessageTemplate = "{1} (\"{0}\") must be between {3} ({4}) and {5} ({6}) characters in length.")]
public string FirstName { get; set; }
[NotNullValidator(MessageTemplate = "Last Name must be supplied.")]
[ContainsCharactersValidator("Wes", ContainsCharacters.All, MessageTemplate = "{1} must contains characters \"{3}\" ({4}).")]
[StringLengthValidator(5, 50, MessageTemplate = "{1} (\"{0}\") must be between {3} ({4}) and {5} ({6}) characters in length.")]
public string LastName { get; set; }
}
}
There's something rather cool about this. I can call the above like this:
var validationResults = person.Validate();
BUT, if I just want some basic checking, I can strip out Validate(), the [SelfValidation] stuff, keep the attributes and then just call:
var validationResults = Validation.Validate(person);
I only need to include as much validation as I need and there's ZERO configuration in web.config.
How's the cut of my jib? :)
Richard

I'm personally not a fan of calling validation directly in code, and especially not directly on a entity itself. There will be a lot of places were you will call Validate and it is easy to forget to call Validate. Instead, let the ObjectContext invoke the underlying validation framework automatically for ALL entities that have changed, and throw a special exception (that can be caught in the presentation layer) when validation errors occur.
You can do this by hooking onto the ObjectContext.SavingChanges event and trigger validation there. You can write your partial ObjectContext as follows:
public partial class ModelContainer
{
partial void OnContextCreated()
{
this.SavingChanges +=
(sender, e) => Validate(this.GetChangedEntities());
}
private IEnumerable<object> GetChangedEntities()
{
const EntityState AddedAndModified =
EntityState.Added | EntityState.Modified;
var entries = this.ObjectStateManager
.GetObjectStateEntries(AddedAndModified);
return entries.Where(e => e != null);
}
private static void Validate(IEnumerable<object> entities)
{
ValidationResults[] invalidResults = (
from entity in entities
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
select results).ToArray();
if (invalidResults.Length > 0)
throw new ValidationException(invalidResults);
}
}
You can read more about it here.

Related

How to avoid multiple ifelse statements when validating user input? [Web Forms]

I have a very basic sign up page that has textbox fields like 'email','password','username'.
Although I have already used client-side validators (such as asp:RegularExpressionValidator) I also want to have a strong server-side validation. But so far I got this just for a single text field:
if(username.Contains(" ") || string.IsNullOrEmpty(password))
{
//error: invalid username
}
else if (username.length<8)
{
//error: username cannot be shorter than 8 characters
}
else if (username.length>30)
{
//error: username cannot be longer than 30 characters
}
else if (IsUsernameTaken(username))
{
//error: this username has already been taken by someone else
}
else if (something)
{
//some error
}
//etc etc
Is there a better (and more efficient) way to validate my controls without using the above code?
Edit: I am using Asp.net Web Forms
I don't know if you're using MVC, but yes, there is. You don't really want to validate your 'controls', you want to validate the model / viewmodel. When the user submits the form, you should be deserializing the submitted data into your own model/viewmodel class. In your declaration of the class that makes that viewmodel, you can use DataAnnotations to tell the framework the requirements for each field. Something like this:
using System.ComponentModel.DataAnnotations;
using ExpressiveAnnotations.Attributes; // this is a non-standard library
namespace MyProject.Models.ViewModels.Workorder
{
public class CreateBillableUnbillableProjectViewModels
{
public class CreateBillableUnbillableProject : IValidatableObject
{
[Required]
public string Title { get; set; }
[Display(Name = "Proposed Budget")]
[AssertThat("ProposedBudget > 0", ErrorMessage = "You must enter a value greater than 0")]
[Required]
[UIHint("String")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = Settings.DataFormatStringCurrency)]
public decimal ProposedBudget { get; set; }
}
}
}
Note the markup in square brackets. These are DataAnnotations. Additionally, the model/viewmodel as a whole can be given rules that must be valid. For example, if PropertyA must be greater than PropertyB. This is why the class above implements IValidatableObject. Something like this:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
//TODO: any other validation before changing and saving this record?
if (PropertyA <= PropertyB)
yield return new ValidationResult($"PropertyA must be greater than PropertyB", new[] { "PropertyA" });
var db = new MyProjectEntities();
if (db.WorkOrders.Any(i => i.Title == Title))
yield return new ValidationResult($"A WorkOrder with the same title already exists.", new[] { "Title" });
}
All of the above (property-level validation and model-level validation) is triggered when you do a if (ModelState.IsValid) in your controller action.
See:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-6.0

DevForce Validation returning Ok for navigation properties that have not been set

I have been working on validatin for our entities in DevForce and I have managed to get everything I need working aside from validating Navigation Properties.
I have tried placing a RequiredValueVerifier attribute on the property and that does show the validation error on the UI but as soon as I use Manager.VerifierEngine.Execute({entitytovalidate}) the result comes back as Ok.
I know DevForce creates nullos and we can modify what the properties have in said nullos but I would like a way that the VeirifierEngine would return not Ok when we have not updated the value from the nullo.
My current work-around is to have a secondary Int32RangeVerifier on the Id that is used for the FKey but I am not to happy with that as a work-around.
Trying to do this without having to create Verifier Providers just for these properties.
If anyone has a solution to this I would be greatly appreciative if you could share.
Here is a sample of the current work-around:
namespace BearPaw.Models.Main
{
[MetadataType(typeof(TechnicianNoteMetadata))]
public partial class TechnicianNote {
public static TechnicianNote Create(int byUserId, DateTimeZone clientZone, DateTime userUtc)
{
var newItem = new TechnicianNote()
{
CreatedById = byUserId,
CreatedDate = userUtc,
CreatedDateTz = clientZone.Id,
ModifiedById = byUserId,
ModifiedDate = userUtc,
ModifiedDateTz = clientZone.Id
};
return newItem;
}
}
public class TechnicianNoteMetadata
{
[Int32RangeVerifier(ErrorMessage = "Note Category is required", MinValue = 1)]
public static int NoteCategoryId;
[RequiredValueVerifier(DisplayName = "Note Category")]
public static NoteCategory NoteCategory;
[RequiredValueVerifier(DisplayName = "Note Detail")]
public static string NoteDetail;
}
}
Many thanks in advance
You can create a custom verifier to handle navigation property validation, and add it directly to the VerifierEngine with the AddVerifier method if you don't want to use an IVerifierProvider.
For example:
public class NullEntityVerifier : PropertyValueVerifier
{
public NullEntityVerifier(
Type entityType,
string propertyName,
string displayName = null)
: base(new PropertyValueVerifierArgs(entityType, propertyName, true, displayName)) { }
public NullEntityVerifier(PropertyValueVerifierArgs verifierArgs)
: base(verifierArgs) { }
protected override VerifierResult VerifyValue(object itemToVerify, object valueToVerify, TriggerContext triggerContext, VerifierContext verifierContext)
{
var entity = valueToVerify as Entity;
var msg = $"{this.ApplicableType.Name}.{this.DisplayName} is required.";
return new VerifierResult(entity != null && !entity.EntityAspect.IsNullEntity, msg);
}
}
To add to the engine:
var verifier = new NullEntityVerifier(typeof(TechnicianNote), "NoteCategory");
_em1.VerifierEngine.AddVerifier(verifier);
If you want to stick with attributed verifiers you can create a custom attribute for your verifier. See the DevForce Resource Center for more information.

Entity Framework Contextual Validation

How would you implement validation for entity framework entities when different validation logic should be applied in certain situations?
For example, validate the entity in one way if the user is an admin, otherwise validate in a different way.
I put validation attributes on context-specific, dedicated edit models.
The entity has only validations which apply to all entities.
Before I start talking about how to do this with VAB, let me say that you will have to really think your validation rules over. While differentiating validations between roles is possible, it does mean that the object that a user in one roles saves, is invalid for another user. This means that a user in a certain role might need to change that object before it can save it. This can also happen for the same user when it is promoted to another role. If you're sure about doing this, please read on.
This seems like a good job for Enterprise Library's Validation Application Block (VAB), since it allows validation of these complex scenarios. When you want to do this, forget attribute based validation; it simply won't work. You need configuration based validation for this to work.
What you can do using VAB is using a configuration file that holds the actual validation. It depends a bit on what the actual validation rules should be, but what you can do is create a base configuration that always holds for every object in your domain. And next create one or more configurations that contain only the extended validations. Say, for instance, that you've got a validation_base.config file, a validation_manager.config and a validation_admin.config file.
What you can do is merge those validations together depending on the role of the user. Look for instance at this example that creates three configuration sources, based on the configuration file:
var base = new FileConfigurationSource("validation_base.config");
var mngr = new FileConfigurationSource("validation_manager.config");
var admn = new FileConfigurationSource("validation_admin.config");
Now you have to merge these files into (at least) two configurations. One containing the base + manager and the other that contains the base + admin rules. While merging is not something that is supported out of the box, this article will show you how to do it. When using the code in that article, you will be able to do this:
var managerValidations =
new ValidationConfigurationSourceCombiner(base, mngr);
var adminValidations =
new ValidationConfigurationSourceCombiner(base, admn);
The last thing you need to do is to wrap these validations in a class that return the proper set based on the role of the user. You can that like this:
public class RoleConfigurationSource : IConfigurationSource
{
private IConfigurationSource base;
private IConfigurationSource managerValidations;
private IConfigurationSource adminValidations;
public RoleConfigurationSource()
{
this.base = new FileConfigurationSource("validation_base.config");
var mngr = new FileConfigurationSource("validation_manager.config");
var admn = new FileConfigurationSource("validation_admin.config");
managerValidations =
new ValidationConfigurationSourceCombiner(base, mngr);
adminValidations =
new ValidationConfigurationSourceCombiner(base, admn);
}
public ConfigurationSection GetSection(string sectionName)
{
if (sectionName == ValidationSettings.SectionName)
{
if (Roles.UserIsInRole("admin"))
{
return this.adminValidations;
}
else
{
return this.managerValidations;
}
}
return null;
}
#region IConfigurationSource Members
// Rest of the IConfigurationSource members left out.
// Just implement them by throwing an exception from
// their bodies; they are not used.
#endregion
}
Now this RoleConfigurationSource can be created once and you can supply it when you validate your objects, as follows:
static readonly IConfigurationSource validationConfiguration =
new RoleConfigurationSource();
Validator customerValidator =
ValidationFactory.CreateValidator<Customer>(validationConfiguration);
ValidationResults results = customerValidator.Validate(customer);
if (!results.IsValid)
{
throw new InvalidOperationException(results[0].Message);
}
Please note that the Validation Application Block is not an easy framework. It take some time to learn it. When your application is big enough, your specific requirements however, will justify its use. If you choose the VAB, start by reading the "Hands-On Labs" document. If you have problems, come back here at SO ;-)
Good luck.
Until I hear a brighter idea, I'm doing this:
public partial class MyObjectContext
{
ValidationContext ValidationContext { get; set; }
partial void OnContextCreated()
{
SavingChanges += new EventHandler(EntitySavingChanges);
}
private void EntitySavingChanges(object sender, EventArgs e)
{
ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted)
.Where(entry => entry.Entity is IValidatable).ToList().ForEach(entry =>
{
var entity = entry.Entity as IValidatable;
entity.Validate(entry, ValidationContext);
});
}
}
interface IValidatable
{
void Validate(ObjectStateEntry entry, ValidationContext context);
}
public enum ValidationContext
{
Admin,
SomeOtherContext
}
public partial class MyEntity : IValidatable
{
public ValidationContext ValidationContext { get; set; }
public void Validate(ObjectStateEntry entry, ValidationContext context)
{
// this validation doesn't apply to admins
if (context != ValidationContext.Admin)
{
// validation logic here
}
}
}

Is there a better way to handle validation in LINQ to SQL?

Are there any ways, besides throwing exceptions, that one can go about using the partial validation methods in LINQ to SQL to cancel the insert of a record?
I can understand that you don't want to throw an exception directly after a property is set with an invalid value. This approach makes it difficult to communicate correctly to the user what actually is wrong. However, I think it's better to keep away from using those partial validation methods. IMO you want to throw an exception when your model is invalid, but only just before you're persisting your model to the database.
I advise you to use a validation framework and integrate it with your LINQ to SQL DataContext class. Here's an example of how to do this with The Enterprise Library Validation Application Block, but the concept will work for every validation framework you pick:
public partial class NorthwindDataContext
{
public override void SubmitChanges(ConflictMode failureMode)
{
ValidationResult[] = this.Validate();
if (invalidResults.Length > 0)
{
// You should define this exception type
throw new ValidationException(invalidResults);
}
base.SubmitChanges(failureMode);
}
private ValidationResult[] Validate()
{
// here we use the Validation Application Block.
return invalidResults = (
from entity in this.GetChangedEntities()
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
from result in results
select result).ToArray();
}
private IEnumerable<object> GetChangedEntities()
{
ChangeSet changes = this.GetChangeSet();
return changes.Inserts.Concat(changes.Updates);
}
}
[Serializable]
public class ValidationException : Exception
{
public ValidationException(IEnumerable<ValidationResult> results)
: base("There are validation errors.")
{
this.Results = new ReadOnlyCollection<ValidationResult>(
results.ToArray());
}
public ReadOnlyCollection<ValidationResult> Results
{
get; private set;
}
}
There are several validation frameworks available, such as DataAnnotations and
the Enterprise Library Validation Application Block (VAB). VAB is very suited for doing this. With LINQ to SQL your entities are generated, so you'll need to use the configuration based approach that VAB offers (don’t try decorating your entities with attributes). By overriding the SubmitChanges method you can make sure the validation gets triggered just before entities are persisted. My SO answers here and here contain useful information about using VAB.
I've written a few interesting articles about integrating VAB with LINQ to SQL here and here. The nice thing about LINQ to SQL (compared to Entity Framework 1.0) is that a lot of useful metadata is generated. When combining this with VAB you can use this metadata to validate your model, without having to hook up every validation manually. Especially validations as maximum string length and not null can be extracted from the model. Read here how to do this.
VAB to the rescue!
Ultimately this indicates that at you last line of defence (before any database constraints, at least) your data was invalid. If you want to do something other than scream loudly, then perhaps verify the data (via any of a multitude of approaches) before adding it to the insert list.
As an additional thought, you could try overriding SubmitChanges (on the data-context); obtain the change-set, verify the inserts and remove (delete-on-submit, which IIRC checks the insert list and drops them) any that you've decided were mistakes. Then call the base.SubmitChanges. But to me this is a bit backwards.
To illustrate, this only does a single insert (not two as requested), but I don't like this approach. At all. As long as we're clear ;-p
namespace ConsoleApplication1 {
partial class DataClasses1DataContext { // extends the generated data-context
public override void SubmitChanges(
System.Data.Linq.ConflictMode failureMode) {
var delta = GetChangeSet();
foreach (var item in delta.Inserts.OfType<IEntityCheck>()) {
if (!item.IsValid()) {
GetTable(item.GetType()).DeleteOnSubmit(item);
}
}
base.SubmitChanges(failureMode);
}
}
public interface IEntityCheck { // our custom basic validation interface
bool IsValid();
}
partial class SomeTable : IEntityCheck { // extends the generated entity
public bool IsValid() { return this.Val.StartsWith("d"); }
}
static class Program {
static void Main() {
using (var ctx = new DataClasses1DataContext()) {
ctx.Log = Console.Out; // report what it does
ctx.SomeTables.InsertOnSubmit(new SomeTable { Val = "abc" });
ctx.SomeTables.InsertOnSubmit(new SomeTable { Val = "def" });
ctx.SubmitChanges();
}
}
}
}

Using DataAnnotations on Windows Forms project

I recently used ASP.Net MVC with DataAnnotations and was thinking of using the same approach for a Forms project but I'm not sure how to go about it.
I have set my attributes but they do not seem to get checked when I click Save.
UPDATE: I have used Steve Sanderson's approach which will check for attributes on my class and return a collection of errors like so:
try
{
Business b = new Business();
b.Name = "feds";
b.Description = "DFdsS";
b.CategoryID = 1;
b.CountryID = 2;
b.EMail = "SSDF";
var errors = DataAnnotationsValidationRunner.GetErrors(b);
if (errors.Any())
throw new RulesException(errors);
b.Save();
}
catch(Exception ex)
{
}
What do you think of this approach?
Here's a simple example. suppose you have an object like the following
using System.ComponentModel.DataAnnotations;
public class Contact
{
[Required(AllowEmptyStrings = false, ErrorMessage = "First name is required")]
[StringLength(20, MinimumLength = 5, ErrorMessage = "First name must be between 5 and 20 characters")]
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType(DataType.DateTime)]
public DateTime Birthday { get; set; }
}
And suppose we have a method that creates an instance of this class and tries to validate its properties, as listed below
private void DoSomething()
{
Contact contact = new Contact { FirstName = "Armin", LastName = "Zia", Birthday = new DateTime(1988, 04, 20) };
ValidationContext context = new ValidationContext(contact, null, null);
IList<ValidationResult> errors = new List<ValidationResult>();
if (!Validator.TryValidateObject(contact, context, errors,true))
{
foreach (ValidationResult result in errors)
MessageBox.Show(result.ErrorMessage);
}
else
MessageBox.Show("Validated");
}
The DataAnnotations namespace is not tied to the MVC framework so you can use it in different types of applications. the code snippet above returns true, try to update the property values to get validation errors.
And make sure to checkout the reference on MSDN: DataAnnotations Namespace
Steve's example is a bit dated (though still good). The DataAnnotationsValidationRunner that he has can be replaced by the System.ComponentModel.DataAnnotations.Validator class now, it has static methods for validating properties and objects which have been decorated with DataAnnotations attributes.
I found a decent example of using DataAnnotations with WinForms using the Validator class, including tying into the IDataErrorInfo interface so ErrorProvider can display the results.
Here is the link.
DataAnnotations Validation Attributes in Windows Forms
If you use newest versions of Entity Framework you can use this cmd to get a list of your errors if existing:
YourDbContext.Entity(YourEntity).GetValidationResult();

Categories