从现有的数据库 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 关系的主要内容,如果未能解决你的问题,请参考以下文章
从现有的 id_token 获取 Auth0 access_token