我正在从一生的SQL到CoreData进行心理转换,并且不是没有几点打嗝。目前正在踢我的是创建一个自引用对象。请允许我解释一下。
使用SQL术语,我有一个包含进程中步骤的表。为了简单起见,我会说该表包含两条信息 - 步骤的名称和它后面的步骤(可能是也可能不是表格中的下一条记录)。该过程中的所有步骤都存储在单个表中。每个步骤都将始终有一个步骤。但并非所有步骤都有一个步骤。
换句话说,它看起来应该是这样的:
desired outcome http://justinwhitney.com/images/desiredOutcome.png
在SQL世界中,我会创建一个带有标识字段的单个表,名称和引用其自己的标识字段的外键(我猜这将是一个国内密钥?),因此:
SQL version http://justinwhitney.com/images/usingSQL.png
但是,对于关系,没有身份字段这样的东西。也没有我可以创建的选择查询来提取我需要的信息。
那么创建实体做同样事情的最佳方法是什么?我尝试创建一个与自身相反的关系,结果证明是一个难以调试的灾难。还有什么其他选择?
谢谢!
答案 0 :(得分:3)
从实体到自身创建关系“nextStep”。那你就可以了 做点什么
// Create first thing:
Thing *thingA = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:context];
thingA.name = @"...";
// Create second thing:
Thing *thingB = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:context];
thingB.name = @"...";
// Establish the relationship between these two objects:
thingA.nextStep = thingB;
“nextStep”也应该有“反向关系”。由于两个或更多的对象 可以有相同的后继者(在你的情况下,“C”和“D”都指向“E”), 反向关系将是“多对多”关系,并且可以被调用 “previousSteps”或类似的。
在核心日期模型编辑器中,这看起来像:
答案 1 :(得分:3)
啊!你提供了至关重要的线索,马丁。
我尝试了代码示例,但这并不是很有效。它最终创建了所有内容的副本,因为thingA和thingB都插入到表中。然而,该图实际上给了我我认为可能是关键。以前我曾尝试将nextStep指定为自己的反向关系,这只是疯子。但是,只需添加previousSteps并将其设置为Many,而nextStep设置为One,这似乎已导致解决方案。
以下是我最终为关系创建的内容:
这是我用来填充Steps实体的plist(打算在首次使用时运行或重置数据库时):
DefaultSteps.plist http://justinwhitney.com/images/DefaultSteps_plist.png
现在这是实体人口常规。这就是昨天绊倒我的事情:
- (IBAction)populateSteps:(UIButton *)sender {
NSString *responseString;
BOOL isStepsPopulated = [[UserPrefs loadUserData:@"didPopulateSteps"] boolValue];
if (!isStepsPopulated) {
NSDictionary *stepDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"DefaultSteps" ofType:@"plist"]];
NSArray *stepArray = [stepDict objectForKey:@"Steps"];
//1
__block NSMutableDictionary *stepObjectDict = [[NSMutableDictionary alloc] initWithCapacity:[stepArray count]];
//2
[stepArray enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
//3
Steps *thisStep = [NSEntityDescription insertNewObjectForEntityForName:@"Steps" inManagedObjectContext:self.managedContext];
thisStep.stepName = [dict objectForKey:@"StepName"];
//4
[stepObjectDict setObject:thisStep forKey:thisStep.stepName];
}];
//5
[stepArray enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
Steps *thisStep = [stepObjectDict objectForKey:[dict objectForKey:@"StepName"]];
Steps *nextStep = [stepObjectDict objectForKey:[dict objectForKey:@"NextStep"]];
thisStep.nextStep = nextStep;
}];
NSError *error = nil;
[self.managedContext save:&error];
if (error) {
responseString = [NSString stringWithFormat:@"Error populating Steps: %@", error.description];
} else {
responseString = @"Steps have been populated.";
[UserPrefs saveUserData:[NSNumber numberWithBool:TRUE] forKey:@"didPopulateSteps"];
}
} else {
responseString = @"Steps already populated.";
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Populate Steps" message:responseString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
}
这是关键。在(1)创建一个NSMutableDictionary来保存迭代的结果,使用stepName作为Key,以便稍后可以引用它。
虽然(2)通过plist内容进行枚举,但(3)就像你一样创建第一个NSEntityDescription。但不是创建第二个,(4)将其添加到字典中。
初始枚举完成后,(5)再次返回。但这次,通过引用原始对象本身来创建关系。有意义吗?
之后,正常保存上下文。在这种情况下,结果是5条记录,每条记录引用同一实体中的另一条记录,没有冲突或重复。
Final Result http://justinwhitney.com/images/FinalResult.png
现在重要的问题是:我如何将其标记为已回答?谁得到了复选标记?