创建新对象时查询特定属性的核心数据,如果存在则返回对象,如果不存在则创建新对象 [英] Querying Core Data for Specific Attributes when Creating New Objects and returning the object if it exists, or creating a new one if it does not

查看:24
本文介绍了创建新对象时查询特定属性的核心数据,如果存在则返回对象,如果不存在则创建新对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在创建新对象之前(通过谓词)检查实体的特定属性是否存在于核心数据数据库中时遇到问题;如果对象存在,我宁愿返回它而不是创建一个新对象.

I have a problem checking whether a particular attribute of an Entity exists in the Core Data Database (through predicates) before creating a new object; if the object exists, I'd rather return it than create a new object.

我有一个简单的应用程序,它有一个表格视图,导航栏中有一个加号按钮;用户单击它并显示一个带有 4 个文本字段的视图控制器.他们填写该信息,按保存,然后将其保存到 Core Data 并显示在 TableView 中(通过使用 NSFetchedResultsControllers).

I have a simple App which has a table view with a plus button in the Navigation Bar; the user clicks that and is presented with a View Controller with 4 text fields. They fill in that information, press save and it gets saved to Core Data and displayed in the TableView (through the use of NSFetchedResultsControllers).

数据模型如下:

具有 isReceived BOOL 属性的交易实体具有名称字符串属性的人员实体具有标题字符串属性的场合实体具有金额字符串属性的项目实体

Transaction Entity with isReceived BOOL attribute Person Entity with name string attribute Occasion Entity with title string attribute Item Entity with amount string attribute

事务与Person(whoBy)、Occasion(Occasion)和Item实体有关系.

The transaction has a relationship to the Person (whoBy), Occasion (Occasion) and Item entities.

在带有 save 方法的视图控制器中,我有下面的代码将新对象插入到事务、人员、场合实体等中.每个事务当然是唯一的,但是对于每个事务,用户可以选择一个现有的人和/或场合,如果那个人不存在,它将被创建(与场合一样).

In the view controller with the save method, I have the code below which will insert new objects into the Transaction, Person, Occasion Entities, etc. Each Transaction is of course unique, but with each transaction, the user can select an existing PERSON and/or Occasion and if that person then does not exist, it will be created (likewise with the occasion).

我对这里的代码格式有点困惑.

I'm slightly confused as to the format of the code here.

我尝试了代码组合,但无法正常工作.在下面的代码中,我在谓词中引用了 person.name,但我也尝试创建一个本地 NSString 变量来保存 self.nameTextField.text 代码,但没有做任何事情.我尝试创建一个 NSString 属性来引用它,但不起作用.我尝试使用单词 MATCHES, LIKE, CONTAINS, == 以及它们之间的每个组合.

I have tried a combination of code and can just not get this working. In the code below, I'm referencing person.name in the predicate, but I also tried creation a local NSString variable to hold the self.nameTextField.text code but that did nothing. I tried creating a NSString property to reference it that way and that not work. I tried using the words MATCHES, LIKE, CONTAINS, == and every combination in-between.

