在创建新对象时查询特定属性的核心数据,并返回对象(如果存在)或创建新对象(如果对象不存在) [英] 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

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

问题描述

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



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



数据模型如下:



事务实体与isReceived BOOL属性
名称字符串属性
具有标题字符串属性的偶然实体
具有金额字符串属性的项实体



事务与Person(whoBy)场合(场合)和项目实体。



在具有保存方法的视图控制器中,我有下面的代码,将新对象插入事务,人员,事件实体等。每个事务唯一的,但是对于每个交易,用户可以选择现有的PERSON和/或场合,并且如果那个人不存在,则将创建(同样地)。



我对这里的代码格式有些困惑。



编辑:我试过了一个组合的代码,只是不能得到这个工作。在下面的代码中,我在引用谓词中的person.name,但我也尝试创建一个本地NSString变量来保存 self.nameTextField.text 代码,但是没有。我试着创建一个NSString属性来引用它的方式,这不工作。我尝试使用 MATCHES,LIKE,CONTAINS,== 和中间的每个组合。

   - (IBAction)save:(id)sender 
{
NSManagedObjectContext * context = [self managedObjectContext ];
Transaction * transaction = [NSEntityDescription insertNewObjectForEntityForName:@TransactioninManagedObjectContext:context];
Person * person = [NSEntityDescription insertNewObjectForEntityForName:@PersoninManagedObjectContext:context];
场合*场景= [NSEntityDescription insertNewObjectForEntityForName:@OccasioninManagedObjectContext:context];
Item * amount = [NSEntityDescription insertNewObjectForEntityForName:@iteminManagedObjectContext:context];

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

personFind.predicate = [NSPredicate predicateWithFormat:@name ==%@,person.name];
//我已经尝试过像MATCHES,LIKE这样的谓词的每个组合。
//我创建了一个局部NSString变量和一个NSString属性
NSSortDescriptor * sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@nameascending:YES];
personFind.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

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

if(!matches ||([matches count]> 1))
{
//句柄错误
}
else if matches count] == 0)
{
person.name = self.nameTextField.text;
transactions.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(@无法保存! @,error,[error localizedDescription]);

}
[self dismissViewControllerAnimated:YES completion:nil];

}



要实现的是:




  • 当用户添加事务时,检查它是为一个新的人还是一个现有的它是一个现有的,从人员列表中选择(当用户选择一个人,获得其NSManagedObjectID)。如果是新的,请立即创建。


  • 同样的场合。




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



我的问题是:




  • 我使用什么谓词来使这个工作?



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

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

即使使用this语句,它仍然创建一个新的person对象一样的名字。



我会在工作后正确执行这个机会,但是我只是失去了如何让这个工作。



任何帮助都将大受赞赏!

解决方案

正确?:

否。您正在创建一个新的 Person 场合您正在使用现有的人/场合。

首先检查是否存在,如果对象不存在,请插入新的对象。

或者,如果该人/ occasion存在,请删除插入的对象。



如何检索person / event的managedObjectID?:

  Person * person = / *获取现有的人对象* / 
NSManagedObjectID * personId = person.objectID / *这是人对象ID,将用于任何NSManagedObject子类* /

要查找以字符串 str 在抓取请求中使用此谓词:

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



编辑



更精确地说,您可以使用这样的方式练习find或create:0047
(这是非常有限的,仅对单个对象在性能方面有好处)
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){//找不到现有对象,创建一个
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:方法应该看起来像这样:

(我假设人名和场合标题由视图控制器上的UITextField持有[ txtPersonName

   - ( void)save:(id)sender 
{
//创建一个干净的上下文,以便更改可以在失败时自动丢弃
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[context setParentContext:[self managedObjectContext]];

//事务总是在save事件中创建,因此将其添加到上下文中
Transaction * transaction = [NSEntityDescription insertNewObjectForEntityForName:@TransactioninManagedObjectContext: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;
}

场合* o =(场合*)[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;
//不确定你使用这个属性
transaction.item = [NSEntityDescription insertNewObjectForEntityForName:@Item
inManagedObjectContext:context];

NSManagedObjectContext * ctx = context;
if([context getsPermanentIDsForObjects:[context.insertedObjects allObjects]
error:& error])
{
//将更改保存到存储
__block BOOL saveSuccess = YES;
while(ctx&& saveSuccess){
[ctx performBlockAndWait:^ {
saveSuccess = [ctx save:& error];
}];
ctx = [ctx parentContext];
}

if(!saveSuccess){
NSLog(@无法保存事务,错误:%@,错误);
}
} else {
NSLog(@无法获取插入对象的ID,错误:%@,错误);
}

//做你接下来做的事
}


b $ b

这只是为了让您更清楚地了解应该如何避免重复,并重用现有对象。


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.

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).

The data model is as follows:

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

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

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.

EDIT: 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:

  • 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.

  • The same for the Occasion.

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

My question is:

  • What predicate do I use to get this working?

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; 
        }

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.

Any help would be massively appreciated!

解决方案

"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.

"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*/

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

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

Edit:

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;
}

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天全站免登陆