如何防止核心数据在iOS 5中重复?

时间:2021-08-09 07:37:54

I've run into a problem.

我遇到了一个问题。

Over the weekend I've been working on a project where I'm pulling a large xml from a webservice.

上周末我一直在做一个项目,我正在从web服务中提取一个大的xml。

It basically has 3 tiers - Clients, Managers, Staff all hierarchical. So the first time the app runs, it pulls this xml and parses it and creates all the entries in the 3 releated Entities - Clients, Managers and Staff.

它基本上有3层 - 客户,经理,员工都是等级的。因此,第一次运行应用程序时,它会提取此xml并对其进行解析,并在3个相关实体 - 客户端,经理和员工中创建所有条目。

Every time the app launches I need to pull that same XML down, but this time, I only need to 'update' any of the existing records that have changed, or add new ones for new clients, managers or staff that have appeared since last time.

每次应用程序启动时,我都需要将相同的XML拉下来,但这一次,我只需要“更新”任何已更改的现有记录,或者为自上次出现以来出现的新客户,经理或员工添加新记录时间。

So - at the moment, as I said, it's pulling it all, parsing it correctly and creating the correct entities and filling in all the attributes.

所以 - 目前,就像我说的那样,它正在全力以赴,正确解析并创建正确的实体并填充所有属性。

However, with no data change, on the 2nd launch it's DUPLICATING all of the data - so instead of 15 clients ( the correct number ) I have 30 and so on...

但是,在没有数据更改的情况下,在第二次启动时它会重复所有数据 - 所以不是15个客户端(正确的数字)我有30个等等......

Do I really have to add lots of code in my parsing to check that instead of creating a new NSManagedObject, I check if it's already there?

我是否真的必须在解析中添加大量代码来检查而不是创建新的NSManagedObject,我检查它是否已经存在?

And if it is - I have to then manually check every attribute?

如果是 - 我必须手动检查每个属性?

That's awfully painful and longwinded - isn't there a way to make Core Data do this kinda stuff for me - automatically?

那是非常痛苦和长篇大论 - 是不是有办法让Core Data自动为我做这样的事情?

Thanks for any help or suggestions.

感谢您的任何帮助或建议。

2 个解决方案

#1


3  

As Stated in Apple Docs https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

正如Apple Docs中所述,https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

You need to loop the data model and handle it from there like this

您需要循环数据模型并从那里处理它

Example:

例:

// loop over employeeIDs
// anID = ... each employeeID in turn
// within body of loop

NSString *predicateString = [NSString stringWithFormat: @"employeeID == %@", anID];

NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];

Personally I do not like this method and I wrote this snippet of code that handles this in a pro-efficient manor and which is straight forward! I noticed with Apples method I ran into issues with strings having different characters such as capitol letters and spaces. Below code is tested and working if you rename all your corresponding objects correctly I honestly believe this is the most efficient way to accomplish not adding duplicates in core data.

就我个人而言,我不喜欢这种方法,而且我写了这段代码,用一个高效的庄园来处理这个问题,这是直截了当的!我注意到使用Apples方法我遇到了具有不同字符的字符串的问题,例如国会大厦字母和空格。如果您正确地重命名所有相应的对象,下面的代码已经过测试和工作我真的相信这是完成不在核心数据中添加重复项的最有效方法。

-(void)AvoidDuplicatesinDataModel
{
    // Define our table/entity to use
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Users"
                                              inManagedObjectContext:managedObjectContext];

    // Setup the fetch request
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];

    // Define how we will sort the records
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"users"
                                                                   ascending:NO];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    [request setSortDescriptors:sortDescriptors];
    [sortDescriptor release];

    // Fetch the records and handle an error
    NSError *Fetcherror;
    NSMutableArray *mutableFetchResults = [[managedObjectContext
                                            executeFetchRequest:request error:&Fetcherror] mutableCopy];

    if (!mutableFetchResults) {
        // Handle the error.
        // This is a serious error
    }

    //here usersNameTextField.text can be any (id) string that you are searching for
    if ([[mutableFetchResults valueForKey:@"users"]
         containsObject:usernameTextField.text]) {
        //Alert user or handle your duplicate methods from here
        return;
    }
}

#2


12  

I fear you have to keep your DB clean by yourself … The easiest way would be using NSFetchRequest: When importing your updated data you can run a query against the existing data and decide what you want to do.

我担心你必须自己保持你的数据库清理...最简单的方法是使用NSFetchRequest:导入更新的数据时,你可以对现有数据运行查询并决定你想做什么。

As Marcus S. Zarra mentioned in another thread about this topic:

