核心数据表视图控制器的重复条目以及如何使用 NSFetchedResultsController 处理

Posted

技术标签:

【中文标题】核心数据表视图控制器的重复条目以及如何使用 NSFetchedResultsController 处理【英文标题】:Duplicate Entries for A Core Data Table View Controller and How To Handle This With NSFetchedResultsController 【发布时间】:2013-11-15 08:25:25 【问题描述】:

我有一个简单的应用程序,具有以下前提和一些小问题:

我有 4 个选项卡,每个选项卡都有一个显示不同信息的表格视图,每个选项卡都有一个加号按钮,以模态方式连接到同一个视图控制器,允许用户向应用程序添加信息。 用户在视图控制器中添加名称、标题、日期和金额,当他们按下保存时,它会保存到核心数据表视图中。

第一个选项卡在带有标签等的自定义表格单元格中显示上述所有信息(姓名、标题、日期、金额)。效果很好。

第二个选项卡仅显示有关名称的信息,第三个选项卡仅显示标题。这样,用户可以看到第一个选项卡中的所有内容,但只能看到第二个选项卡中的“名称”,他们可以选择收集有关该名称的信息。

当每个名称(或日期或标题)只有一个条目时,该应用运行良好,但当我添加第二个条目时,它会在“名称”选项卡中重复。

例如,如果我有一个 John Smith 的条目,只有一个条目,它会在“名称”选项卡中显示一个条目,当我进入时,它只显示带有 John 姓名的交易( 1 在这种情况下)。都好。

但是,如果我为 John 添加另一个条目(拼写完全相同),它会将其视为单独的条目,并且突然之间,名称选项卡现在有 2 个约翰。

我没有在核心数据模型的属性中放置任何唯一 ID,但是我必须这样做吗,或者我可以使用谓词询问“如果它已经存在,不要显示它两次”名称标签栏?

任何帮助将不胜感激!

编辑:

这是名称选项卡的 fetchRequest:

- (NSFetchedResultsController *)fetchedResultsController

    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
    if (_fetchedResultsController != nil)
    
        return _fetchedResultsController;
    

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Transaction" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"whoBy.name" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];    
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;

正如我们所见,没有谓词或类似的东西。我想要的是一个简单的检查来查看“如果名称存在,不要将它添加两次”到名称表视图。我希望这是有道理的?

【问题讨论】:

在数据库驱动的上下文中使用唯一 ID 始终是一个好主意。也许您可以提供一些获取/插入代码来了解问题所在。 谢谢 MrBr - 问题是我还没有在数据库中提供任何唯一的 ID - 这实际上不是问题,因为我可以重新生成,但我希望使用谓词是一个更简单的方法。我将编辑我的帖子以显示代码 - 谢谢 数据模型是什么样的?此信息(姓名、标题、日期、金额)是在一个表中还是在多个表中? 问题是:你想要模型中有两个实例还是只有一个?您需要先说出您想要的结果,然后我们才能说出如何实现它...... @MarkoNikolovski 感谢您的回复 - 模型是这样的:交易实体,其中包含指向个人(单独实体)、标题(单独实体)和金额(单独实体)的链接。我为其他属性拆分了这样的功能,在某些时候,我确定我需要使用面向对象的方法来确保事务没有添加所有内容,但每个实体都添加了它自己的属性.. 【参考方案1】:

该条目重复,因为在Names 选项卡中,您正在查询Transactions 表,然后获取执行该事务的人的姓名。因为 John 有两笔交易,所以您两次得到他的名字。

试试这样的:

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:managedObjectContext];
fetchRequest.entity = entity;
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:NO];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];

像这样,您应该只获取每个名称(人)一次(假设名称和事务之间的关系是一对多的)。

编辑:每次创建Transaction 时,Person 可能会发生两件事:

它可以是一个新的Person,你应该创建一个新的实体;或者, 这是一个新的Person

逻辑是这样的,你得弄清楚代码:

    1234563 @(当他选择一个人时,你会得到它的NSManagedObjectID)。如果是新的,您就当场创建。

    创建Transaction 对象后,需要将Person 对象添加到其关系中。如果我没看错,你在Transaction 对象中有一个whoBy 关系。将其设置为您在步骤 1 中获得的 Person 对象。

    设置Transaction对象的所有其他字段(金额等)。

    完成所有操作后保存NSManagedObjectContext

您目前在代码中所做的是为每个Transaction 创建一个新的Person,这就是为什么您会看到多个同名条目的原因。

【讨论】:

非常感谢 Marko - 这是有道理的,正如您所提到的,我意识到我的代码存在问题。现在,我有 Add Entry VC 直接添加到每个组件,所以它添加了带有 transaction.name 的 Name,它添加了带有 transaction.amount 的 Amount(或类似的东西)所以我有点让 Transaction 类做所有事情,当我实际上应该把它分开并说,如果我要添加一个名称,请调用 Name 类。我怀疑这会使这种方法更容易。虽然我认为 uniqueID 属性可能很好,因为如果我有两个独立的 John Smith 怎么办? 您不必管理自己的 uniqueID — Core Data 已经为每个 NSManagedObject 管理了一个 uniqueObjectID。创建新的Transaction时,获取Person的uniqueID(假设是现有人的交易)并将其设置为TransactionwhoBy关系。 检查NSManagedObject方法- (NSManagedObjectID *)objectID 是的,最佳方式是让Person 表与Transaction 表具有一对多关系,因此Transaction 仅引用此人,而不保存名称本身。通过您的NSFetchedResultsController 代码,我看到情况已经如此了? 感谢@Marko,objectID 非常有趣——它可能会让生活变得更轻松。我不确定我是否是,基本上当我添加一个条目时,我正在调用 NSManagedObject *transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context]; NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];所以我有点用这种方式创建所有东西,而不是调用特定的 Person 子类等。我不确定我是否需要这样做【参考方案2】:

是的,您可以通过设置告诉 CoreData 您只需要不同的值:

NSFetchRequest *request;
request.propertiesToFetch = [NSArray arrayWithObject:@"Name"];
request.returnsDistinctResults = YES;
request.resultType = NSDictionaryResultType;
...

但是,如果您不希望数据库中有 2 个约翰,那么最好不要将它们放在那里而不是过滤结果。因此,当您插入数据时,请检查您是否已经拥有该条目,如果没有插入(您可以更新该记录)...

【讨论】:

以上是关于核心数据表视图控制器的重复条目以及如何使用 NSFetchedResultsController 处理的主要内容,如果未能解决你的问题,请参考以下文章

如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?

将数据传递给目标视图控制器[重复]

避免在 ios 中的 UITableView 中出现重复条目

如何在第二个视图控制器上显示核心数据?

同时添加核心数据输入和segue

如何在 Swift 中使用区分大小写和不区分大小写的混合字符串避免 Array 中的重复条目?