将多对多关系迁移到Core Data中的连接表

bry*_*ark 11 iphone core-data core-data-migration ios simperium

我有一个iPhone应用程序,它使用多对多关系将标签和注释链接在一起.我目前正在使用Core Data的"关系"功能来完成此任务,但我想迁移到使用连接表.

这是我的挑战:我想从旧模型迁移到连接表模型,我需要弄清楚如何执行数据迁移.

有什么好的例子说明如何做到这一点?

更新:我在这里澄清我的问题,以帮助解决这里发生的事情:我想尝试使用Simperium来支持我们的应用程序,但是Simperium不支持多对多关系(!).

作为我正在尝试做的一个例子,让我们以iPhoneCoreDataRecipes应用程序为例.

以下是我的核心数据方案目前的类似内容: 在此输入图像描述

......这就是我要过渡到的地方: 在此输入图像描述

我如何从一个到另一个,并带来数据?

Apple的Core Data Migration文档非常稀疏,我没有看到任何有用的演练来使用NSEntityMapping或NSMigrationManager子类来完成工作.

小智 25

这是基本过程:

  1. 创建数据模型的版本化副本.(选择模型,然后选择编辑器 - >添加模型版本)

  2. 对数据模型的新副本进行更改

  3. 将新数据模型的副本标记为当前版本.(单击顶级xcdatamodel项,然后在文件检查器中将"Versioned Data Model"部分下的"Current"条目设置为您在步骤1中创建的新数据模型.

  4. 更新模型对象以添加RecipeIngredient实体.同时使用您在步骤2中为RecipeIngredient实体创建的新关系替换配方和成分实体上的成分和配方关系.(两个实体都添加了这个关系.我称之为recipeIngredients)显然,无论你在旧代码中创建从feed到recipe的关系,你现在都需要创建一个RecipeIngredient对象..但这超出了这个答案的范围.

  5. 在模型之间添加一个新的映射(文件 - >新文件...->(核心数据部分) - >映射模型.这将为您自动生成几个映射.RecipeToRecipe,IngredientToIngredient和RecipeIngredient.

  6. 删除RecipeIngredient映射.同时删除它为RecipeToRecipe和IngredientToRecipe(或在步骤2中称为它们的任何内容)提供的recipeIngredient关系映射.

  7. 将RecipeToRecipe Mapping拖动到映射规则列表中的最后一个.(这很重要,因此我们确保在食谱之前迁移成分,以便我们可以在迁移食谱时将它们链接起来.)迁移将按迁移规则列表的顺序进行.

  8. 为RecipeToRecipe映射设置自定义策略"DDCDRecipeMigrationPolicy"(这将覆盖Recipes对象的自动迁移,并为我们提供一个可以执行映射逻辑的钩子.

  9. 通过为配方子类化NSEntityMigrationPolicy创建DDCDRecipeMigrationPolicy以覆盖createDestinationInstancesForSourceInstance(请参阅下面的代码).这将为每个食谱调用一次,这将让我们创建食谱对象,以及将它链接到成分的相关RecipeIngredient对象.我们将在步骤5中通过Xcode自动为我们创建的映射规则自动迁移Ingredient.

  10. 无论您在何处创建持久对象存储(可能是AppDelegate),请确保将用户字典设置为自动迁移数据模型:

if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
      configuration:nil 
      URL:storeURL 
      options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,  nil] 
      error:&error])
{
}
Run Code Online (Sandbox Code Playgroud)

食谱的子类NSEntityMigrationPolicy

#import <CoreData/CoreData.h>
@interface DDCDRecipeMigrationPolicy : NSEntityMigrationPolicy
@end
Run Code Online (Sandbox Code Playgroud)

*覆盖DDCDRecipeMigrationPolicy.m中的createDestinationInstancesForSourceInstance*

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError **)error
{

    NSLog(@"createDestinationInstancesForSourceInstance : %@", sInstance.entity.name);

   //We have to create the recipe since we overrode this method. 
   //It's called once for each Recipe.  
    NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:[manager destinationContext]];
    [newRecipe setValue:[sInstance valueForKey:@"name"] forKey:@"name"];
    [newRecipe setValue:[sInstance valueForKey:@"overview"] forKey:@"overview"];
    [newRecipe setValue:[sInstance valueForKey:@"instructions"] forKey:@"instructions"];

    for (NSManagedObject *oldIngredient in (NSSet *) [sInstance valueForKey:@"ingredients"])
    {
        NSFetchRequest *fetchByIngredientName = [NSFetchRequest fetchRequestWithEntityName:@"Ingredient"];
        fetchByIngredientName.predicate = [NSPredicate predicateWithFormat:@"name = %@",[oldIngredient valueForKey:@"name"]];

        //Find the Ingredient in the new Datamodel.  NOTE!!!  This only works if this is the second entity migrated.
         NSArray *newIngredientArray = [[manager destinationContext] executeFetchRequest:fetchByIngredientName error:error];

        if (newIngredientArray.count == 1)
        {
             //Create an intersection record. 
            NSManagedObject *newIngredient = [newIngredientArray objectAtIndex:0];
            NSManagedObject *newRecipeIngredient = [NSEntityDescription insertNewObjectForEntityForName:@"RecipeIngredient" inManagedObjectContext:[manager destinationContext]];
            [newRecipeIngredient setValue:newIngredient forKey:@"ingredient"];
            [newRecipeIngredient setValue:newRecipe forKey:@"recipe"];
             NSLog(@"Adding migrated Ingredient : %@ to New Recipe %@", [newIngredient valueForKey:@"name"], [newRecipe valueForKey:@"name"]);
        }


    }

    return YES;
}
Run Code Online (Sandbox Code Playgroud)

我在Xcode和示例Xcode项目中发布了一个设置图片,但我似乎没有任何关于堆栈溢出的声誉点......所以它不会让我.我也会把这个发布到我的博客上.bingosabi.wordpress.com/.

另请注意,Xcode核心数据模型映射的东西有点片状,偶尔需要一个"干净",良好的Xcode rester,模拟器弹跳或上述所有工作.

  • 我在这里有点怀疑,看来你在使用时也在这段代码中设置关系:[newRecipeIngredient setValue:newRecipe forKey:@"recipe"]; 不是这个方法应该只创建实例,而不是设置关系? (2认同)