I care... :-D
Giacomo On Thu, May 14, 2009 at 5:40 AM, Jonathan Pryor <[email protected]> wrote: > 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 Categoryare > 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.csfile, > 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 -~----------~----~----~----~------~----~------~--~---
