Re: Core Data : calling validation in awakeFromInsert problem ?
Re: Core Data : calling validation in awakeFromInsert problem ?
- Subject: Re: Core Data : calling validation in awakeFromInsert problem ?
- From: Eric Morand <email@hidden>
- Date: Mon, 5 Sep 2005 22:08:03 +0200
Thanks for such a great explanation ! I'll try it ass soon as I've
typed this mail.
This approach will be very inefficient for anything but data sets
where there is little likelihood of name collisions -- you have to
do an additional fetch for every collision. It's not clear what
your goal is here. If you're simply wanting to set temporary
distinct names (like window titles) for small numbers of objects,
this would probably be OK. If these will be permanent values for
an ever-increasing number of objects, then you'll need a different
approach...
Well, that's simple : just like in iTunes, when you create two or
three playlist without renaming them, the playlist name is append
with "(index++). I need the same kind of behavior : in my app, user
can add managed objects via a "Add" button and I want the name of the
added object to "increase" automatically. Does it seem a bad idea to
you ?
How am I supposed to do if I want to be sure that a property is
unique for a given entity ?
Eric.
Le 5 sept. 05 à 21:07, mmalcolm crawford a écrit :
On Sep 5, 2005, at 9:42 AM, Eric Morand wrote:
I have a managed object with one property named "name". This
property is a string. I want this property to be unique in the
managed context of the object, so I've implemented
"validateName..." method in my managed object subclass (see code
below).
Next, I want new objects inserted into the context to have a name
set to a validated one (that means : unique). So I have
implemented "awakeFromInsert..." method in my subclass that set
the name property to a valid one.
[...]
Here is the code for my custom class (implementation only, there
is nothing of interest in the header) :
]...]
- (void)awakeFromInsert
{
[...]
NSString * validName = nil;
NSString * defaultName = [NSString
stringWithString:@"TestObjectName"];
validName = defaultName;
int index = 0;
while ( ![self validateValue:&validName forKey:@"name"
error:nil] )
{
index++;
validName = [defaultName stringByAppendingFormat:@" (%d)",
index];
}
[self setName:validName];
}
- (BOOL)validateName: (id *)valueRef error:(NSError **)outError
{
NSPredicate * predicate = [NSPredicate
predicateWithFormat:@"name == %@", *valueRef];
NSEntityDescription * entityDescription = [NSEntityDescription
entityForName:@"TestObject" inManagedObjectContext:[self
managedObjectContext]];
NSFetchRequest * fetchRequest = [[[NSFetchRequest alloc] init]
autorelease];
[fetchRequest setEntity:entityDescription];
[fetchRequest setPredicate:predicate];
NSArray * results = [[self managedObjectContext]
executeFetchRequest:fetchRequest error:nil];
return ( [results count] < 1 ) || ( [results objectAtIndex:0]
== self );
}
@end
I'm using a simple interface to test the insertion process : in
fact it is the standard interface created automatically when alt-
dragging the object from Xcode to IB - one table view, 3 buttons
(Fetch, Add, Remove), a text field to enter name and a search
field. Then, the problems begin : when I click on the add button,
TWO objects are added to the table view !
I'm not sure if this is a bug (I'll follow up) but it's because
you're executing a fetch (whose results will include self) before
the insertion has completed. You can sidestep this by setting the
name after awakeFromInsert:
- (void)awakeFromInsert
{
[super awakeFromInsert];
[self performSelector:@selector(setUniqueName) withObject:nil
afterDelay:0];
}
There are several issues with the remainder of the awakeFromInsert
code (now moved to the new method) and the validation method:
- (void)setUniqueName
{
NSString * defaultName = @"TestObjectName"; // no need for
stringWithString:
NSString * validName = defaultName; // no need for two assignments
int index = 0;
while ( ![self validateValue:&validName forKey:@"name"
error:nil] )
{
index++;
validName = [defaultName stringByAppendingFormat:@" (%d)",
index];
}
[self setName:validName];
}
- (BOOL)validateName: (id *)valueRef error:(NSError **)outError
{
/*
Check (SELF != %@) in fetch; use 'LIKE' not '==' for string comparison
*/
NSPredicate * predicate = [NSPredicate
predicateWithFormat:@"(SELF != %@) AND (name LIKE %@)", self,
*valueRef];
NSFetchRequest * fetchRequest = [[NSFetchRequest alloc] init];
/*
Use self's entity -- no need to use NSEntityDescription method to
retrieve (also avoids hard-wiring entity name)
*/
[fetchRequest setEntity:[self entity]];
[fetchRequest setPredicate:predicate];
/*
Should check for error
*/
NSArray * results = [[self managedObjectContext]
executeFetchRequest:fetchRequest error:nil];
[fetchRequest release];
return ( [results count] < 1 );
}
And do you think I'm using the right process to validate my name
property and mak sure my object are inserted with valid name ?
This approach will be very inefficient for anything but data sets
where there is little likelihood of name collisions -- you have to
do an additional fetch for every collision. It's not clear what
your goal is here. If you're simply wanting to set temporary
distinct names (like window titles) for small numbers of objects,
this would probably be OK. If these will be permanent values for
an ever-increasing number of objects, then you'll need a different
approach...
mmalc
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden