views:

344

answers:

3

I am using core data framework to manage objects.i have an entity which has several attributes of decimal types. Among them is attribute which is mathematically calculated from other attributes. Ex :-

@interface Marks : NSManagedObject
{
}

@property (nonatomic, retain) NSDecimalNumber * answerGradeA;
@property (nonatomic, retain) NSDecimalNumber * answerGradeB;
@property (nonatomic, retain) NSDecimalNumber * answerGradeC;
@property (nonatomic, retain) NSDecimalNumber * total;

Here i want attribute total = 3xanswerGradeA + 2xanswerGradeB + 1xanswerGradeC

if it is possible to do like this, then how ? please reply. Thanks in advance.

+3  A: 

Why not make it a category and compile it in a separate file? (Strictly speaking, total should not be part of CoreData.)

@interface Marks (Calculated)
@property (nonatomic, readonly) NSDecimalNumber* total;
@end

@implementation Marks (Calculated)
- (NSDecimalNumber*) total { 
  return whatEverYouLike; 
}
@end
Jens
+1: Using categories is ideal, because if you change the data model and make new entity header and implementation files, your category remains separate and intact.
Alex Reynolds
Why should total not be part of the Core Data model? @Kundan defines total here as the sum of three specific properties -- it's hard to see why Marks should *not* be responsible for providing the implementation of total, unless @Kundan has no access to the source code.
Elise van Looij
That depends on what you are going to do with total. If you want to further process, query, undo ... 'total', then your approach with transient properties works fine (even though transient properties may be tricky). If it is just a matter of adding 3 numbers, I wouldn't probably change the model for that purpose.
Jens
+2  A: 

The Core Data way is to add 'total' as an attribue to the model and mark it 'transient'. You then provide the implementation in a subclass.

@interface Marks :  NSManagedObject  
{
}
@property (nonatomic, readonly) NSDecimalNumber* total;
@end

@implementation Marks (Calculated)
- (NSDecimalNumber*) total { 
    return (3 * [self valueForKey:@"answerGradeA"]) + (2 * [self valueForKey:@"answerGradeB"]) + [self valueForKey:@"answerGradeC"]; 
}
+ (NSSet *)keyPathsForValuesAffectingTotal
{
    return [NSSet setWithObjects:@"answerGradeA", @"answerGradeB", @"answerGradeC", nil];
}
@end

This will ensure proper caching and updating of total.

Elise van Looij
As core data assign values to attributes dynamically, isn't it necessary to declare those remaning attributes ["answerGradeA" and so ] in this headerfile along with "total" so that this attribute will know what is the value for key "answerGradeA" while computing.
Kundan Pandit
No, happily the compiler is smart enough to figure that out on its own. Moreover, every line a programmer writes is a potential source of problems and bugs: 'less is more' is as true in programming as it is in life. Let Core Data do its thing and only nudge it in the right direction when necessary.
Elise van Looij
+1  A: 

I want to post a little modification to Looji's answer.

@interface Marks :  NSManagedObject  
{
}
@property (nonatomic, retain) NSDecimalNumber * answerGradeA;
@property (nonatomic, retain) NSDecimalNumber * answerGradeB;
@property (nonatomic, retain) NSDecimalNumber * answerGradeC;
@property (nonatomic, readonly) NSDecimalNumber* total;
@end

@implementation Marks (Calculated)
- (NSDecimalNumber*) total { 
return (3 * [self valueForKey:@"answerGradeA"]) + (2 * [self valueForKey:@"answerGradeB"]) + [self valueForKey:@"answerGradeC"]; 
}
+ (NSSet *)keyPathsForValuesAffectingTotal
{
return [NSSet setWithObjects:@"answerGradeA", @"answerGradeB", @"answerGradeC", nil];
}
@end
Kundan Pandit