For those that care, this has been fixed in r1073 (along with the
longest commit message in project history).

 - Jon

On Mon, 2009-05-11 at 17:18 -0400, Jonathan Pryor wrote:

> Attached is my current patch to make this work.  It doesn't work.
> 
> The approach looks at all properties of the object we're
> inserting/updating, looking for EntitySet<T>'s, and if any are found
> it then inserts/updates the items found within the EntitySet<T>'s.
> This appears to work properly.
> 
> The current problem is that after the Category is inserted,
> Category.CategoryID is updated.  However, the Product doesn't see the
> new CategoryID value, so it tries to insert using 0 as the CategoryID,
> which fails due to a Foreign Key constraint violation.
> 
> What I'm currently wondering is how the Products attached to the
> Category are notified that the CategoryID has changed.  The
> DbLinq-generated Category and Product classes seem to be similar
> enough, and INotifyPropertyChanged/etc. aren't used within the file,
> so I'm not sure what mechanism is used to update all Products attached
> to the Category.
> 
> Thoughts?
> - Jon
> 
> On Mon, 2009-05-11 at 20:22 +0100, Giacomo Tesio wrote:
> 
> > Than probably its related with the InsertOnSubmit().
> > 
> > Table<T> refer to the datacontext and surely register the entity.
> > While registering it's probably deeply visited to locate any
> > EntitySet and EntityRef filled.
> > The entities filled are then registered (note that entity
> > registration is idempotent: you should be able to register an entity
> > many time without any problem)
> > 
> > I try to take a look at it, now...
> > 
> > 
> > Giacomo
> > 
> > On Mon, May 11, 2009 at 6:29 PM, Jonathan Pryor <[email protected]>
> > wrote:
> > 
> >         It's not a dumb question, and yes it passes on linq to sql.
> >         
> >         WriteTest.cs (which contains this test) is part of the
> >         DbLinq.SqlServer_test_strict project, which builds against
> >         SQL Server.  These tests pass within that project.
> >         
> >         I'm not sure how Linq to SQL does it (is it EntitySet<T>
> >         that does it, or something else), but it is supported.  I
> >         doubt that EntitySet<T> is directly involved, though, since
> >         there isn't any connection between EntitySet<T> and
> >         DataContext (so there'd be no way afaik for
> >         EntitySet<T>.Add() to implicitly register values).
> >         
> >         - Jon 
> >         
> >         
> >         
> >         On Mon, 2009-05-11 at 17:26 +0200, Giacomo Tesio wrote:
> >         
> >         > I know it's a dumb question, but... do such a test pass on
> >         > linq to sql?
> >         > 
> >         > I would imagine that you should add the product to the
> >         > product list too, to make it submitted.
> >         > I mean you should 
> >         > 
> >         > db.Products.InsertOnSubmit(p);
> >         > 
> >         > 
> >         > 
> >         > The alternative would be the EntitySet keeping track of
> >         > entities added... (or asking to the datacontext to
> >         > register each added entities...
> >         > 
> >         > 
> >         > If it should work, it should not be too complex to do..
> >         > 
> >         > 
> >         > Giacomo
> >         > 
> >         > 
> >         > On Mon, May 11, 2009 at 4:55 PM, Jonathan Pryor
> >         > <[email protected]> wrote:
> >         > 
> >         >         Actually, it's worse than I thought.  (Which is
> >         >         why I should add more assertions to my tests...)
> >         >         The real problem is that inserting and updating
> >         >         only inserts/updates the immediate object, and not
> >         >         all referenced objects.  Thus, this (paraphrased
> >         >         from WriteTest.cs): 
> >         >         
> >         >                 Category c = new Category {...};
> >         >                 Product  p = new Product {...};
> >         >                 c.Products.Add(p);
> >         >                 db.Categories.InsertOnSubmit(c);
> >         >                 db.SubmitChanges();
> >         >                 
> >         >                 // This works: the Category IS inserted
> >         >                 Assert.AreEqual(1, db.Categories.Where(c => 
> > c.CategoryName == ...).Count());
> >         >                 // This fails: the referenced Product is NOT 
> > inserted
> >         >                 Assert.AreEqual(1, db.Products.Where(p => 
> > p.ProductName == ...).Count());
> >         >         
> >         >         Looks like QueryBuilder.GetInsertQuery() and
> >         >         QueryBuilder.GetUpdateQuery() are at fault,
> >         >         especially considering that they appear to assume
> >         >         that only one statement will be required (while
> >         >         more than one may be required when updating an
> >         >         entire object graph, as the above example
> >         >         requires).
> >         >         
> >         >         - Jon 
> >         >         
> >         >         
> >         >         
> >         >         On Mon, 2009-05-11 at 01:10 -0400, Jonathan Pryor
> >         >         wrote:
> >         >         
> >         >         > After more debugging, I'm heading to bed, but
> >         >         > this is what I've found out: our change tracking
> >         >         > isn't tracking changes. :-)
> >         >         > 
> >         >         > Specifically, when you have code like this
> >         >         > (using something NUnit-testable instead of from
> >         >         > NerdDinner): 
> >         >         > 
> >         >         >         Category c = GetExistingCategory();
> >         >         >         Product p = new Product {...};
> >         >         >         c.Products.Add(p);
> >         >         >         db.SubmitChanges();
> >         >         > 
> >         >         > the newly added product isn't actually added to
> >         >         > the database.  The problem appears to be because
> >         >         > of MemberModificationHandler: it only tracks
> >         >         > INotifyPropertyChanged and not
> >         >         > INotifyPropertyChanging (see
> >         >         > MemberModificationHandler.RegisterNotification()).
> >         >         > 
> >         >         > So, what happens is c is a tracked object (yay).
> >         >         > As such, it is registered with the EntityTracker
> >         >         > (double yay).  However, for change tracking to
> >         >         > work, MemberModificationHandler uses the
> >         >         > INotifyPropertyChanged interface.  In this case,
> >         >         > c.Products.Add(p) doesn't actually invoke
> >         >         > MemberModificationHandler.OnPropertyChangedEvent(), 
> > because it isn't registered.
> >         >         > 
> >         >         > Specifically, the registered Category fires the
> >         >         > INotifyPropertyChanging.PropertyChanging event,
> >         >         > because (as far as it knows) c.Products will be
> >         >         > changing.  However, c doesn't know what, if
> >         >         > anything, will change.  c.Products.Add(p)
> >         >         > eventually calls into
> >         >         > Category.attach_Products(), which then
> >         >         > effectively does 'p.Category = c'.  This last
> >         >         > expression is what fires the
> >         >         > INotifyPropertyChanged.PropertyChanged event,
> >         >         > but only on p, which hasn't been registered with
> >         >         > anything, and thus there are no handlers
> >         >         > registered.
> >         >         > 
> >         >         > Oops.
> >         >         > 
> >         >         > The only solution I can think of is that
> >         >         > MemberModificationHandler needs to listen to the
> >         >         > INotifyPropertyChanging.PropertyChanging event
> >         >         > (if available), and if it's fired it needs to
> >         >         > check for changes on all properties of the
> >         >         > changing object.
> >         >         > 
> >         >         > So I did this, and it still fails (see attached
> >         >         > patch, which includes the unit test).  It
> >         >         > properly detects that 'c' may have changed, but
> >         >         > QueryRunner.Update() doesn't do a "deep" update;
> >         >         > that is, it only updates the Category properties
> >         >         > (CategoryName, Description, etc.) but not the
> >         >         > values of anything attached to it (like the
> >         >         > Category.Products collection).
> >         >         > 
> >         >         > Any ideas on how to resolve this?
> >         >         > 
> >         >         > Thanks,
> >         >         > - Jon
> >         >         > 
> >         >         > On Sun, 2009-05-10 at 22:02 -0400, Jonathan
> >         >         > Pryor wrote:
> >         >         > 
> >         >         > > I already fixed this, and it has been
> >         >         > > committed to svn.
> >         >         > > DataContext.SetEntitySetQueries() was being
> >         >         > > called because of
> >         >         > > DataContext._GetOrRegisterEntity(), which in
> >         >         > > turn was called by DataContext.Register(),
> >         >         > > which was called by
> >         >         > > DataContext.SubmitChanges().  (Just check the
> >         >         > > callstack in the original message...)  The
> >         >         > > problem was twofold:  (1) DbLinq's
> >         >         > > EntitySet<T> was screwy (though I blame a lot
> >         >         > > of that screwiness on .NET's EntitySet<T> --
> >         >         > > see the new EntitySetTest.cs file, and laugh
> >         >         > > hysterically when you see when the
> >         >         > > EntitySet<T>.ListChanged event is raised), and
> >         >         > > (2) calling dinner.RSVPs.Add(rsvp) caused the
> >         >         > > EntitySet<RSVP> source to be created, and
> >         >         > > EntitySet<T>.SetSource() cannot be called
> >         >         > > again once
> >         >         > > EntitySet<T>.HasLoadedOrAssignedValues is
> >         >         > > true.  Since
> >         >         > > DataContext.SetEntitySetsQueries() tried to do
> >         >         > > just that, it threw an exception.
> >         >         > > 
> >         >         > > Now I'm working on a related bug: subsequent
> >         >         > > updates aren't tracked.  To wit: 
> >         >         > > 
> >         >         > >         Dinner dinner = new Dinner {...};
> >         >         > >         RSVP rsvp = new RSVP();
> >         >         > >         rsvp.AttendeeName = User.Identity.Name;
> >         >         > >         dinner.RSVPs.Add(rsvp);
> >         >         > >         db.Dinners.InsertOnSubmit(dinner);
> >         >         > >         db.SubmitChanges();
> >         >         > >         
> >         >         > >         // The above now works.  Now...
> >         >         > >         
> >         >         > >         RSVP rsvp2 = new RSVP();
> >         >         > >         rsvp2.AttendeeName = "whatever";
> >         >         > >         dinner.RSVPs.Add(rsvp2);
> >         >         > >         db.SubmitChanges();
> >         >         > >         // rsvp2 is NOT submitted to the database.
> >         >         > > 
> >         >         > > I'm still tracking down why this happens.  The
> >         >         > > attached patch file recreates this same
> >         >         > > scenario within the unit tests, and also
> >         >         > > (thankfully) fails.
> >         >         > > 
> >         >         > > What I would appreciate is if you could look
> >         >         > > into the cache issues that I'm seeing. :-)
> >         >         > > 
> >         >         > > Thanks,
> >         >         > > - Jon
> >         >         > > 
> >         >         > > On Sun, 2009-05-10 at 23:41 +0100, Giacomo
> >         >         > > Tesio wrote:
> >         >         > > 
> >         >         > > > I think this is due to the
> >         >         > > > "dinner.RSVPs.Add(rsvp);"  line where you
> >         >         > > > actually fill the EntitySet.
> >         >         > > > 
> >         >         > > > The DataContext.SetEntitySetQueries() should
> >         >         > > > not run on just inserted entity, since they
> >         >         > > > are yet filled.
> >         >         > > > 
> >         >         > > > 
> >         >         > > > Tomorrow I'll try to fix this...  
> >         >         > > > 
> >         >         > > > 
> >         >         > > > Giacomo
> >         >         > > > 
> >         >         > > >  
> >         >         > > > On Fri, May 8, 2009 at 4:59 AM, Jonathan
> >         >         > > > Pryor <[email protected]> wrote:
> >         >         > > > 
> >         >         > > >         Still continuing with my "NerdDinner
> >         >         > > >         on Mono" effort, and hitting the
> >         >         > > >         following issue:
> >         >         > > >         
> >         >         > > >         When I attempt to add a new Dinner,
> >         >         > > >         NerdDinner effectively does: 
> >         >         > > >         
> >         >         > > >                 Dinner dinner = new Dinner {...};
> >         >         > > >                 RSVP rsvp = new RSVP();
> >         >         > > >                 rsvp.AttendeeName = 
> > User.Identity.Name;
> >         >         > > >                 dinner.RSVPs.Add(rsvp);
> >         >         > > >                 db.Dinners.InsertOnSubmit(dinner);
> >         >         > > >                 db.SubmitChanges();
> >         >         > > >         
> >         >         > > >         This fails, with: 
> >         >         > > >         
> >         >         > > >                 
> > System.Reflection.TargetInvocationException: Exception has been thrown by 
> > the target of an invocation. ---> System.InvalidOperationException: The 
> > EntitySet is already loaded and the source cannot be changed.
> >         >         > > >                   at 
> > System.Data.Linq.EntitySet`1[NerdDinner.Models.RSVP].SetSource 
> > (IEnumerable`1 entitySource) [0x00000] 
> >         >         > > >                   at (wrapper managed-to-native) 
> > System.Reflection.MonoMethod:InternalInvoke 
> > (object,object[],System.Exception&)
> >         >         > > >                   at 
> > System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags 
> > invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, 
> > System.Globalization.CultureInfo culture) [0x000ca] in 
> > /home/jon/Development/mono-HEAD/mcs/class/corlib/System.Reflection/MonoMethod.cs:169
> >  
> >         >         > > >                   --- End of inner exception stack 
> > trace ---
> >         >         > > >                   at 
> > System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags 
> > invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, 
> > System.Globalization.CultureInfo culture) [0x000e5] in 
> > /home/jon/Development/mono-HEAD/mcs/class/corlib/System.Reflection/MonoMethod.cs:179
> >  
> >         >         > > >                   at 
> > System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] 
> > parameters) [0x00000] in 
> > /home/jon/Development/mono-HEAD/mcs/class/corlib/System.Reflection/MethodBase.cs:111
> >  
> >         >         > > >                   at 
> > System.Data.Linq.DataContext.SetEntitySetsQueries (System.Object entity) 
> > [0x00173] in 
> > /home/jon/Development/mono-HEAD/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs:608
> >  
> >         >         > > >                   at 
> > System.Data.Linq.DataContext._GetOrRegisterEntity (System.Object entity) 
> > [0x00015] in 
> > /home/jon/Development/mono-HEAD/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs:468
> >  
> >         >         > > >                   at 
> > System.Data.Linq.DataContext.Register (System.Object entity) [0x0000d] in 
> > /home/jon/Development/mono-HEAD/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs:675
> >  
> >         >         > > >                   at 
> > System.Data.Linq.DataContext.SubmitChanges (ConflictMode failureMode) 
> > [0x000a6] in 
> > /home/jon/Development/mono-HEAD/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs:378
> >  
> >         >         > > >                   at 
> > System.Data.Linq.DataContext.SubmitChanges () [0x00000] in 
> > /home/jon/Development/mono-HEAD/mcs/class/System.Data.Linq/src/DbLinq/Data/Linq/DataContext.cs:339
> >  
> >         >         > > >                   at 
> > NerdDinner.Models.DinnerRepository.Save () [0x00000] 
> >         >         > > >                   at 
> > NerdDinner.Controllers.DinnersController.Create (NerdDinner.Models.Dinner 
> > dinner) [0x00000] 
> >         >         > > >         
> >         >         > > >         Suffice it to say, this doesn't
> >         >         > > >         happen under .NET's
> >         >         > > >         System.Data.Linq.  I'm wondering if
> >         >         > > >         anyone has seen a similar error to
> >         >         > > >         this before, and/or knows what an
> >         >         > > >         appropriate fix would be.
> >         >         > > >         
> >         >         > > >         Thanks,
> >         >         > > >         - Jon
> >         >         > > >         
> >         >         > > >         
> >         >         > > >         
> >         >         > > >         
> >         >         > > > 
> >         >         > > > 
> >         >         > > > 
> >         >         > > > 
> >         >         > > 
> >         >         > > 
> >         >         > > 
> >         >         > 
> >         >         > 
> >         >         > 
> >         >         
> >         >         
> >         >         
> >         >         
> >         > 
> >         > 
> >         > 
> >         > 
> >         
> >         
> >         
> >         
> > 
> > 
> > 
> > 
> 
> 
> > 

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"DbLinq" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/dblinq?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to