LLBLGen Entity classes and Validation Application Block

Posts   
 
    
AmitayD
User
Posts: 45
Joined: 22-Aug-2007
# Posted on: 25-Sep-2007 15:18:02   

Hello, A project i'm working on is using LLBLGen Pro 2.5, and will now start to implement Microsoft's Validation Application Block (VAB) from Enterprise Library 3.1.

VAB provides a way to configure validation for members (for example: Customer.Age must be between 0 and 120, and not null), and for function calls (in Divide(numerator, denominator) denominator must be greater than 0). You can define multiple rule sets for the validation, to support different validations for example for drafts and final saves. Validation is configured for a class, either by attributes for its members, or by external xml file (one big ugly file).

So much for the introduction simple_smile

Now, my question, what way do you know or think best to implement validation on LLBLGen validation?

The alternatives i'm considering: 1) Defining the validations in external XML file. This would actually be a good solution, but since the Enterprise library supports a single file configuration only, it would be a pain to work with on a large project. We can extend the enterprise library to support multiple validation configuration sources, but we prefer not to at this moment. Additionaly, the tool for editing the configuration (EntLib configuration console) would be pretty time consuming to work with.

2) Overriding each Entity/EntityCollection class just to override the members we want validation attributes on, and adding those attributes, while keeping the overrides with just calls to the base...). This would require rewriting some of the project, and create unrequired overhead for the new level in the hierarchy. If we do go in this direction, maybe creating a template that will create (just once) a derived class in an external project for each entity may be a good thing. What do you think about exposing the entities just as derived classes in general?

3) Extending the templates to create validations based on attributes. I don't like this very much as it will put another responsibility on the designer, and another bottleneck when validation rules are changed. I want to keep generating the LLBLGen project only when the database schema changes.

4) Other creative ideas?

If anyone tried to use a combination of VAB and LLBLGen i'd love to learn from your experience (the last threads i saw were from before VAB was released).

VAB links: http://msdn2.microsoft.com/en-us/library/bb410105.aspx (msdn) http://www.codeplex.com/VAB/Wiki/View.aspx?title=Home&version=1 (codeplex)

Thanks in advance, Amitay

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 25-Sep-2007 19:11:31   

any validation object can be used with LLBL, so long as it inherits the SD.LLBLGen.Pro.ORMSupportClasses.IValidator interface

your best bet might be to create a validator that interits both the Interface for LLBL and the bases class/interface for VAB. Then define the validation in code.

I looked into this once before, but decided it was too much work for my requirements and abandoned EntLib 3.

goose avatar
goose
User
Posts: 392
Joined: 06-Aug-2007
# Posted on: 25-Sep-2007 19:23:28   

Hi, I think is better to use LLBLGen Validation, LLBLGen has a rich validation mechanism, please read Generated code - Validation per field or per entity in documentation for details.

AmitayD
User
Posts: 45
Joined: 22-Aug-2007
# Posted on: 25-Sep-2007 19:27:22   

any validation object can be used with LLBL, so long as it inherits the SD.LLBLGen.Pro.ORMSupportClasses.IValidator interface

Hi, Maybe i wasn't making myself clear: My problem is with configurating the validation rules for LLBLGen classes: This is on VAB by either setting attributes for target classes (i.e. the entity classes) or configuring an external XML validation file. In VAB you don't create a validation class for each object class, but rather define the validation on the class itself (or via the configuration file). I'm looking for an approach to be used with generated entity classes, preferably with attributes due to VAB limitations when it comes to storing the rules in an XML file.

After i get this issue solved i might create an IValidator class that will simply use the VAB validation on the class itself, and i don't think that should be a problem.

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 25-Sep-2007 19:37:17   

attributes are tricky because they must be assigned directly above the property. I'm pretty sure user defined code regions would not work because of the comment placement

//begin my usercode region
[VAB Validation Attribute]
//end my usercode region
public int MyProperty { ... }

i think attributes must look like this

[VAB Validation Attribute]
public int MyProperty { ... }

personally i find xml is cumbersome and error prone. What project requirement(s) does the VAB fulfill that LLBL validation does not?

AmitayD
User
Posts: 45
Joined: 22-Aug-2007
# Posted on: 25-Sep-2007 19:39:43   

Hi, I think is better to use LLBLGen Validation, LLBLGen has a rich validation mechanism, please read Generated code - Validation per field or per entity in documentation for details.

Hi, Since we're going to use the VAB validation on other objects on the project, and not just generated entities, i find it a bad practice to use different mechanism for different classes (kind of misses the point of a validation framework). In addition, VAB comes and is already extended by our team with a set of controls to support per field validation errors display as web and winform controls. I find it pretty full featured and generic. In LLBLGen validation you pretty much write your own validation code, it doesn't include any framework for the validation itself, and is intended only for llblgen objects.

