从现有的数据库 ID 关系加载 Core Data 关系

Posted

技术标签:

【中文标题】从现有的数据库 ID 关系加载 Core Data 关系【英文标题】:Loading Core Data relationships from existing database ID relationships 【发布时间】:2014-01-30 23:27:15 【问题描述】:

我有一个应用程序,它在表格中包含大量数据,我正在编写一个获取一小部分数据的移动应用程序。表中的数据使用 ID 来表示关系,我正在尝试将其加载到 Core Data 模型中并保留关系。

但似乎没有一种简单的方法可以告诉 Core Data,“对于这个关系,我想要另一个表的外键。”相反,我有这个怪物(这是六个异步 RestKit 查询中的一个的简化版本,这些查询将返回填充数据库):

[manager postObject:nil path:@"api/shiftservice/get" parameters:@@"workerIds": @[@1] success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) 
    NSLog(@"Loaded shifts: %d", [[mappingResult array] count]);
    NSManagedObjectContext *context = [AppDelegate managedObjectContext];

    NSSet *positions = [context fetchObjectsForEntityName:@"NWPosition"];
    NSDictionary *positionDict = [NSMutableDictionary new];
    for (NWPosition *position in positions) [positionDict setValue:position forKey:position.positionId.stringValue];

    NSSet *workers = [context fetchObjectsForEntityName:@"NWWorker"];
    NSDictionary *workerDict = [NSMutableDictionary new];
    for (NWWorker *worker in workers) [workerDict setValue:worker forKey:worker.workerId.stringValue];

    NSSet *shifts = [context fetchObjectsForEntityName:@"NWShift"];
    for (NWShift *shift in shifts)
    
        NWPosition *position = [positionDict valueForKey:shift.positionId.stringValue];
        position.shifts = [context fetchObjectsForEntityName:@"NWShift" withPredicateFormat:@"positionId = %d", position.positionId];
        shift.position = position;

        NSSet *tradesAsGetShift = [context fetchObjectsForEntityName:@"NWTrade" withPredicateFormat:@"getShiftId = %@", shift.shiftId];
        for (NWTrade *trade in tradesAsGetShift) trade.getShift = shift;
        shift.tradesAsGetShift = tradesAsGetShift;

        NSSet *tradesAsGiveShift = [context fetchObjectsForEntityName:@"NWTrade" withPredicateFormat:@"giveShiftId = %@", shift.shiftId];
        for (NWTrade *trade in tradesAsGiveShift) trade.giveShift = shift;
        shift.tradesAsGiveShift = tradesAsGiveShift;

        NWWorker *worker = [workerDict valueForKey:shift.workerId.stringValue];
        worker.shifts = [context fetchObjectsForEntityName:@"NWShift" withPredicateFormat:@"workerId = %d", worker.workerId];
        shift.worker = worker;
    
 failure:^(RKObjectRequestOperation *operation, NSError *error) 
    NSLog(@"Failed to load shifts with error: %@", [error localizedDescription]);
];

我在 http://www.cocoawithlove.com/2008/03/core-data-one-line-fetch.html 使用 Matt Gallagher 的 One-Line Fetch 的修改版本作为 fetchObjectsForEntityName

无论如何,这对我来说似乎很可怕,就像我错过了一些明显的东西。有没有办法告诉 Core Data 数据库风格的外键?如果没有,是否有一种简单的方法来填充它们?因为为每一个实体做这么多的获取肯定看起来不是正确的方法。如果 RestKit 在这方面有所帮助,那就更好了。

【问题讨论】:

RestKit 应该处理外键(Core Data 没有)。显示您收到的 JSON 和您拥有的映射。您是否在映射中添加了外键关系? 【参考方案1】:

不,但应该不需要设置反向关系。

【讨论】:

【参考方案2】:

感谢 Wain 和他富有洞察力的评论,我设法让 RestKit 以一种相当优雅的方式为我做这件事。这是我设置外键关系的方法。我设置了每个关系的两端,以便任何一组数据都可以先进入,并且关系仍然可以正确填充。

首先,我设置了管理器,告诉它使用 JSON 序列化:

RKManagedObjectStore *store = [AppDelegate managedObjectStore];

