Question

I have a POCO object that implements IValidatableObject.

public class Documentation : IValidatableObject
{
    [Key]
    public int DocumentationId { get; set; }

    [ForeignKey("Project")]
    public int ProjectId { get; set; }

    public virtual Project Project { get; set; }

    public string FileGuid { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        return new[] { new ValidationResult("File has not been uploaded", new[] { "FileGuid" }) };
    }

}

Why is it that DbContext will run the validation while DbDomainService does not?

This test passes for DbContext:

    [TestMethod, ExpectedException(typeof(DbEntityValidationException))]
    public void TestDbContext()
    {
        SampleDbContext ctx = new SampleDbContext();
        var p = new Project()
        {
            ProjectName = "UnitTest",
        };
        var d = new Documentation()
        {
            FileGuid = "UnitTestDoc",
        };
        p.Documentations = new List<Documentation>();
        p.Documentations.Add(d);
        ctx.Projects.Add(p);
        ctx.SaveChanges();
    }

While this doesn't (no exceptions thrown):

    [TestMethod, ExpectedException(typeof(ValidationException))]
    public void TestDbDomain()
    {
        SampleDomainService svc = new SampleDomainService();
        svc.Initialize(ServiceProvider.CreateDomainServiceContext());
        var p = new Project()
        {
            ProjectName = "UnitTest",
        };
        var d = new Documentation()
        {
            FileGuid = "UnitTestDoc",
            Project = p,
        };
        ChangeSet changeSet = new ChangeSet(
            new [] {
                new ChangeSetEntry(1, p, null, DomainOperation.Insert),
                new ChangeSetEntry(2, d, null, DomainOperation.Insert),
            }
        );
        svc.Submit(changeSet);
    }

Sample code is here

Was it helpful?

Solution

There were two problems with the original code as shown above.

Firstly, DomainService does not throw ValidationException for IValidatableObject validation errors. Only DataAnnotation validations throw ValidationException. So the first thing to fix is in my test case:

[TestMethod] //, ExpectedException(typeof(ValidationException))]
public void TestDbDomain()
{
    //... setup

    bool success = svc.Submit(changeSet);

    bool foundError = (from item in changeSet.ChangeSetEntries
                       where item.HasError
                       from validationError in item.ValidationErrors
                       select validationError).Any();

    Assert.IsTrue(foundError);
}

Secondly, with a little help from Telerik's JustDecompiler (many thanks!), it turns out DomainServices.ValidateChanges only works if there's some member on the class that has been annotated with validation directives.

public class Documentation : IValidatableObject
{
    ...

    [DataType("File")]
    public string FileGuid { get; set; }

    ...
}

The DataType("File") directive doesn't trigger any validation in the validation machinery, but it is useful for setting flags.

If you are interested in the details, have a look around System.DomainServices.Server.ValidationUtilities.ValidateObjectRecursive:

private static bool ValidateObjectRecursive(object instance, string memberPath, ValidationContext validationContext, List<ValidationResult> validationResults)
{
    MetaType metaType = MetaType.GetMetaType(instance.GetType());
    if (!metaType.RequiresValidation)
    {
        return true;
    }

    // ... checks for IValidatableObject later
}

This is clearly a bug, because presence of IValidatableObject should have set metaType.RequiresValidation flag.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top