views:

167

answers:

2

Problem:

I have a WCF Webservice which can be used by customers to upload multiple datarecords. To validate the data I use Enterprise Library Validation Block. The records can be nested several layers deep.

Question:

How to identify in which record the validation failed?

Example:

Consider the following datastructure. For each Continent there can be multible Countries and for each Country there can be multiple Cities.

  • Continent
    • Name
    • Country
      • Name
      • City
        • Name
        • Mayor

When the validation of the Mayor of a City fails, I want to know for which City, Country and Continet it failed.

A: 

I think you may need to write your own validator that would contain the logic necessary to go back up your object tree and produce a suitable validation error message. Take a look at this article for more info:

http://www.codeproject.com/KB/cs/VABCustomValidator.aspx

MattK
+1  A: 

I Solved my problem by doing the following:

Setup int the WCF Integration:

(Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF)

Create a simple marker Attribute:

public class ValidationKeyAttribute : Attribute
{
}

which will be put on the Property that identifies the data record.

Create an Interface:

public interface IValidationBackReference
{
    object BackReference { get; set; }
}

This is for the reference from the City record to the Country record in the above example so that all Identifiers up the chain also get included

Modify the BeforeCall() Method in ValidationParameterInspector

to crawl all inputs and set the BackReference using something like this:

private void SearchForBackReferences(object input, object backReference)
{
    if (input == null)
    {
        return;
    }
    Type t = input.GetType();
    if (t.IsArray)
    {
        Object[] inputs = (object[])input;
        SearchForBackReferences(inputs, backReference);
    }
    else if (input is IValidationBackReference)
    {
        foreach (PropertyInfo info in t.GetProperties())
        {
            object value = info.GetValue(input, null);
            SearchForBackReferences(value, input);
        }
        ((IValidationBackReference)input).BackReference = backReference;
    }
}

private void SearchForBackReferences(object[] inputs, object backReference)
{
    if(inputs==null)
    {
        return;
    }
    foreach (object input in inputs)
    {   
        SearchForBackReferences(input, backReference);
    }
}

Modify the CreateValidationDetail method in ValidationParameterInspector

to go up the record tree via the BackReferences with something like this

private static string FindKey(object target)
{
    StringBuilder result = new StringBuilder();
    if( target == null )
    {
        return result.ToString();
    }
    if(target is IValidationBackReference)
    {
        result.Append(FindKey(((IValidationBackReference) target).BackReference));
    }
    Type t = target.GetType();
    foreach (var info in t.GetProperties())
    {
        if (Attribute.IsDefined(info, typeof(ValidationKeyAttribute)))
        {
            object objectValue = info.GetValue(target, null);
            string stringValue = "(null)";
            if (objectValue != null)
            {
                stringValue = objectValue.ToString();
            }
            result.Append(string.Format("{0} = '{1}'; ",info.Name, stringValue));
        }
    }
    return result.ToString();
}

and put it into the Details.Key of the ValidationKDetail. So far the Setup.


In the Webservice

all you have to do now is Implement the IValidationBackReference interface on the classes used by the Webservice and put the [ValidationKey] Attribute on appropriate Properties.

The example above would look like:

public class Continent : IValidationBackReference
{
    public object BackReference { get; set; }
    [ValidationKey]
    public string Name { get; set; }

    public Country[] Countries{ get; set; }
}

public class Country : IValidationBackReference
{
    public object BackReference { get; set; }
    [ValidationKey]
    public string Name { get; set; }

    public City[] Cities { get; set; }
}

public class City : IValidationBackReference
{
    public object BackReference { get; set; }
    [ValidationKey]
    public string Mayor { get; set; }
}

what a monster ...

Thomas Schreiner