RKObjectManager *manager = [[RKObjectManager alloc] initWithHTTPClient:oauthClient];
[RKObjectManager setSharedManager:manager];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:RKMIMETypeJSON];
[manager.HTTPClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[manager registerRequestOperationClass:[AFJSONRequestOperation class]];
[manager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[manager setRequestSerializationMIMEType:RKMIMETypeJSON];
manager.managedObjectStore = store;

接下来,我设置连接和响应描述符:

NSManagedObjectContext *context = [AppDelegate managedObjectContext];
NSDictionary *orgRels = [[NSEntityDescription entityForName:@"NWOrg" inManagedObjectContext:context] relationshipsByName];
NSDictionary *positionRels = [[NSEntityDescription entityForName:@"NWPosition" inManagedObjectContext:context] relationshipsByName];
// ...

NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);

NSArray *orgConnections =
@[[self connectionForRelation:@"positions" localKey:@"orgId" foreignKey:@"orgId" withRels:orgRels],
  [self connectionForRelation:@"workers" localKey:@"orgId" foreignKey:@"orgId" withRels:orgRels]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:
    [self entityMappingForName:@"NWOrg" fromDictionary:@@"orgId": @"orgId", @"orgName": @"orgName" withKey:@"orgId" andConnections:orgConnections]
    method:RKRequestMethodAny pathPattern:@"api/orgservice/get" keyPath:nil statusCodes:statusCodes]];

NSArray *positionConnections =
@[[self connectionForRelation:@"org" localKey:@"orgId" foreignKey:@"orgId" withRels:positionRels],
  [self connectionForRelation:@"shifts" localKey:@"positionId" foreignKey:@"positionId" withRels:positionRels]];
[manager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:
    [self entityMappingForName:@"NWPosition" fromDictionary:@@"positionId": @"positionId", @"orgId": @"orgId", @"name": @"positionName" withKey:@"positionId" andConnections:positionConnections]
    method:RKRequestMethodAny pathPattern:@"api/positionservice/get" keyPath:nil statusCodes:statusCodes]];

// ...

然后,我能够完成简单的请求,并且效果很好:

[manager postObject:nil path:@"api/shiftservice/get" parameters:@@"workerIds": @[@1]
    success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) 
    NSLog(@"Loaded shifts: %d", [[mappingResult array] count]);
 failure:^(RKObjectRequestOperation *operation, NSError *error) 
    NSLog(@"Failed to load shifts with error: %@", [error localizedDescription]);
];

注意:这使用了一些辅助方法,我将在此处重现:

- (RKConnectionDescription *)connectionForRelation:(NSString *)relation
    localKey:(NSString *)localKey foreignKey:(NSString *)foreignKey 
    withRels:(NSDictionary *)rels

    return [[RKConnectionDescription alloc] initWithRelationship:rels[relation]
        attributes:@localKey: foreignKey];


- (RKEntityMapping *)entityMappingForName:(NSString *)name
    fromDictionary:(NSDictionary *)dict withKey:(NSString *)key

    RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:name
        inManagedObjectStore:[AppDelegate managedObjectStore]];
    [mapping addAttributeMappingsFromDictionary:dict];
    mapping.assignsDefaultValueForMissingAttributes = YES;
    mapping.assignsNilForMissingRelationships = YES;
    mapping.identificationAttributes = @[key];

    return mapping;


- (RKEntityMapping *)entityMappingForName:(NSString *)name
    fromDictionary:(NSDictionary *)dict withKey:(NSString *)key
    andConnections:(NSArray *)connections

    RKEntityMapping *mapping = [self entityMappingForName:name
        fromDictionary:dict withKey:key];
    for (RKConnectionDescription *connection in connections)
    
        [mapping addConnection:connection];
    
    return mapping;

【讨论】:

以上是关于从现有的数据库 ID 关系加载 Core Data 关系的主要内容,如果未能解决你的问题,请参考以下文章

从现有的 MS Access 表自动生成 SQL

tcpdf - 从现有的 PDF 文档开始

从现有的 id_token 获取 Auth0 access_token

Core Data 复杂关系

Core-Data:想要将新的 web xml 内容持久保存到我的数据存储中,而不是替换现有的

从现有的创建新的Kendo UI DataSource