|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Ling to SQL and error recoveryHere 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); } } } } Tapio Kulmala <nob***@nowhere.com> wrote:
Show quote > Here is a simple console-program that illustrates the problem. It uses I don't know whether it applies to LINQ to SQL as well, but I know that > 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? 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. -- Jon Skeet - <sk***@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too Tapio Kulmala wrote:
Show quote > Hi! It fails again, because SubmitChanges simply flushes all changes of> > 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. 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#) ------------------------------------------------------------------------ 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 **************************************************************** In article <xn0fdbzxv40vpm***@news.microsoft.com>, perseus.usenetNOSPAM@xs4all.nl says... Show quote > > 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 > > -- Tapio Kulmala wrote:
> Hi! true. > > 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. 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 NHibernate doesn't do graph management/entity management in a lot of> 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). 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 Show quote > > > Tapio > > > **************************************************************** > Tapio Kulmala > > "Those are my principles. If you don't like them I have others." > > - Groucho Marx > **************************************************************** > > > > In article <xn0fdbzxv40vpm***@news.microsoft.com>, > perseus.usenetNOSPAM@xs4all.nl says... > > > > 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. -- ------------------------------------------------------------------------ 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#) ------------------------------------------------------------------------ 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 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 Tapio Kulmala wrote:
Show quote > Hi! Not yet, we have entity versioning in place but not graph versioning.> > 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? 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#) ------------------------------------------------------------------------ |
|||||||||||||||||||||||