Asked by:
Re: Dynamic Data EF 5 ( DbContext ) - How to configure Custom Validation ?

Question
-
User343140345 posted
Hi,
How and where do I code Entity Validation logic when I have DbContext and simple classes with DD controls and EntityDataSource ?
It seems that DD controls need to use ObjectContext but I don't know where I can put custom Entity validation code when I have DbContext classes.
VS 2012, EF 5, DD 4, C# , Sql Server 2008 , EF Database First edmx with T4 templates
Global.asax - We register an ObjectContext ( mapped from the generated DBContext )
// E N T I T Y F R A M E W O R K - remap DbContext to ObjectContext
DefaultModel.RegisterContext(() =>
{ return ((IObjectContextAdapter)new NVTracEntities()).ObjectContext; },
new ContextConfiguration() { ScaffoldAllTables = true } );
The pagetemplate Edit.ascx uses the EntityDataSource control which uses the ObjectContext class.
But when saving the events in the DbContext don’t fire and the Validate event in the entity partial class doesn’t fire also.
[MetadataType(typeof(ProvidersNewMetadata))]
public partial class ProvidersNew : IValidatableObject
{
private IList<ValidationResult> validationErrors = new List<ValidationResult>();
// ??? D O E S N ' T F I R E on Save ?????
IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
{
this.validationErrors.Clear();
if (this.ProviderName == "xxx") // test only
{
this.validationErrors.Add(new ValidationResult("Invalid Provider Name"));
}
return this.validationErrors;
}
// DbContext Base Class doesn't have this stuff ! like ObjectContext in Linq To Sql
//partial void OnContextCreated()
//{
// SavingChanges += OnSavingChanges;
//}
//public void OnSavingChanges(object sender, EventArgs e)
//{
// var stateManager = ((ProvidersNew)sender).ObjectStateManager;
// var changedEntities = stateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added);
// foreach (var entry in changedEntities)
// {
// if (entry.Entity is Product)
// {
// Product prod = (Product)entry.Entity;
// if (prod.InStock && prod.Amount <= 0)
// {
// throw new ValidationException("Product cannot be in stock when the amount is zero or ess");
// }
// }
// }
//}
internal class ProvidersNewMetadata
{
public int ProviderID { get; set; }
public string ProviderName { get; set; }
public int RegionID { get; set; }
public int FundID { get; set; }
. . .
} }
*** I also tried overridng the ValidateEntity method in DBContext but they DON'T FIRE
public partial class NVTracEntities : DbContext {
protected override bool ShouldValidateEntity(DbEntityEntry entityEntry)
{ if ( entityEntry.Entity is ProvidersNew) return true;
return base.ShouldValidateEntity(entityEntry); }
protected override DbEntityValidationResult ValidateEntity ( DbEntityEntry entityEntry, IDictionary<object, object> items)
{ var result = base.ValidateEntity(entityEntry, items);
. . . . .
}
Tuesday, October 8, 2013 5:26 PM
All replies
-
User697462465 posted
Hi laguy,
I suggest you to use the TryValidateObject method to solve it.
According to your description, I have created a sample, it have a custom validate method, which to compare the two values.
Please try to refer to the following code:
In the ValidateCustom class:
public class ValidateCustom : IValidatableObject { [Required] public bool Enable { get; set; } [Range(1, 5)] public int Prop1 { get; set; } [Range(1, 5)] public int Prop2 { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { var results = new List<ValidationResult>(); if (this.Enable) { Validator.TryValidateProperty(this.Prop1, new ValidationContext(this, null, null) { MemberName = "Prop1" }, results); Validator.TryValidateProperty(this.Prop2, new ValidationContext(this, null, null) { MemberName = "Prop2" }, results); // you can add your custom validation if (this.Prop1 > this.Prop2) { results.Add(new ValidationResult("Prop1 must be smaller than Prop2")); } } return results; } }
In the test method:
public void DoValidation() { var customValidate = new ValidateCustom() { //Enable = null, Prop1 = 3, Prop2 = 2 }; bool validateAllProperties = true; var results = new List<ValidationResult>(); customValidate.Enable = true; bool isValid = Validator.TryValidateObject( customValidate, new ValidationContext(customValidate, null, null), results, validateAllProperties); Response.Write(isValid+"<br/>"); foreach (ValidationResult r in results) { Response.Write(r.ErrorMessage); } }
There is also a blog, maybe useful to you:
http://blogs.msdn.com/b/adonet/archive/2010/12/15/ef-feature-ctp5-validation.aspxHope it can help you.
If your problem still exists, please let me know.
Best Regards,
Terry GuoWednesday, October 9, 2013 10:48 PM -
User343140345 posted
Hi Terry,
Thanks for your help. :)
Since, I'm using Dynamic Data and the Edit Mode is using the Page Template: edit.ascx .cs, I don't explicitly
create the DBContext Entities class and do the SaveChanges.
In Edit.ascx there is a FormView bound to EntityDataSource control.
I don't know how to intercept the Save button submit and issue your code example.
Again, the EntityDataSource control creates a ObjectContext and not a DbContext object.
My EF Entities Database First T4 Generated classes do NOT have the Validation methods that are generated
in the Linq To Sql ObjectContext.
I am unable to get IValidatableObject Validate method to fire during the Save.
The article you gave me doesn't address the situation when you are using the
EntityDataSource with the FormView control.
Switch to Linq To Sql:
I'm getting frustrated, since I believe that the Entity Validation is much more straight forward when working
with a Linq To Sql .dbml file generated ObjectContext.
So, if I can't get this Entity Validation working, should I switch to DD with Linq To Sql ?
Are there some big limitations with DD L2S that are handled with DD EF Entities ?
Thanks, Paul
UPDATE:
I made a little more progress, using the TryValidate code in the EntityDataSource Control Updating EventHandler
I can fire the Validate method on the Entity but I don't know how to show ( bind ) the Errors to the DynamicValidator ( ValidationSummary ) control ?
I guess I could create another ValidationSummary control on the page and do that ?
Also, when there is a Validation error and I issue the e.Cancel the updates are NOT saved (which is good) but DD logic goes back ( redirects ) to the List View
instead of stayiing on the current Edit.aspx page.
protected void DetailsDataSource_Updating(object sender, EntityDataSourceChangingEventArgs e)
{
var obj1 = e.Entity;
if (obj1 is IValidatableObject)
{
var objValidatable = (IValidatableObject)(obj1);ICollection<ValidationResult> listErrs = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(objValidatable, new ValidationContext(objValidatable), listErrs);
if (!isValid)
{
e.Cancel = true;
// e.ExceptionHandled = true;// ??? How to bind listErrs to the ValidationSummary control ???
}
}}
Update2: Here is code in Edit.ascx.cs to show Custom Validation Errors in injected ValidationSummary control
protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)
{
if ( bErrorFlag == false)
{
// no Entity Validate method error, continue
if (e.Exception == null || e.ExceptionHandled)
{
Response.Redirect(table.ListActionPath);
}
}
}private bool bErrorFlag = false; // Error Flag set in Updating EventHandler
private string strErrorMsg = "";protected void DetailsDataSource_Updating(object sender, EntityDataSourceChangingEventArgs e)
{
var obj1 = e.Entity;
if (obj1 is IValidatableObject)
{
var objValidatable = (IValidatableObject)(obj1);
//IEnumerable<ValidationResult> errs = obj.Validate(new ValidationContext(obj));ICollection<ValidationResult> listErrs = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(objValidatable, new ValidationContext(objValidatable), listErrs);
if (!isValid)
{
e.Cancel = true;
bErrorFlag = true; // set error flag
foreach ( var item in listErrs)
{
strErrorMsg += " " + item.ErrorMessage;
}DisplayCustomMessageInValidationSummary(strErrorMsg); // Display in ValidationSummary
}
}}
private void DisplayCustomMessageInValidationSummary(string message)
{
//credit to: http://www.extremeexperts.com/Net/FAQ/DisplayCustomMsgInValidationSummary.aspx
CustomValidator CustomValidatorCtrl = new CustomValidator();CustomValidatorCtrl.IsValid = false;
CustomValidatorCtrl.ErrorMessage = message;
this.Page.Controls.Add(CustomValidatorCtrl);
}
So, I'm hoping there is a cleaner way to do this where the error message can also show next to the
column as well as in the ValidationSummary.
Any help please ?
LA Guy
Friday, October 11, 2013 4:47 PM -
User697462465 posted
Hi laguy,
If you want to display thest validation message to client, I suggest you to use the CustomValidator control.
About the CustomValidator please try to refer to the following code:
aspx code:
<asp:CustomValidator id="CustomValidator1" runat="server" OnServerValidate="TestValidate" />
cs code:
protected void TestValidate(object sender, ServerValidateEventArgs args) { args.IsVlid = //this is your validate business code... CustomValidator1.ErrorMessage = //this is error message... }
More information please try to refer to:
http://msdn.microsoft.com/en-us/library/9eee01cx(v=vs.85).aspxHope it can help you.
Best Regards,
Terry Guo
Tuesday, October 15, 2013 11:35 AM -
User-330204900 posted
As long as you thow a ValidationException you should be OK and DD works fine with DB context now to see how look here
Thursday, October 17, 2013 4:53 AM