I recommend you'd have a look at the VAB, despite some implementation problems it's a pretty cool concept.

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 25-Sep-2007 19:50:44   

your best bet will probally be a LLBL/VAB adapter object to marry the 2 together. yes this will add another heirachy to the chain of command, but that should be expected. it's simliar to problem of fitting a round peg in a sqaure hole.

AmitayD
User
Posts: 45
Joined: 22-Aug-2007
# Posted on: 25-Sep-2007 19:51:16   

personally i find xml is cumbersome and error prone. What project requirement(s) does the VAB fulfill that LLBL validation does not?

  1. Not limited to LLBLGen classes and fields. The major point is using a generic validation solution.
  2. includes a rich a set of ready validators (i.e. range validation, regex validation, multiple option, and more).
  3. Comes with a tool for configuration the validation externally in an xml file (though in not such a good way)
  4. Includes a set of validation controls for asp.net that can be extended.
  5. Field validation is configured in much ordered way than a long "switch case" statement", and isn't done by code, but by using validation classes which usage per fields is configured in attributes or xml.
  6. Multiple validation rules per target can be used (i.e draft validation vs. completed).
  7. My team leader says so simple_smile and i think he has (more than one) point.

Thanks, Amitay

BringerOD
User
Posts: 70
Joined: 15-Jul-2006
# Posted on: 04-Mar-2008 22:45:17   

Did you ever get VAB working with LLBL?

AmitayD
User
Posts: 45
Joined: 22-Aug-2007
# Posted on: 24-Jun-2008 14:54:00   