正如Marcus S. Zarra在另一个关于这个主题的帖子中提到的:

When you are importing a new row you can run a query against the existing rows to see if it is already in place. To do this you create a NSFetchRequest against your entity, set the predicate to look for the guid property and set the max rows returned to 1.

导入新行时,可以对现有行运行查询,以查看它是否已就位。为此,您需要针对您的实体创建NSFetchRequest,将谓词设置为查找guid属性并将返回的最大行设置为1。

I would recommend keeping this NSFetchRequest around during your import so that you can reuse it while going through the import. If the NSFetchRequest returns a row you can update that row. If it does not return a row then you can insert a new row.

我建议您在导入期间保留此NSFetchRequest,以便在进行导入时可以重复使用它。如果NSFetchRequest返回一行,您可以更新该行。如果它没有返回一行,那么您可以插入一个新行。

When done correctly you will find the performance more than acceptable.

如果操作正确,您会发现性能超出了可接受范围。

Another source for good information are Apples Programming Guides: Core Data Programming Guide

另一个获得良好信息的来源是苹果编程指南:核心数据编程指南

#1


3  

As Stated in Apple Docs https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

正如Apple Docs中所述,https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdImporting.html

You need to loop the data model and handle it from there like this

您需要循环数据模型并从那里处理它

Example:

例:

// loop over employeeIDs
// anID = ... each employeeID in turn
// within body of loop

NSString *predicateString = [NSString stringWithFormat: @"employeeID == %@", anID];

NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];

Personally I do not like this method and I wrote this snippet of code that handles this in a pro-efficient manor and which is straight forward! I noticed with Apples method I ran into issues with strings having different characters such as capitol letters and spaces. Below code is tested and working if you rename all your corresponding objects correctly I honestly believe this is the most efficient way to accomplish not adding duplicates in core data.

就我个人而言,我不喜欢这种方法,而且我写了这段代码,用一个高效的庄园来处理这个问题,这是直截了当的!我注意到使用Apples方法我遇到了具有不同字符的字符串的问题,例如国会大厦字母和空格。如果您正确地重命名所有相应的对象,下面的代码已经过测试和工作我真的相信这是完成不在核心数据中添加重复项的最有效方法。

-(void)AvoidDuplicatesinDataModel
{
    // Define our table/entity to use
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Users"
                                              inManagedObjectContext:managedObjectContext];

    // Setup the fetch request
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    [request setEntity:entity];

    // Define how we will sort the records
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"users"
                                                                   ascending:NO];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    [request setSortDescriptors:sortDescriptors];
    [sortDescriptor release];

    // Fetch the records and handle an error
    NSError *Fetcherror;
    NSMutableArray *mutableFetchResults = [[managedObjectContext
                                            executeFetchRequest:request error:&Fetcherror] mutableCopy];

    if (!mutableFetchResults) {
        // Handle the error.
        // This is a serious error
    }

    //here usersNameTextField.text can be any (id) string that you are searching for
    if ([[mutableFetchResults valueForKey:@"users"]
         containsObject:usernameTextField.text]) {
        //Alert user or handle your duplicate methods from here
        return;
    }
}

#2


12  

I fear you have to keep your DB clean by yourself … The easiest way would be using NSFetchRequest: When importing your updated data you can run a query against the existing data and decide what you want to do.

我担心你必须自己保持你的数据库清理...最简单的方法是使用NSFetchRequest:导入更新的数据时,你可以对现有数据运行查询并决定你想做什么。

As Marcus S. Zarra mentioned in another thread about this topic:

正如Marcus S. Zarra在另一个关于这个主题的帖子中提到的:

When you are importing a new row you can run a query against the existing rows to see if it is already in place. To do this you create a NSFetchRequest against your entity, set the predicate to look for the guid property and set the max rows returned to 1.

导入新行时,可以对现有行运行查询,以查看它是否已就位。为此,您需要针对您的实体创建NSFetchRequest,将谓词设置为查找guid属性并将返回的最大行设置为1。

I would recommend keeping this NSFetchRequest around during your import so that you can reuse it while going through the import. If the NSFetchRequest returns a row you can update that row. If it does not return a row then you can insert a new row.

我建议您在导入期间保留此NSFetchRequest,以便在进行导入时可以重复使用它。如果NSFetchRequest返回一行,您可以更新该行。如果它没有返回一行,那么您可以插入一个新行。

When done correctly you will find the performance more than acceptable.

如果操作正确,您会发现性能超出了可接受范围。

Another source for good information are Apples Programming Guides: Core Data Programming Guide

另一个获得良好信息的来源是苹果编程指南:核心数据编程指南