StackOwerFlow Cross Reference Hell

May 7, 2010 at 2:03 PM

As introduction just a simple example:

Order has a reference to OrderItem and OrderItem has a reference to Order. They have also proper public Properties just for navigation purposes.

Using code generating tools, we created validation code:

public partial class OrderItemSpecification  : Validates<OrderItem>
{
    public OrderItemSpecification  ()
    {
        this.IsDefaultForType();
        Check(o => o.Description).Optional().MaxLength(20);
        Check(o => o.Order).Required().Specification();
    }
}

 

public partial class OrderSpecification  : Validates<Order>
{
    public OrderItemSpecification  ()
    {
        this.IsDefaultForType();
        Check(o => o.Description).Optional().MaxLength(20);
        Check(o => o.OrderItem).Required().ForEachSpecification();
    }
}

 

It's clear, that this generates a Stackowerflow during execution of ValidationCatalog.Validate. You could handle this problem i your framework cachig already validated referenes.

What do you think?

 

Coordinator
May 7, 2010 at 3:10 PM

marinko,

In the example you gave, an Order is composed of OrderItems, but an OrderItem is not composed of an Order - right?  What I might suggest is that when you validate an OrderItem, there is no need to validate the Order it belongs to.  However, it IS certainly valid to validate each and every OrderItem when validating an Order.  Thus, I would suggest that you leave off the "Check(o => o.Order).Required().Specification();" statement from the OrderItemSpecification.

On a side note, we had at one time considered auto wiring up the specifications for classes (i.e. enforcing the validation for all properties that refer to types we have specification for).  Although this would have eliminated the need for a developer to write code like "Check(o => o.Order).Required().Specification();", we identified that it would have caused all kinds of problems - including the stackoverflow exceptions you identified.  This is why we opted for a way for a developer to control what properties get validated, and which do not.

Hope this helps clarify your situation.

Randy

May 7, 2010 at 7:52 PM

Randy,

You are right, but just to give you much more details. In my example I use Entitiy Framework and T4 template to generate Default Specifications for every entity. If there is in the *.edmx designer a value MaxLength for a specific Entity Column I would generate the code above. I also know the accociation One to Many and others, so I'm able to generate ForEachSpecification in appropriate case. The problem I have is I have no Idea how the business context is. In my example 

Entity 1 has an OneToMany Relation To Entity 2

1 Example - OrderItem uses an Article as property  --> correct from business perpective
2 Example - OrderItem uses Order as property.       --> invalid from business perspective, but correct from OO perspective

If you would handle cross reference automatically I could generate the code above and It would work fine.

By the way to my opinion your framework should have some options how to behave in certain situations:

  • Throw an Exception if specification was not found for a specific type or return just an empty ValidationNotification
  • ......The same for ValidateProperty
  • Option for my problem above

Kind regards,

Marinko

 

Coordinator
May 7, 2010 at 8:25 PM
Marinko, At this time we are not going to be able to handle a situation where classes hold references to one another. I would suggest that in you code generated specifications, you not generate code enforcing relationships and consider creating other specifications that "use" the code generated and in them, specify the relationship rules, as well as any other customized business rules. Unless your code generation leverages some other meta data that describes business relations and more complex business rules, I would think you as a developer would need to inject some human logic into the generated code, and this would allow you that opportunity. We will take your other suggestions under consideration as well. Randy
Coordinator
May 7, 2010 at 8:41 PM

About your other suggestions. If you are explicitly adding a Specification or ForEachSpecification rule such as

 

Check(o => o.Order).Required().Specification();

 

And no type was found I would definitely consider that an Exception, mostly caused by the specification not being registered. There is a way to check if a Specification was registered for that type without throwing an exception. Below is an example:

var spec = ValidationCatalog<SaveContext>.SpecificationContainer.TryGetSpecification(args.Entity.GetType());
if (spec != null)
{
	var vn = ValidationCatalog<SaveContext>.Validate(args.Entity);
	if (!vn.IsValid)
	{
		throw new ValidationException("Error saving to Repository.", vn);
	}
}

 

I'm sure that you could create a custom rule that inherited from the Specification/ForEachSpecification Rule and combine it with something similar to above to achieve what your looking for.