Ling to SQL and error recovery

T

Tapio Kulmala

Hi!

Here is a simple console-program that illustrates the problem. It uses
Northwind database and requires only Customer and Order entities.

The problem is that if the first datacontext.SubmitChanges fails because
of validation errors also the second one will fail. It seems that the
second datacontext.SubmitChanges tries to validate also the first order
and that will of course fail.

What should I do to recover from the first failure? The failing order is
somewhere in the cache and I don't know how to get rid of it. I know I
could create a new datacontext instance but in my case it would require
major refactorings.

Any Ideas?

Tapio



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinqProblemDemo
{
class Program
{
static void Main(string[] args)
{
NorthwindDataClassesDataContext datacontext = new
NorthwindDataClassesDataContext();

Customer customer1 = datacontext.Customers.Single<Customer>
(c => c.CustomerID == "BONAP");
Customer customer2 = datacontext.Customers.Single<Customer>
(c => c.CustomerID == "BOLID");

try
{
// fails

Order order1 = new Order();
customer1.Orders.Add(order1);
datacontext.SubmitChanges();
}
catch ( Exception ex)
{
System.Console.WriteLine(ex.Message);
}

try
{
// shouldn't fail but fails

Order order2 = new Order();
order2.OrderDate = DateTime.Now;
customer2.Orders.Add(order2);
datacontext.SubmitChanges();
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}

System.Console.ReadLine();
}
}

partial class Order
{
partial void OnValidate()
{
if (!this.OrderDate.HasValue)
{
throw new ApplicationException("OrderDate was missing.
Customer : " + this.CustomerID);
}
}
}
}
 
J

Jon Skeet [C# MVP]

Tapio Kulmala said:
Here is a simple console-program that illustrates the problem. It uses
Northwind database and requires only Customer and Order entities.

The problem is that if the first datacontext.SubmitChanges fails because
of validation errors also the second one will fail. It seems that the
second datacontext.SubmitChanges tries to validate also the first order
and that will of course fail.

What should I do to recover from the first failure? The failing order is
somewhere in the cache and I don't know how to get rid of it. I know I
could create a new datacontext instance but in my case it would require
major refactorings.

Any Ideas?

I don't know whether it applies to LINQ to SQL as well, but I know that
within Hibernate it was generally considered unwise to reuse a session
which had failed in some way.

I suggest creating a new context. There may be ways of getting round
the issue, but they're likely to have hidden problems.
 
F

Frans Bouma [C# MVP]

Tapio said:
Hi!

Here is a simple console-program that illustrates the problem. It
uses Northwind database and requires only Customer and Order entities.

The problem is that if the first datacontext.SubmitChanges fails
because of validation errors also the second one will fail. It seems
that the second datacontext.SubmitChanges tries to validate also the
first order and that will of course fail.

What should I do to recover from the first failure? The failing order
is somewhere in the cache and I don't know how to get rid of it. I
know I could create a new datacontext instance but in my case it
would require major refactorings.

It fails again, because SubmitChanges simply flushes all changes of
entity objects assigned to the context. As the entity which caused the
first failure is still assigned to the context, it will be considered
again.

So the only solution is to remove the faulty entity from the graph.
Fun eh, those context-oriented o/r mappers ;)

In general, it's wise to do validation before a save action occurs
btw, so you validate the graph before the db code is actually called.
NO idea if Linq to Sql has that feature.

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
T

Tapio Kulmala

Hi!

I "normally" validate before sending anything to the persistence layer.
In this case i just thought, it would be a good idea to enforce the
validation in the OnValidate method. Nobody could send in data without
validation.

There is also another problem in LinqToSQL. When I assign a Customer to
the Order I effectively have already modified entities. The context is
already ruined. The only way to avoid this is to validate before any
changes. How could I do validation before the changes? I don't think
NHibernate has this problem. It does not know anything about the new
entity before I try to save it. LinqToSql hooks it into the context much
earlier ( = construction).


Tapio


****************************************************************
Tapio Kulmala

"Those are my principles. If you don't like them I have others."

- Groucho Marx
****************************************************************



It fails again, because SubmitChanges simply flushes all changes of
entity objects assigned to the context. As the entity which caused the
first failure is still assigned to the context, it will be considered
again.

So the only solution is to remove the faulty entity from the graph.
Fun eh, those context-oriented o/r mappers ;)

In general, it's wise to do validation before a save action occurs
btw, so you validate the graph before the db code is actually called.
NO idea if Linq to Sql has that feature.

FB

--
 
F

Frans Bouma [C# MVP]

Tapio said:
Hi!

I "normally" validate before sending anything to the persistence
layer. In this case i just thought, it would be a good idea to
enforce the validation in the OnValidate method. Nobody could send in
data without validation.

true.

You ran into an interesting research area though: graph versioning
in-memory. read on ->
There is also another problem in LinqToSQL. When I assign a Customer
to the Order I effectively have already modified entities. The
context is already ruined. The only way to avoid this is to validate
before any changes. How could I do validation before the changes? I
don't think NHibernate has this problem. It does not know anything
about the new entity before I try to save it. LinqToSql hooks it into
the context much earlier ( = construction).

NHibernate doesn't do graph management/entity management in a lot of
places, so you're not affected by it there, still you'll run into the
same issue you have presented here.

If you have a customer entity instance and you add a new order entity
instance to its Orders collection, you build a graph (it has 2 nodes
and 1 edge: customer <- order, where order is depending on customer)

Some frameworks do entity / graph management and sync the FK in order
with the PK of customer when you do: myCustomer.Orders.Add(myOrder);.
Others don't, it's a matter of choice.

So when you flush a context or session, you flush the changes on the
graph to the DB. (or graphs, the context/session checks every entity it
has a reference of and sees if it is in a graph and builds a queue of
items to do, using Topological ordering/sorting of the DAGs)

This means that if you have made a modification to a graph, e.g. added
an order to a customer's Orders collection, that change WILL be seen by
the context, if order or customer is referenced by the context.

It can be, as you've seen, that there's an error in the graph
somewhere, e.g. Customer has some wrong values. Without correcting
them, the graph will never be able to be saved, as the graph is handled
as a unit. So if you don't fix the wrong values, you have to remove
Customer from the graph and make sure Customer isn't seen by the
context nor order (detach), so the context won't take into account the
customer at all, because it's not in the graph.

So, to 'undo' graph changes, you want to 'rollback' the graph to a
previous state, which means graph versioning. This is a difficult topic
and requires a lot of code behind the scenes.

What you can do till o/r mappers have graph versioning build in, is
manually roll back the steps, so as I said, remove order from the
customer, which should make things the same as it was before, and also
make sure customer isn't seen from the context.

However, what's best is to validate earlier. LLBLGen Pro for example
uses dependency injection to inject validator objects inside entities
(so you can do that with linq to sql using winsor or spring.net) to
inject validation which is used inside the entity directly. This means
that if an order is added to the graph, the validator can check if this
is ok. If not, it won't happen. This is the kind of code you should add
to your linq to sql code, so the graph you have in-memory at time T is
always valid, because it IS a graph: if it wasn't valid, the graph
wouldn't be that way: order wouldnt be addable to customer for example,
if customer was invalid.

FB
Tapio


****************************************************************
Tapio Kulmala

"Those are my principles. If you don't like them I have others."

- Groucho Marx
****************************************************************


--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 
T

Tapio Kulmala

Hi!

I totally agree with you.

LinqToSql and Nhibernate have a different way of managing the graph. It
seems that in LinqToSql the graph is modified already during the new
Order() -method. In NHibernate the graph requires session.Save(order)
before the new entity is saved into the graph.

If you update existing entities in graph ( modify an entity or
customer.Orders.Add(order) ) there propably isn't much difference. The
behavior of LingToSql an NHibernate is the same.

This is major issue if the application is using NHibernate and you want
to start using LinqToSql. It will require a fundamental change in the
validation logics. With LinqToSql you'd have to validate before any
changes to the graph and before any changes to objects already in the
graph. To do that, you'll need a ctystal ball. The only way to solve
this is to throw away the whole graph/context/session.

My code example was a very much simplified one. Injecting the valdator
using Windsor doesn't really change anything.

Maybe some day we'll see a framework that knows how to rollback also all
graph-changes. Can LLBLGen to that?


Tapio
 
T

Tapio Kulmala

I shouldn't have written that previous post. It's just rubbish. Why
didn't the garbagecollector notice it. I don't know what I was thinking
when I wrote it.

LinqToSql does not know anything about the new object until it is added
to the graph. In LinqToSql neworder.Customer = existingCustomer calls
existingCustomer.Orders.Add(newOrder) under the hood. This assignment
adds an order to the graph and might cause problems later if the order
does not pass the validation. NHibernate gives you more options.

Anyway... I'll have to play with this a bit more.

Tapio
 
F

Frans Bouma [C# MVP]

Tapio said:
Hi!

I totally agree with you.

LinqToSql and Nhibernate have a different way of managing the graph.
It seems that in LinqToSql the graph is modified already during the
new Order() -method. In NHibernate the graph requires
session.Save(order) before the new entity is saved into the graph.

If you update existing entities in graph ( modify an entity or
customer.Orders.Add(order) ) there propably isn't much difference.
The behavior of LingToSql an NHibernate is the same.

This is major issue if the application is using NHibernate and you
want to start using LinqToSql. It will require a fundamental change
in the validation logics. With LinqToSql you'd have to validate
before any changes to the graph and before any changes to objects
already in the graph. To do that, you'll need a ctystal ball. The
only way to solve this is to throw away the whole
graph/context/session.

My code example was a very much simplified one. Injecting the
valdator using Windsor doesn't really change anything.

Maybe some day we'll see a framework that knows how to rollback also
all graph-changes. Can LLBLGen to that?

Not yet, we have entity versioning in place but not graph versioning.
We're planning transactional graph versioning for v3

FB

--
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#)
------------------------------------------------------------------------
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top