使用 CoreData 执行乘法(聚合):如何?
Posted
技术标签:
【中文标题】使用 CoreData 执行乘法(聚合):如何?【英文标题】:Performing multiplication (aggregation) with CoreData: how to? 【发布时间】:2012-01-24 10:25:30 【问题描述】:在a fantastic tutorial by Jeff Lamarche 之后,我正在尝试为NSManagedObject
的特定子类聚合数据。
这就是场景。我创建了一个名为 Product
的类,它扩展了 NSManagedObject
类。 Product
类具有如下三个属性:
@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* quantity;
@property (nonatomic, retain) NSNumber* price;
我还创建了一个名为Product+Aggregate
的类别,我在其中执行总和聚合。特别是,按照 Jeff 教程,我管理了数量属性的总和。
+(NSNumber *)aggregateOperation:(NSString *)function onAttribute:(NSString *)attributeName withPredicate:(NSPredicate *)predicate inManagedObjectContext:(NSManagedObjectContext *)context
NSString* className = NSStringFromClass([self class]);
NSExpression *ex = [NSExpression expressionForFunction:function
arguments:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:attributeName]]];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:@"result"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger64AttributeType];
NSArray *properties = [NSArray arrayWithObject:ed];
[ed release];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];
if (predicate != nil)
[request setPredicate:predicate];
NSEntityDescription *entity = [NSEntityDescription entityForName:className
inManagedObjectContext:context];
[request setEntity:entity];
NSArray *results = [context executeFetchRequest:request error:nil];
NSDictionary *resultsDictionary = [results objectAtIndex:0];
NSNumber *resultValue = [resultsDictionary objectForKey:@"result"];
return resultValue;
这个类方法被一个特定的UIViewController
调用如下:
NSNumber *totalQuantity = [Product aggregateOperation:@"sum:" onAttribute:@"quantity" withPredicate:nil inManagedObjectContext:self.context];
代码运行良好。事实上,如果我说 3 个产品
NAME QUANTITY PRICE
PRODUCT 1 2 23.00
PRODUCT 2 4 12.00
PRODUCT 3 1 2.00
aggregateOperation
方法按预期返回 7。
现在我会多走一步。修改该方法,我需要返回产品订单的总成本。换句话说,我需要计算每个产品的 QUANTITY*PRICE 值,最后返回 TOTAL。
你能建议我正确的方法吗?提前谢谢你。
编辑这是我在 Cyberfox 建议后使用的新代码,但不幸的是它不起作用。
NSString* className = NSStringFromClass([self class]);
NSArray *quantityPrice = [NSArray arrayWithObjects: [NSExpression expressionForKeyPath:@"quantity"], [NSExpression expressionForKeyPath:@"price"], nil];
NSArray *multiplyExpression = [NSArray arrayWithObject:[NSExpression expressionForFunction:@"multiply:by:" arguments:quantityPrice]];
NSExpression *ex = [NSExpression expressionForFunction:function arguments:multiplyExpression];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:@"result"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger64AttributeType];
// same as before
【问题讨论】:
【参考方案1】:对于那些感兴趣的人,我找到了解决上述问题的方法。
代码如下:
static NSString* exprName1 = @"val1";
static NSString* exprName2 = @"val2";
NSString *className = NSStringFromClass([self class]);
NSExpression *quantityPathExpression = [NSExpression expressionForKeyPath:firstAttribute]; //e.g. quantity
NSExpression *unitaryPricePathExpression = [NSExpression expressionForKeyPath:secondAttribute]; //e.g. price
NSExpressionDescription *quantityED = [[NSExpressionDescription alloc] init];
[quantityED setName:exprName1];
[quantityED setExpression:quantityPathExpression];
[quantityED setExpressionResultType:NSDictionaryResultType];
NSExpressionDescription *unitaryPriceED = [[NSExpressionDescription alloc] init];
[unitaryPriceED setName:exprName2];
[unitaryPriceED setExpression:unitaryPricePathExpression];
[unitaryPriceED setExpressionResultType:NSDictionaryResultType];
NSArray *properties = [NSArray arrayWithObjects:quantityED, unitaryPriceED, nil];
[quantityED release];
[unitaryPriceED release];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPropertiesToFetch:properties];
[request setResultType:NSDictionaryResultType];
if (predicate != nil)
[request setPredicate:predicate];
NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:context];
[request setEntity:entity];
NSError* error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
if(error != nil)
NSLog(@"An error occurred: %@", [error localizedDescription]);
abort();
float total = 0;
for (NSDictionary *resultDict in results)
NSNumber* quantityNumber = [resultDict valueForKey:exprName1];
NSNumber* unitaryPriceNumber = [resultDict valueForKey:exprName2];
int moltVal = [quantityNumber intValue]*[unitaryPriceNumber intValue];
total += moltVal;
return [NSNumber numberWithInt:total];
P.S. 使用它创建一个 Class 方法,该方法返回 NSNumber
并接受托管上下文和 2 个您要执行数据检索的属性 (NSString
) 作为参数。
希望对你有帮助!
【讨论】:
【参考方案2】:这是在黑暗中拍摄,因为要复制您的代码,我需要构建一个数据库等,但我相信您想要:
NSArray *quantityPrice = [NSArray arrayWithObjects:
[NSExpression expressionForKeyPath:@"quantity"],
[NSExpression expressionForKeyPath:@"price"], nil];
NSArray *multiplyExpression = [NSArray arrayWithObject:[NSExpression expressionForFunction:@"multiply:by:" arguments:quantityPrice]];
NSExpression *ex = [NSExpression expressionForFunction:function arguments:multiplyExpression];
quantityPrice
是引用quantity
和price
的表达式对的数组。
multiplyExpression
是表达式multiply:by:
,参数为quantityPrice
。
ex
是您的 sum:
表达式,引用了引用数量和价格键路径的多个表达式。
我很确定您会想要做类似的事情,但是如果没有像您这样的数据库设置等,我无法对其进行测试,所以这只是理论。
【讨论】:
感谢您的回复。运行它,代码停在NSArray *results = [context executeFetchRequest:request error:nil];
,但有以下异常EXCEPTIONS: unwinding through frame有什么建议吗?
您是否得到了我的编辑版本,我在 'multiply:by:' 表达式中添加了尾随 ':'? *** 给我解决了大约 10 分钟的麻烦。 :( 也就是说,堆栈跟踪中的某处应该有一个更全面的错误,类似于:Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Error while updating. 'constraint failed''
你能贴出完整堆栈跟踪的粘贴吗?
是的,我做到了。堆栈跟踪显示该异常。我可以做些什么来查看完整的堆栈跟踪吗?我的方案项目中启用了日志异常。
另一件有用的事情是编辑应用程序的运行方案,并将-com.apple.CoreData.SQLDebug 1
(它可能需要作为两个单独的参数输入)添加到参数中。这将(在某些平台上)在针对数据库运行时触发 SQL 语句的核心数据转储到控制台。有时查看生成的 SQL 可以告诉您出了什么问题。
当我的意思是“multiplyExpression”时,看起来我也写了“multipleExpression”。仍然,您在 XCode 中查看控制台视图,它给出的只是一行“展开框架”而没有额外的细节,或者如果您向上或向下滚动,它会提供任何东西?呃;这将使调试变得困难。确实应该有一个更深层次的“原因”。以上是关于使用 CoreData 执行乘法(聚合):如何?的主要内容,如果未能解决你的问题,请参考以下文章
CoreData - 如何使用 NSPrivateQueueConcurrencyType 使用临时上下文执行 NSFetchRequest?