Hi Brian, Sorry it took me a while to answer this, just saw your message (so big chance it's not relevant anymore), but maybe for other LLBLGen users:

Yes, we did manage to use VAB with LLBLGen, and it works quite well. We're using XML file (.config file) to store the validation rules. This is quite a bit more work than attribute based validation rules, but it's still not too bad. The main problem is that you have to use (unless you extend VAB, which we didn't so far) a single file for all validations, which is hard to manage in a multi developer environment. I'd like better a xml file per class.

ObjectCollectionValidator and ObjectValidator are very useful in LLBLGen entities for setting validation for entities in relations. This is one of the areas where generation a validation xml file for entities would save lots of time, since those validators should exist for each entity (since you'd want the validate the linked entities). Most of the other validators we use are quite basic: Regex, not null, Range.

One problem i remember we did encounter is with Non-Nullable fields, which still store the default value of the type. This could be solved by creating a special validator that will check the entity's Fields["FieldName"].IsNull property.

The validation itself happens on 2 main places: 1) User inteface: our validation controls are using the validations defined for the Object.Property they represent. 2) Prior to processing requests received at the main server, the received contents are validated. This validation is turned on/off per method on the main server (on by default). Validation errors are returned as exceptions (the UI validation should catch them on an earlier stage).

We have some special cases where the results of the validation are processed in a custom way.

So to conculde: VAB works quite fluently with LLBLGen entities in runtime. Most of the problems are in development with managing and adding to the validation rule files (which is actually a single huge file).

Hope it's still of any help, Amitay

wtijsma
User
Posts: 252
Joined: 18-Apr-2006
# Posted on: 24-Jun-2008 15:38:04   

Thanks for your additional info...

I guess the major issue here is actually about not being able to decorate LLBLGen generated classes with attributes in general, one of the rare major annoying things about LLBLGen.

Well, that AND the product name simple_smile .

What I do in some situations is redeclare the property with a different name in the partial class, which is not a really nice solution.

It would already already be better if it would be possible to change the visibility of entity properties to private in the LLBLGen designer, so you could encapsulate, decorate or hide them with your own properties.

Unfortunately MS didn't implement partial properties in C#, similar to partial methods, that would allow you to redeclare the same property in another file simple_smile

Thanks!

Wiebe

AmitayD
User
Posts: 45
Joined: 22-Aug-2007
# Posted on: 24-Jun-2008 16:59:49   

Thanks for the additional ideas! Another idea i'm considering (and VAB is only a minor reason for it), is creating a layer of entities above the generated one, and creating an inheriting entity for each generated entity.

The structure would look something like this: LLBLGenGenerated DLL: _EntityClasses Namespace: __* UserEntity __* ClientEntity

MyEntities DLL: _ApplicationEntities namesapce __* UserEntity: Generated.EntityClasses.UserEntity __* ClientEntity: Generated.EntityClasses.UserEntity

Of course, we can create a template to auto create the inheriting entities. You could then override the field properties, and decorate them with VAB attributes.

One problem i can see, is with the inheriting entities returning the "original" generated entities in EntityCollections and a single linked Entity fields...

But i'd open it as a seperate discussion when i'll have some extra time simple_smile

Thanks, Amitay

TomDog
User
Posts: 618
Joined: 25-Oct-2005
# Posted on: 09-Dec-2008 10:12:09   

One way around the difficulty of decorating LLBLGen(or any other) generated classes with attributes is the new System.ComponentModel.DataAnnotations that came with Dynamic Data in 3.5 SP1. In particular the MetadataType attribute - it let’s you specify another class to contain the entity metadata. A decent example of its use is here: http://visualstudiomagazine.com/features/article.aspx?editorialsid=2527 or http://lostintangent.com/category/aspnet-dynamic-data/

Then by using the System.Web.DynamicData.MetaModel you can get the validator for a particular field/property without using reflection. To see how I did it in a Winforms app go to http://www.codeplex.com/RapidDevBookCode/SourceControl/changeset/view/44685 expand AW.Data and look at Utility.cs. - a bit weird using a System.Web namespace but it works - pity they didn't separate out the Web-specific stuff out of the MetaModel like they did with the DataAnnotations.

The validator I tested it with is a System.ComponentModel.DataAnnotations.RegularExpressionAttribute but validators from VAB might work just as well.

Jeremy

Jeremy Thomas
Brandt
User
Posts: 142
Joined: 04-Apr-2007
# Posted on: 08-Mar-2009 06:28:20   

TomDog wrote:

One way around the difficulty of decorating LLBLGen(or any other) generated classes with attributes is the new System.ComponentModel.DataAnnotations that came with Dynamic Data in 3.5 SP1. In particular the MetadataType attribute - it let’s you specify another class to contain the entity metadata. A decent example of its use is here: http://visualstudiomagazine.com/features/article.aspx?editorialsid=2527 or http://lostintangent.com/category/aspnet-dynamic-data/

Then by using the System.Web.DynamicData.MetaModel you can get the validator for a particular field/property without using reflection. To see how I did it in a Winforms app go to http://www.codeplex.com/RapidDevBookCode/SourceControl/changeset/view/44685 expand AW.Data and look at Utility.cs. - a bit weird using a System.Web namespace but it works - pity they didn't separate out the Web-specific stuff out of the MetaModel like they did with the DataAnnotations.

The validator I tested it with is a System.ComponentModel.DataAnnotations.RegularExpressionAttribute but validators from VAB might work just as well.

Jeremy

That would work, but you still have to put in all the effort to create all the metadata classes. That’s really inefficient, considering all the metadata you want is in the EntityField definition. Unless you are able to generate the meta data classes (which I tried and failed using the dynamic data extensions for llblgen) and allow llblgen user code sections above each property to add additional attributes you are going to run into alot of problems.

Here is what I did to integrate the Validation application block with llblgen. I am going to run though this pretty fast as I am limited on time. The validation block prefers you to define validators through attributes or configuration. Since code generation would blow away any attributes on properties I added and defining a configuration file would duplicate the metadata that is already present in the entityfields, I didn’t want to implement either. The only other option was to define the validator through code and initially that didn’t seem possible as the core validation classes validate a value and not an entity. With out the property name returned in the validation results there was no way to link the error with the data control that was invalid. It turned out there are some special validators that act as wrappers giving the wrapped validator a context with a the defining object. One of them is the PropertyValueValidator which validates a value returned by a property.


PropertyValueValidator<ContactEntity> propertyValueValidator = new PropertyValueValidator<ContactEntity> (field.Name, compositeValidator);

For each entity I defined a SelfValidation method with the SelfValidation attribute which creates the base validators from the metadata in the EntityFields for that entity. (WARNING) Unfortunately, the Ent Lib team didn’t include a non-generic implementation of the PropertyValueValidator so you can’t define a base self validation method in the CommonEntityBase class, like I did, with out having to modify the enterprise library code.

I am only including the CommonEntityBase, an Entity and the DefaultValidator in the code below. The PrecisionAndScaleValidator validators are just custom validators that use the FieldUtility methods in the OrmSupportClasses to handles those values. If you want that code then Ill post later.

The reason why you want to use the PropertyValueValidator is because it is the only way the core validators get fed the property name as a key in their DoValidate method. The key is then added to the ValidationResult and can be used to determine which property the error relates to in the UI.

** This is an alternative to the default way the entities validate themselves so you will need to turn off the built in validation BuildInValidationBypass.AlwaysBypass; This method is also not able to use the asp.net integration controls in the ent validation library with out modifying the controls.



        // defines the base validation for all of the fields
    [HasSelfValidation]
    public abstract partial class CommonEntityBase 
    {
        [SelfValidation]
        public void EntLibBaseFieldValidation(ValidationResults validationResults)
        {
            Type thisType = this.GetType();
            IList<Validator> allValidators = new List<Validator>();
            foreach (IEntityField2 field in this.Fields)
            {
                IList<Validator> fieldValidators = new List<Validator>();

                object value = field.CurrentValue;

                Type dataType = field.DataType;
                if (GeneralUtils.IsNullableType(dataType))
                {
                    dataType = dataType.GetGenericArguments()[0];
                }
                switch (Type.GetTypeCode(dataType))
                {
                    case TypeCode.Object:
                        if (field.DataType == typeof(byte[]))
                        {
                            //byte[] buffer = (byte[])field.CurrentValue;
                            //flag3 = (buffer.Length >= 0) && (buffer.Length <= field.MaxLength);
                            //exceptionMessage = string.Concat(new object[] { "The value specified will cause an overflow error in the database. Value length: ", buffer.Length, ". Column max. length: ", field.MaxLength });
                        }
                        break;
                    case TypeCode.SByte:
                    case TypeCode.Byte:
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                        fieldValidators.Add(new EnterpriseLibrary.PrecisionValidator(field.Precision));
                        break;
                    case TypeCode.Single:
                        fieldValidators.Add(new EnterpriseLibrary.SinglePrecisionAndScaleValidator(field.Precision, field.Scale));
                        break;

                    case TypeCode.Double:
                        fieldValidators.Add(new EnterpriseLibrary.DoublePrecisionAndScaleValidator(field.Precision, field.Scale));
                        break;

                    case TypeCode.Decimal:
                        fieldValidators.Add(new EnterpriseLibrary.DecimalPrecisionAndScaleValidator(field.Precision, field.Scale));
                        break;
                    case TypeCode.String:
                        fieldValidators.Add(new StringLengthValidator(field.MaxLength));
                        break;
                    default:
                        break;

                }

                AndCompositeValidator compositeValidator = new AndCompositeValidator(fieldValidators.ToArray());
                PropertyValueValidator propertyValueValidator = new PropertyValueValidator(thisType, field.Name, compositeValidator);
                allValidators.Add(propertyValueValidator);

                fieldValidators.Clear();
            }

            AndCompositeValidator allcompositeValidator = new AndCompositeValidator(allValidators.ToArray());

            ValidationResults results = allcompositeValidator.Validate(this);

            base.DataErrorInfoErrorsPerField.Clear();
            if (!results.IsValid)
            {
                foreach (var result in results)
                {
                    if (!String.IsNullOrEmpty(result.Key))
                    {
                        base.SetEntityFieldError(result.Key, result.Message, true);
                    }
                }
            }
            validationResults.AddAllResults(results);
        }
    }



    [HasSelfValidation()]
    public partial class ContactGroupEntity
    {

        [SelfValidation()]
        protected void AdditionalValidators(ValidationResults results)
        {
            StringLengthValidator stringLength = new StringLengthValidator(10, RangeBoundaryType.Inclusive, 0, RangeBoundaryType.Ignore, false);

                PropertyValueValidator<ContactGroupEntity> propertyValueValidator = new PropertyValueValidator<ContactGroupEntity>(ContactGroupFields.Name.ToString(), stringLength);
                propertyValueValidator.Validate(this, results);
        
        }
}



/// <summary>
    /// This is the default validator for every entity.  
    /// The <see>CommonEntityBase.cs</see> base class populates the Validator for each entity on creation. 
    /// </summary>
    public class DefaultValidator : SD.LLBLGen.Pro.ORMSupportClasses.ValidatorBase
    {

        /// <summary>
        /// ValidateEntity method called by the entities ValidateEntity method.
        /// </summary>
        /// <param name="involvedEntity"></param>
        public override void ValidateEntity(SD.LLBLGen.Pro.ORMSupportClasses.IEntityCore involvedEntity)
        {

            Validator validator = Microsoft.Practices.EnterpriseLibrary.Validation.ValidationFactory.CreateValidator(involvedEntity.GetType());
            var results = validator.Validate(involvedEntity);

            if (!results.IsValid)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var result in results)
                {
                    sb.AppendLine(result.Message);
                }
                throw new ORMEntityValidationException(sb.ToString(), involvedEntity);
            }

        }

        /// <summary>
        /// Validates the entity before save.
        /// </summary>
        /// <param name="involvedEntity"></param>
        public override void ValidateEntityBeforeSave(IEntityCore involvedEntity)
        {
            ValidateEntity(involvedEntity);
            base.ValidateEntityBeforeSave(involvedEntity);
        }

}