- (IBAction)save:(id)sender
{
    NSManagedObjectContext *context = [self managedObjectContext];
    Transaction *transaction= [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    Occasion *occasion = [NSEntityDescription insertNewObjectForEntityForName:@"Occasion" inManagedObjectContext:context];
    Item *amount = [NSEntityDescription insertNewObjectForEntityForName:@"item" inManagedObjectContext:context];

 NSFetchRequest *personFind = [NSFetchRequest fetchRequestWithEntityName:@"Person"];

    personFind.predicate = [NSPredicate predicateWithFormat:@"name == %@", person.name];
    // I have tried every combination of the predicate like MATCHES, LIKE. 
    // I created a local NSString variable and an NSString property
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    personFind.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *matches = [context executeFetchRequest:personFind error:&error];

    if (!matches || ([matches count] > 1))
    {
        // Handle Error
    }
    else if ([matches count] == 0)
    {
        person.name = self.nameTextField.text;
        transaction.whoBy = person;
        occasion.title = self.occasionTextField.text;
        transaction.occasion = occasion;
    }

    else
    {
        person = [matches lastObject];
        transaction.whoBy = person; 
        occasion.title = self.occasionTextField.text
        transaction.occasion = occasion; 
    }


if (![context save:&error])
    {
        NSLog(@"Can't save! %@ %@", error, [error localizedDescription]);

    }
    [self dismissViewControllerAnimated:YES completion:nil];

}

逻辑上,我想要实现的是:

Logically, what I want to achieve is:

  • 当用户添加一个交易时,检查它是给一个新人还是一个现有的人——如果是一个现有的人,从一个人的列表中选择它(当用户选择一个人时,得到它的NSManagedObjectID).如果是新的,请当场创建.

  • When the user is adding a Transaction, check if it's for a new person or an existing one — if it's an existing one, choose it from a list of Persons (and when the user selects a person, get its NSManagedObjectID). If it's a new one, create it on the spot.

场合也一样.

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

Set all the other fields of the Transaction object (amount, etc.).

我的问题是:

  • 我使用什么谓词来实现这个功能?

当我在这个方法中放置一个断点时,一个 NEW NAME(一个以前不存在的名字)正确地调用了 else if ([matches count] == 0) 方法,如果我创建一个具有现有名称的条目,它调用

When I put a break point in this method, a NEW NAME (one that doesn't exist before) correctly calls the else if ([matches count] == 0) method and if I create an entry with an existing name, it calls the

else
        {
            person = [matches lastObject];
            transaction.whoBy = person; 
            occasion.title = self.occasionTextField.text
            transaction.occasion = occasion; 
        }

即使有了 this 语句,它仍然为同名创建了一个新的 person 对象.

Even with the this statement, it is still creating a new person object for the same name.

在让这个人工作后我会正确地实施这个场合,但我只是迷失了如何让它工作.

I will correctly implement the occasion after getting the person working, but I'm just lost on how to get this working.

任何帮助将不胜感激!

推荐答案

这是否正确?":
否.无论您是否使用现有的人物/场合,您都在创建一个新的 PersonOccasion 对象.
首先检查是否存在,只有当对象不存在时,才插入一个新的.
或者,如果此人/场合存在,则删除插入的对象.

"Is this correct?":
No. You are creating a new Person and Occasion objects whether you are using an existing person/occasion or not.
First check for existence and only if the object not already exist, insert a new one.
Alternatively, if the person/occasion exist, delete the inserted object.

如何检索人员/事件的 managedObjectID?":

"How do I retrieve the managedObjectID for person/event?":

Person* person = /*Get an existing person object*/
NSManagedObjectID* personId = person.objectID /*This is the person object ID, will work for any NSManagedObject subclass*/

要查找以字符串 str 开头的人,请在获取请求中使用此谓词:

To find a person that start with a string str use this predicate in a fetch request:

/*UNTESTED*/
[NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@)", str];

更准确地说,您可以使用以下方法练习查找或创建:
(这是非常有限的,并且仅适用于单个对象的性能)(未测试)

To be more precise, you practice find or create using something like this:
(this is very limited, and only good for a single object performance-wise) (NOT TESTED)

- (NSManagedObject*) findOrCreateObjectByValue:(id)value
                                  propertyName:(NSString*)propertyName
                                    entityName:(NSString*)entityName
                                additionalInfo:(NSDictionary*)additionalInfo
                                       context:(NSManagedObjectContext*)context
                                         error:(NSError* __autoreleasing*)error
{
    NSManagedObject* res = nil;

    NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:entityName];
    [r setPredicate:[NSPredicate predicateWithFormat:@"%K == %@",propertyName,value]];

    NSArray* matched = [context executeFetchRequest:r
                                              error:error];

    if (matched) {
        if ([matched count] < 2) {
            res = [matched lastObject];
            if (!res) { //No existing objects found, create one
                res = [NSEntityDescription insertNewObjectForEntityForName:entityName
                                                    inManagedObjectContext:context];
                [res setValue:value
                       forKey:propertyName];
                [res setValuesForKeysWithDictionary:additionalInfo];
            }
        } else {
            if (error) {
                *error = [NSError errorWithDomain:@"some_domain"
                                             code:9999
                                         userInfo:@{@"description" : @"duplicates found"}];
            }
        }
    }

    return res;
}

现在,您的 save: 方法应该类似于:
(我在这里假设人名和场合标题由视图控制器 [txtPersonNametxtOccasionTitle 分别] 上的 UITextField 保存)

So now, your save: method should look something like:
(I assume here that the person name and occasion title are held by a UITextField on the view controller [txtPersonName and txtOccasionTitle respectively] )

- (void) save:(id)sender
{
    //create a clean context so that changes could be discarded automatically on failure
    NSManagedObjectContext* context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [context setParentContext:[self managedObjectContext]];

    //A Transaction is always created in save event, so add it to the context
    Transaction* transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];

    __block NSError* error = nil;

    Person* p = (Person*)[self findOrCreateObjectByValue:self.txtPersonName.text
                                            propertyName:@"name"
                                              entityName:@"Person"
                                          additionalInfo:nil
                                                 context:context
                                                   error:&error];
    if (!p) {
        NSLog(@"Error: %@, person name: %@",error,self.txtPersonName.text);
        return;
    }

    Occasion* o = (Occasion*)[self findOrCreateObjectByValue:self.txtOccasionTitle.text
                                                propertyName:@"title"
                                                  entityName:@"Occasion"
                                              additionalInfo:nil
                                                     context:context
                                                       error:&error];
    if (!o) {
        NSLog(@"Error: %@, occasion title: %@",error,self.txtOccasionTitle.text);
        return;
    }

    transaction.whoBy = p;
    transaction.occasion = o;
    //Not sure what you are using this property for
    transaction.item = [NSEntityDescription insertNewObjectForEntityForName:@"Item"
                                                     inManagedObjectContext:context];

    NSManagedObjectContext* ctx = context;
    if ([context obtainPermanentIDsForObjects:[context.insertedObjects allObjects]
                                        error:&error])
    {
        //save your changes to the store
        __block BOOL saveSuccess = YES;
        while (ctx && saveSuccess) {
            [ctx performBlockAndWait:^{
                saveSuccess = [ctx save:&error];
            }];
            ctx = [ctx parentContext];
        }

        if (!saveSuccess) {
            NSLog(@"Could not save transaction, error: %@",error);
        }
    } else {
        NSLog(@"Could not obtain IDs for inserted objects, error: %@",error);
    }

    //Do what you have to do next
}

这只是为了让事情更清楚你应该做什么以避免重复,并重用现有的对象.

This is just for making things a bit clearer on what you should do to avoid duplications, and reuse existing objects.

这篇关于创建新对象时查询特定属性的核心数据,如果存在则返回对象,如果不存在则创建新对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