[Leopard] Core Date migration lacks custom object classes

  • Hi!

    I just noticed that during migration, Core Data maps all entities to
    the NSManagedObject class rather than using the specified custom class.

    This causes me problems as my awake and validation methods are not
    called and thus leave mandatory attributes with nil values.

    It also seems very wrong to me that when I create and insert instances
    of my custom managed object classes within my custom migration policy,
    the
    initWithEntity:insertIntoManagedObjectContext: returns an instance of
    NSManagedObject.

    One can witness the problem using a custom migration policy:

    @implementation ColumnAttributeMigrationPolicy

    - (BOOL)beginEntityMapping:(NSEntityMapping *)mapping manager:
    (NSMigrationManager *)manager error:(NSError **)error
    {
    NSManagedObjectContext *sourceContext = [manager sourceContext];
    NSManagedObjectContext *destinationContext = [manager
    destinationContext];

    NSEntityDescription *sourceEntity = [NSEntityDescription
    entityForName:@"ColumnAttribute" inManagedObjectContext:sourceContext];
    NSEntityDescription *destinationEntity = [NSEntityDescription
    entityForName:@"ColumnAttribute"
    inManagedObjectContext:destinationContext];

    NSLog(@"Source class: %@", [sourceEntity managedObjectClassName]);
    NSLog(@"Destination class: %@", [destinationEntity
    managedObjectClassName]);

    return YES;
    }

    @end

    This prints out the following:

    2007-11-06 15:17:33.872 HoudahSpot[1812:10b] Source class: (null)
    2007-11-06 15:17:33.873 HoudahSpot[1812:10b] Destination class:
    NSManagedObject

    Best,
    Pierre Bernard
    Houdah Software s.à r.l.

    ---
    Pierre Bernard
    http://www.bernard-web.com/pierre
    http://www.houdah.com
  • On Nov 6, 2007, at 6:23 AM, Pierre Bernard wrote:

    > Hi!
    >
    > I just noticed that during migration, Core Data maps all entities to
    > the NSManagedObject class rather than using the specified custom
    > class.
    >
    > This causes me problems as my awake and validation methods are not
    > called and thus leave mandatory attributes with nil values.

    This is by design and documented here: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreDataVersionin
    g/Articles/vmMigrationProcess.html


    The migration process itself is in three stages. It uses a copy of the
    source and destination models in which the validation rules are
    disabled and the class of all entities is changed to NSManagedObject.

    Generally speaking, the data migration process has different needs
    than the normal application usage of that data and you wouldn't want
    or expect application-oriented behaviors during migration.  By using
    generic managed objects during data migration there's no risk of
    unintended side effects during data reading and writing.

    Migration is handled as a 3-stage process where source instances are
    first migrated to destination instances with only attribute values,
    then relationships in the second phase (so that at the relationship
    migration phase you can reliably access both source and destination
    instances' attribute values), and then lastly the model validation
    rules are used to ensure the validity of the migrated objects before
    they are saved to the destination store.

    Use a custom entity migration policy and override
    createDestinationInstancesForSourceInstance:entityMapping:manager:error
    : to populate any derived attributes in your destination objects (but
    only if you need them for the migration).

    Hope that helps.

    - adam

    > It also seems very wrong to me that when I create and insert
    > instances of my custom managed object classes within my custom
    > migration policy, the
    > initWithEntity:insertIntoManagedObjectContext: returns an instance
    > of NSManagedObject.
    >
    > One can witness the problem using a custom migration policy:
    >
    > @implementation ColumnAttributeMigrationPolicy
    >
    > - (BOOL)beginEntityMapping:(NSEntityMapping *)mapping manager:
    > (NSMigrationManager *)manager error:(NSError **)error
    > {
    > NSManagedObjectContext *sourceContext = [manager sourceContext];
    > NSManagedObjectContext *destinationContext = [manager
    > destinationContext];
    >
    > NSEntityDescription *sourceEntity = [NSEntityDescription
    > entityForName:@"ColumnAttribute"
    > inManagedObjectContext:sourceContext];
    > NSEntityDescription *destinationEntity = [NSEntityDescription
    > entityForName:@"ColumnAttribute"
    > inManagedObjectContext:destinationContext];
    >
    > NSLog(@"Source class: %@", [sourceEntity managedObjectClassName]);
    > NSLog(@"Destination class: %@", [destinationEntity
    > managedObjectClassName]);
    >
    > return YES;
    > }
    >
    > @end
    >
    > This prints out the following:
    >
    > 2007-11-06 15:17:33.872 HoudahSpot[1812:10b] Source class: (null)
    > 2007-11-06 15:17:33.873 HoudahSpot[1812:10b] Destination class:
    > NSManagedObject
    >
    > Best,
    > Pierre Bernard
    > Houdah Software s.à r.l.
    >
    > ---
    > Pierre Bernard
    > http://www.bernard-web.com/pierre
    > http://www.houdah.com
  • Hi Adam!

    Thanks for the clarification. I have read the documentation several
    times, but I missed that bit. In my mind this applied only for the
    source model.

    I find the choice to disable custom entities on the destination model
    a bit odd. This will lead to business code being duplicated between
    the MO and the custom policy.

    I find it inconvenient in two situations:

    - My entity persists a derived attribute. This attribute is refreshed
    in awakeFromFetch and in the setter of the attribute it depends on.
    Migration forgoes this mechanism.
    - In my custom migration policy I use my custom classes. Given the
    fact that initWithEntity:insertIntoManagedObjectContext: returns a
    NSManagedObject creates the risk of runtime errors when calling
    methods declared on the custom class.

    Pierre

    On Nov 6, 2007, at 8:39 PM, Adam Swift wrote:

    >
    > On Nov 6, 2007, at 6:23 AM, Pierre Bernard wrote:
    >
    >> Hi!
    >>
    >> I just noticed that during migration, Core Data maps all entities
    >> to the NSManagedObject class rather than using the specified custom
    >> class.
    >>
    >> This causes me problems as my awake and validation methods are not
    >> called and thus leave mandatory attributes with nil values.
    >
    > This is by design and documented here: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreDataVersionin
    g/Articles/vmMigrationProcess.html

    >
    > The migration process itself is in three stages. It uses a copy of
    > the source and destination models in which the validation rules are
    > disabled and the class of all entities is changed to NSManagedObject.
    >
    > Generally speaking, the data migration process has different needs
    > than the normal application usage of that data and you wouldn't want
    > or expect application-oriented behaviors during migration.  By using
    > generic managed objects during data migration there's no risk of
    > unintended side effects during data reading and writing.
    >
    > Migration is handled as a 3-stage process where source instances are
    > first migrated to destination instances with only attribute values,
    > then relationships in the second phase (so that at the relationship
    > migration phase you can reliably access both source and destination
    > instances' attribute values), and then lastly the model validation
    > rules are used to ensure the validity of the migrated objects before
    > they are saved to the destination store.
    >
    > Use a custom entity migration policy and override
    > createDestinationInstancesForSourceInstance:entityMapping:manager:error
    > : to populate any derived attributes in your destination objects
    > (but only if you need them for the migration).
    >
    > Hope that helps.
    >
    > - adam
    >
    >> It also seems very wrong to me that when I create and insert
    >> instances of my custom managed object classes within my custom
    >> migration policy, the
    >> initWithEntity:insertIntoManagedObjectContext: returns an instance
    >> of NSManagedObject.
    >>
    >> One can witness the problem using a custom migration policy:
    >>
    >> @implementation ColumnAttributeMigrationPolicy
    >>
    >> - (BOOL)beginEntityMapping:(NSEntityMapping *)mapping manager:
    >> (NSMigrationManager *)manager error:(NSError **)error
    >> {
    >> NSManagedObjectContext *sourceContext = [manager sourceContext];
    >> NSManagedObjectContext *destinationContext = [manager
    >> destinationContext];
    >>
    >> NSEntityDescription *sourceEntity = [NSEntityDescription
    >> entityForName:@"ColumnAttribute"
    >> inManagedObjectContext:sourceContext];
    >> NSEntityDescription *destinationEntity = [NSEntityDescription
    >> entityForName:@"ColumnAttribute"
    >> inManagedObjectContext:destinationContext];
    >>
    >> NSLog(@"Source class: %@", [sourceEntity managedObjectClassName]);
    >> NSLog(@"Destination class: %@", [destinationEntity
    >> managedObjectClassName]);
    >>
    >> return YES;
    >> }
    >>
    >> @end
    >>
    >> This prints out the following:
    >>
    >> 2007-11-06 15:17:33.872 HoudahSpot[1812:10b] Source class: (null)
    >> 2007-11-06 15:17:33.873 HoudahSpot[1812:10b] Destination class:
    >> NSManagedObject
    >>
    >> Best,
    >> Pierre Bernard
    >> Houdah Software s.à r.l.
    >>
    >> ---
    >> Pierre Bernard
    >> http://www.bernard-web.com/pierre
    >> http://www.houdah.com
    >

    ---
    Pierre Bernard
    http://www.bernard-web.com/pierre
    http://www.houdah.com
  • On Nov 6, 2007, at 12:09 PM, Pierre Bernard wrote:

    > Hi Adam!
    >
    > Thanks for the clarification. I have read the documentation several
    > times, but I missed that bit. In my mind this applied only for the
    > source model.
    >
    > I find the choice to disable custom entities on the destination
    > model a bit odd. This will lead to business code being duplicated
    > between the MO and the custom policy.

    To avoid the code duplication problem, you can move the logic to class
    methods (or utility functions) which both the custom class instances
    and migration policies utilize.

    >
    > I find it inconvenient in two situations:
    >
    > - My entity persists a derived attribute. This attribute is
    > refreshed in awakeFromFetch and in the setter of the attribute it
    > depends on. Migration forgoes this mechanism.

    >
    > - In my custom migration policy I use my custom classes. Given the
    > fact that initWithEntity:insertIntoManagedObjectContext: returns a
    > NSManagedObject creates the risk of runtime errors when calling
    > methods declared on the custom class.

    While it's true that there may be some inconvenience in having to re-
    factor the logic in your custom classes to avoid code duplication, my
    personal experience has been that it's usually worth re-examining
    custom MO logic in light of the different needs of data migration.

    That being said, store data migration is a complex issue, there's
    certainly ways to make it work better and we really welcome your
    feedback!

    > Pierre
    >
    > On Nov 6, 2007, at 8:39 PM, Adam Swift wrote:
    >
    >>
    >> On Nov 6, 2007, at 6:23 AM, Pierre Bernard wrote:
    >>
    >>> Hi!
    >>>
    >>> I just noticed that during migration, Core Data maps all entities
    >>> to the NSManagedObject class rather than using the specified
    >>> custom class.
    >>>
    >>> This causes me problems as my awake and validation methods are not
    >>> called and thus leave mandatory attributes with nil values.
    >>
    >> This is by design and documented here: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreDataVersionin
    g/Articles/vmMigrationProcess.html

    >>
    >> The migration process itself is in three stages. It uses a copy of
    >> the source and destination models in which the validation rules are
    >> disabled and the class of all entities is changed to NSManagedObject.
    >>
    >> Generally speaking, the data migration process has different needs
    >> than the normal application usage of that data and you wouldn't
    >> want or expect application-oriented behaviors during migration.  By
    >> using generic managed objects during data migration there's no risk
    >> of unintended side effects during data reading and writing.
    >>
    >> Migration is handled as a 3-stage process where source instances
    >> are first migrated to destination instances with only attribute
    >> values, then relationships in the second phase (so that at the
    >> relationship migration phase you can reliably access both source
    >> and destination instances' attribute values), and then lastly the
    >> model validation rules are used to ensure the validity of the
    >> migrated objects before they are saved to the destination store.
    >>
    >> Use a custom entity migration policy and override
    >> createDestinationInstancesForSourceInstance:entityMapping:manager:error
    >> : to populate any derived attributes in your destination objects
    >> (but only if you need them for the migration).
    >>
    >> Hope that helps.
    >>
    >> - adam
    >>
    >>> It also seems very wrong to me that when I create and insert
    >>> instances of my custom managed object classes within my custom
    >>> migration policy, the
    >>> initWithEntity:insertIntoManagedObjectContext: returns an instance
    >>> of NSManagedObject.
    >>>
    >>> One can witness the problem using a custom migration policy:
    >>>
    >>> @implementation ColumnAttributeMigrationPolicy
    >>>
    >>> - (BOOL)beginEntityMapping:(NSEntityMapping *)mapping manager:
    >>> (NSMigrationManager *)manager error:(NSError **)error
    >>> {
    >>> NSManagedObjectContext *sourceContext = [manager sourceContext];
    >>> NSManagedObjectContext *destinationContext = [manager
    >>> destinationContext];
    >>>
    >>> NSEntityDescription *sourceEntity = [NSEntityDescription
    >>> entityForName:@"ColumnAttribute"
    >>> inManagedObjectContext:sourceContext];
    >>> NSEntityDescription *destinationEntity = [NSEntityDescription
    >>> entityForName:@"ColumnAttribute"
    >>> inManagedObjectContext:destinationContext];
    >>>
    >>> NSLog(@"Source class: %@", [sourceEntity managedObjectClassName]);
    >>> NSLog(@"Destination class: %@", [destinationEntity
    >>> managedObjectClassName]);
    >>>
    >>> return YES;
    >>> }
    >>>
    >>> @end
    >>>
    >>> This prints out the following:
    >>>
    >>> 2007-11-06 15:17:33.872 HoudahSpot[1812:10b] Source class: (null)
    >>> 2007-11-06 15:17:33.873 HoudahSpot[1812:10b] Destination class:
    >>> NSManagedObject
    >>>
    >>> Best,
    >>> Pierre Bernard
    >>> Houdah Software s.à r.l.
    >>>
    >>> ---
    >>> Pierre Bernard
    >>> http://www.bernard-web.com/pierre
    >>> http://www.houdah.com
    >>
    >
    > ---
    > Pierre Bernard
    > http://www.bernard-web.com/pierre
    > http://www.houdah.com
previous month november 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