Core data - getting fresh records

  • Hello,

    I have a Core Data point-of-sale application.  When it creates a new
    Sale, it assigns it a human-readable, incrementing ID number.  For
    better or worse, it calculates the ID by finding the maximum existing
    value and adding 1.  It works well.  (Elsewhere it uses a more
    conventional technique, like Core Data uses to generate primary keys.)

    However, an external application may add a Sale to the SQLite
    database.  Then, when my application looks for the maximum Sale ID it
    may not see that externally-generated Sale -- it won't yet be
    registered with its NSManagedObjectContext.

    My question is, is there any way, short of writing SQL or doing
    [managedObjectContext reset], that I can force a Sales fetch to go
    directly to the SQLite database and not use the Sales in memory?

    Thanks,

    Steve
  • does this help?

    http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles
    /cdUsingMOs.html#//apple_ref/doc/uid/TP40001803-208900


    +Clint

    On 9/26/07, Steve Steinitz <steinitz...> wrote:
    >
    > Hello,
    >
    > I have a Core Data point-of-sale application.  When it creates a new
    > Sale, it assigns it a human-readable, incrementing ID number.  For
    > better or worse, it calculates the ID by finding the maximum existing
    > value and adding 1.  It works well.  (Elsewhere it uses a more
    > conventional technique, like Core Data uses to generate primary keys.)
    >
    > However, an external application may add a Sale to the SQLite
    > database.  Then, when my application looks for the maximum Sale ID it
    > may not see that externally-generated Sale -- it won't yet be
    > registered with its NSManagedObjectContext.
    >
    > My question is, is there any way, short of writing SQL or doing
    > [managedObjectContext reset], that I can force a Sales fetch to go
    > directly to the SQLite database and not use the Sales in memory?
    >
    > Thanks,
    >
    > Steve
    >
  • On Wednesday, 26 September 2007 at 8:53 am, Clint Shryock wrote:

    > does this help?
    >
    > http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles
    /cdUsingMOs
    .
    > html#//apple_ref/doc/uid/TP40001803-208900

    Thanks for your help Clint.

    No, I don't think it helps because I actually need any new objects --
    that article talks about merging changes to known objects, one-by-one.

    Best regards,

    Steve
  • On 2007 Sep, 26, at 2:27, Steve Steinitz wrote:

    > My question is, is there any way, short of writing SQL or doing
    > [managedObjectContext reset], that I can force a Sales fetch to go
    > directly to the SQLite database and not use the Sales in memory?

    I'm new with Core Data, but intrigued that there is not a simple
    method to do this.  From reading the documentation, though, the
    following Wild And Crazy Kludge might work:

        (a) setStalenessInterval: to something really small like 10
    microseconds
        (b) refreshObject:mergeChanges
        (c) setStalenessInterval: back to 0 (infinite) or whatever it was

    I'm worried that if any other objects get involved in step (b) before
    (c) is invoked, it might set off a never-ending chain reaction of
    fetches, but it might be fun to try!
  • Thanks for your comments Jerry.  You wrote:

    >> My question is, is there any way, short of writing SQL or doing
    >> [managedObjectContext reset], that I can force a Sales fetch to go
    >> directly to the SQLite database and not use the Sales in memory?
    >
    > I'm new with Core Data, but intrigued that there is not a simple
    > method to do this.  From reading the documentation, though, the
    > following Wild And Crazy Kludge might work:
    >
    > (a) setStalenessInterval: to something really small like 10
    > microseconds
    > (b) refreshObject:mergeChanges
    > (c) setStalenessInterval: back to 0 (infinite) or whatever it was
    >
    > I'm worried that if any other objects get involved in step (b) before
    > (c) is invoked, it might set off a never-ending chain reaction of
    > fetches, but it might be fun to try!

    By my reading, the staleness interval is a false hope and doesn't
    have much to do with getting fresh values from the persistent store.
    Here is the somewhat cryptic quote from the doco:

        The staleness interval does NOT affect objects currently in use [?]
        (that is, it is NOT used to automatically update property values
        from a persistent store after a certain period of time).

    And besides, refreshObject: mergeChanges doesn't help me find new
    objects that have been created behind my back -- which is the gist of
    my question.

    Having not had a slam-dunk response to this question, I think I may be
    reduced to writing SQL -- something I actively avoid.

    I did try using [context reset].  It actually worked but it caused so
    many side effects (my dozens of array controllers were eventually only
    partially filled with data or completely empty) that I recoiled in
    fright from that for now.  But I may return to it (maybe mass calls to
    [xyzArrayController fetch] would fix them).

    Please, if anyone has some experience in this area, I'm all ears.  Mr
    Bumgarner?

    Cheers,

    Steve
  • I'm having similar problems as to when CD fetches objects. Have you
    tried NSObjectController.setUsesLazyFetching: NO, assuming you are
    using bindings that is?

    DKITH

    Ian

    On 27/09/2007, at 4:31 PM, Steve Steinitz wrote:

    > Thanks for your comments Jerry.  You wrote:
    >
    >>> My question is, is there any way, short of writing SQL or doing
    >>> [managedObjectContext reset], that I can force a Sales fetch to go
    >>> directly to the SQLite database and not use the Sales in memory?
    >>
    >> I'm new with Core Data, but intrigued that there is not a simple
    >> method to do this.  From reading the documentation, though, the
    >> following Wild And Crazy Kludge might work:
    >>
    >> (a) setStalenessInterval: to something really small like 10
    >> microseconds
    >> (b) refreshObject:mergeChanges
    >> (c) setStalenessInterval: back to 0 (infinite) or whatever it was
    >>
    >> I'm worried that if any other objects get involved in step (b) before
    >> (c) is invoked, it might set off a never-ending chain reaction of
    >> fetches, but it might be fun to try!
    >
    > By my reading, the staleness interval is a false hope and doesn't
    > have much to do with getting fresh values from the persistent store.
    > Here is the somewhat cryptic quote from the doco:
    >
    > The staleness interval does NOT affect objects currently in use
    > [?]
    > (that is, it is NOT used to automatically update property values
    > from a persistent store after a certain period of time).
    >
    > And besides, refreshObject: mergeChanges doesn't help me find new
    > objects that have been created behind my back -- which is the gist of
    > my question.
    >
    > Having not had a slam-dunk response to this question, I think I may be
    > reduced to writing SQL -- something I actively avoid.
    >
    > I did try using [context reset].  It actually worked but it caused so
    > many side effects (my dozens of array controllers were eventually only
    > partially filled with data or completely empty) that I recoiled in
    > fright from that for now.  But I may return to it (maybe mass calls to
    > [xyzArrayController fetch] would fix them).
    >
    > Please, if anyone has some experience in this area, I'm all ears.  Mr
    > Bumgarner?
    >
    > Cheers,
    >
    > Steve
    >
  • > My question is, is there any way, short of writing SQL or doing
    > [managedObjectContext reset], that I can force a Sales fetch to go
    > directly to the SQLite database and not use the Sales in memory?

    Executing a fetch request always goes to the database file on disk.
    There are several issues about the visibility of new or change objects
    in successive fetches at the NSManagedObjectContext level.

    First, any pending changes you have will be incorporated into the
    fetch results.  So if you've got unsaved deletions or unsaved
    insertions, those will be merged into the fetch result set.  You can
    use a new, clean context, or -rollback the current context if that's
    not what you want.

    The larger problem is the visibility of changes for objects that
    preexist in the context when you fetch.  Core Data can't know what
    you're doing with those objects, so it caches the results from the
    database, but leaves your NSManagedObjects unchanged.  You can decide
    for yourself how to incorporate the latest data in your graph of
    existing managed objects by calling -refreshObject:mergeChanges: on
    them after fetching.  In a situation where you know the fetch result
    set is for changes (say you received a notification to that effect)
    you could just loop over the array and refresh all the fetched objects
    (but passing a YES flag for any objects that have changes).  If you
    decide not to decide about this problem space, Core Data's merge
    policies will step in when you try to save changes to objects others
    have modified underneath you.  Since conflicts like that tend to be
    rare, it's usually more efficient to roll with it than poll frequently
    for fresh data.

    This problem doesn't affect new objects, since clearly you're not
    using them.  Fetch requests always return new objects.  This is
    probably an issue with notifying the array controllers that their
    content has changed.  Asking the controller to -fetch: should work.  I
    don't have any special insights on that beyond what's in the
    documentation and programming guides.  It may be worth another look at
    that material, as asking the managed object context to -reset is going
    to do something completely different, at a different layer, than
    working with the array controllers.

    - Ben

    p.s.

    You should never call -refreshObject:mergeChanges:NO on an object with
    pending changes.  On objects that do not have pending changes, passing
    the NO flag is much more efficient.

    The staleness interval helps establish a limit to how old a cached row
    can be re-used.  It doesn't change the objects you're actively using,
    but if you refresh them, or pull them into a new context, it will
    affect them.
  • Hi Ian,

    Isn't it ironic that we live only a few kilometers apart but talk only
    via forums on the other side of the world?  I see a coffee, at your
    favorite place in Pymble, in our future.

    Ian Joyner wrote

    > I'm having similar problems as to when CD fetches objects. Have you
    > tried NSObjectController.setUsesLazyFetching: NO,

    No, setUsesLazyFetching escaped me.  It sounds useful.

    But, for the moment, I'm taking ArrayControllers out of the picture
    and just trying to fetch the externally-created record directly.  See
    my conversation with Ben Trumbull for details and code.  Then, later,
    I'll make sure all the dozens of ArrayControllers have the new objects
    too.

    > assuming you are using bindings that is?

    Abusing bindings might be more like it :)

    Soon,

    Steve
  • Hello Ben,

    Thank you for your thoughtful, informative post.  I have some comments
    and questions.

    On Thursday, 27 September 2007 at 1:47 am, Ben Trumbull wrote:

    >> My question is, is there any way, short of writing SQL or doing
    >> [managedObjectContext reset], that I can force a Sales fetch to go
    >> directly to the SQLite database and not use the Sales in memory?
    >
    > Executing a fetch request always goes to the database file on disk.

    I'm not seeing that result.  We are throwing the word 'fetch' around -
    here is what I mean:

        NSManagedObjectContext *context = [self managedObjectContext];
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

        [fetchRequest setEntity: [NSEntityDescription entityForName: @"Sale"
                                            inManagedObjectContext: context]
        ];
        NSError *error = nil;
        NSArray *sales = [context executeFetchRequest: fetchRequest
                                                error: &error];
        [fetchRequest release];

        NSNumber *maxInvoiceNumber =
        [sales valueForKeyPath: @"@max.invoiceNumber"];

    My experiments indicate that that won't immediately find an
    externally-created object unless I precede the above with with

        [context reset];

    or, as you point out, use a clean context.  I may use the clean
    context technique as a fall back but I would like to try to get it
    working with an existing context.

    > any pending changes you have will be incorporated into the fetch
    > results.  So if you've got unsaved deletions or unsaved insertions,
    > those will be merged into the fetch result set.  You can use a new,
    > clean context, or -rollback the current context if that's not what
    > you want.

    No, that's not an issue right now -- I just want to immediately see
    the externally-created object in the least-impacting way.

    > In a situation where you know the fetch result set is for changes
    > (say you received a notification to that effect) you could just loop
    > over the array and refresh all the fetched objects (but passing a
    > YES flag for any objects that have changes).

    That is interesting, though slightly off the intended topic of this,
    admittedly mis-titled, post.  But, what does catch my eye is that
    looping over the array won't notice a new externally-created object.
    Right?

    > If you decide not to decide about this problem space, Core Data's
    > merge policies will step in when you try to save changes to objects
    > others have modified underneath you.  Since conflicts like that tend
    > to be rare, it's usually more efficient to roll with it than poll
    > frequently for fresh data.

    Again, slightly off-topic, but let me comment that I have found Core-
    Data's merge policies to be highly effective and, so far,
    trouble-free.  In fact, using the different merge policies I have been
    able to give one machine a sort of priority over the others in terms
    of its in-memory object graph being holy.

    > This problem doesn't affect new objects, since clearly you're not
    > using them.

    I'm not sure I understand what you are saying there.  Do you mean that
    because I won't have modified new, externally-created objects, there
    won't be merge issues?

    > Fetch requests always return new objects.

    Then I suppose I need to revisit my experiment -- that is not the
    result I (thought I) saw (see above).

    > asking the managed object context to -reset is going to do something
    > completely different, at a different layer, than working with the
    > array controllers.

    Yes, obviously [context reset] is extreme and probably undesirable.
    But, so far, it is the only way that I have been able to immediately
    see a new externally-created object.  And regarding the array
    controllers, I don't think they have any hope of seeing an
    externally-created object without a lot of massaging.  Right?

    > You should never call -refreshObject:mergeChanges:NO on an object with
    > pending changes.  On objects that do not have pending changes, passing
    > the NO flag is much more efficient.

    Gold - slightly off topic, but gold.
    >
    > The staleness interval helps establish a limit to how old a cached row
    > can be re-used.  It doesn't change the objects you're actively using,
    > but if you refresh them, or pull them into a new context, it will
    > affect them.

    I confess to being confused by the staleness interval. The more I read
    about it, the more confused I am.  The more everyone clarifies points
    about it, the more confused I am.  But, I don't want to discuss it now
    -- I will understand it in fine detail at the appropriate time. Once I
    have immediate access to externally-created objects, I will turn my
    focus to staleness, refresh and merging.

    Ben, your observations are helpful and encouraging, thank you. I'm
    ready to tackle this problem afresh.

    Steve
  • Hello Ben,

    Just a quick follow-up.

    On Thursday, 27 September 2007 at 1:47 am, Ben Trumbull wrote:

    >> Executing a fetch request always goes to the database file on disk.

    Of course you are correct.  My experiment was faulty.  Thanks for your
    help and please accept my apologies for wasting badwidth on a non-issue.

    I can now apply all the additional useful information you supplied.

    Best regards to all,

    Steve
previous month september 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today