Tutorial :Cocoa:CoreData - Multiple entities in a ManagedObjectContext



Question:

I have two entities which I modeled as classes and core data model entities too/ The classes look as follows:

Class: StateManager

#import <Foundation/Foundation.h>  #import <CoreData/CoreData.h>   #import "StateManager.h"  #import "Constants.h"      @implementation StateManager     @synthesize deviceID, physicalID, applicationName, applicationVersion;  @synthesize userName, userPassword, server, port, client, language, protocol;    -(bool)checkCompleteness{  if (physicalID == nil) {      return false;  }  if (applicationName == nil) {      return false;  }  if (applicationVersion == nil) {      return false;  }  if (userName == nil) {      return false;  }  if (userPassword == nil) {      return false;  }  if (port == nil) {      return false;  }  if (server == nil) {      return false;  }  return true;  }  -(void)preserveState:(NSManagedObjectContext *)managedObjectContext{  // Step 1: Create Object  StateManager *inMemoryState = (StateManager *)[NSEntityDescription                                       insertNewObjectForEntityForName:ENTITY_STATE                                       inManagedObjectContext:managedObjectContext];  // Step 2: Set Properties  [inMemoryState setDeviceID:self.deviceID];  [inMemoryState setPhysicalID:self.physicalID];  [inMemoryState setApplicationName:self.applicationName];  [inMemoryState setApplicationVersion:self.applicationVersion];  //[inMemoryState setAutoRegister:[self getAutoRegister]];  [inMemoryState setUserName:self.userName];  [inMemoryState setUserPassword:self.userPassword];  [inMemoryState setServer:self.server];  [inMemoryState setPort:self.port];  [inMemoryState setClient:self.client];  [inMemoryState setLanguage:self.language];  [inMemoryState setProtocol:self.protocol];    // Step 3: Save Object  NSError *error;  if (![managedObjectContext save:&error]) {       NSLog(@"Unresolved Core Data Save error %@, %@", error, [error userInfo]);       exit(-1);   }    }      -(StateManager *)loadState:(NSManagedObjectContext *)managedObjectContext{    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];    NSEntityDescription *entityState = [NSEntityDescription entityForName:ENTITY_STATE                                                  inManagedObjectContext:managedObjectContext];  [fetchRequest setEntity:entityState];    NSError *error;    NSArray *items = [managedObjectContext                    executeFetchRequest:fetchRequest error:&error];    if ([items count] > 0) {      StateManager *inDatabaseState = (StateManager *)[items objectAtIndex:0];        deviceID = [inDatabaseState valueForKey:@"deviceID"];      physicalID = [inDatabaseState valueForKey:@"physicalID"];      applicationName = [inDatabaseState valueForKey:@"applicationName"];      applicationVersion = [inDatabaseState valueForKey:@"applicationVersion"];      //autoRegister = [inDatabaseState valueForKey:@"autoRegister"];      userName = [inDatabaseState valueForKey:@"userName"];      userPassword = [inDatabaseState valueForKey:@"userPassword"];      server = [inDatabaseState valueForKey:@"server"];      port = [inDatabaseState valueForKey:@"port"];      client = [inDatabaseState valueForKey:@"client"];      language = [inDatabaseState valueForKey:@"language"];      protocol = [inDatabaseState valueForKey:@"protocol"];        [inDatabaseState release];  }    [fetchRequest release];  [entityState release];   [items release];   /*   deviceID = @"1234";   physicalID = @"12312";   applicationName = @"erwerw";   applicationVersion = @"applicationVersion";   //autoRegister = [inDatabaseState valueForKey:@"autoRegister"];   userName = @"userName";   userPassword = @"userPassword";   server = @"server";   port = @"port";   client = @"client";   language = @"language";   protocol = @"protocol";*/   return self;    }    -(void)dealloc{    [deviceID release];  [physicalID release];  [applicationName release];  [applicationVersion release];  [userName release];  [userPassword release] ;  [server release];  [port release];  [client release];  [language release];  [protocol release];  [super dealloc];  }    @end  

Class: InboundQueueState

#import "InQueueState.h"      @implementation InQueueState     @dynamic top;  @dynamic read;  @dynamic last;    @end  

Now I am trying to load these objects data from DB into my application at two different places as shown below:

  1. If you look into the class StateManager you will see a method loadState which tries to read the entity "StateManager" using a managed object context. This context was passed to this class from the main class.

  2. Another method "readQueue" as shown below in class "InboundQueue" tries to follow the same paradigm of loading the entity "InboundState" using the global managed object context but fails with an error given below:

Error: [NSFetchRequest objectID]: unrecognized selector sent to instance 0x3a1a300 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSFetchRequest objectID]: unrecognized selector sent to instance 0x3a1a300'

Class:InboundQueue

#import "InboundQueue.h"  #import <CoreData/CoreData.h>   #import "Constants.h"  #import "InQueueItem.h"    @implementation InboundQueue    -(int)last{  return [queueState last];  }    -(int)top{  return [queueState top];  }    -(int)read{  return [queueState read];  }    -(InboundQueue *)initInboundQueue:(NSManagedObjectContext *)managedObjectContext{    //load the inbound queue counters and initialize the inbound queue object with the values  NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];    NSEntityDescription *entityState = [NSEntityDescription entityForName:INQUEUE_STATE                                                  inManagedObjectContext:managedObjectContext];  [fetchRequest setEntity:entityState];    NSError *error;  NSArray *msgs = [managedObjectContext                    executeFetchRequest:fetchRequest error:&error];    if ([msgs count] > 0) {      queueState = [msgs objectAtIndex:0]; //contains the values of the counters related to inbound queue at any point in time  }    [fetchRequest, entityState, msgs release];  return self;  }    -(NSMutableArray *)readQueue:(NSManagedObjectContext*) managedObjectContext{  //load the inbound queue counters and initialize the inbound queue object with the values  NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];    NSEntityDescription *entityState = [NSEntityDescription entityForName:INQUEUE                                                  inManagedObjectContext:managedObjectContext];  [fetchRequest setEntity:entityState];    NSError *error;  NSArray *queueContent = [managedObjectContext                    executeFetchRequest:fetchRequest error:&error];  [queueData setArray:queueContent];    [fetchRequest, entityState, error release];  return queueData;  }    -(void)confirm:(int)msgID managedObjectContext:(NSManagedObjectContext*) managedObjectContext{  if (msgID < [queueState top]) {      return;  }  if (msgID > [queueState last]) {      return;  }  //remove items from queue which are lower than the confrmed message ID  for (InQueueItem *item in queueData) {      int currentMsgId = [item MSG_ID];       if ( currentMsgId <= msgID) {          //delete the item from queue and adjust counters            [queueData removeObject:item];      }  }  [queueState setTop:(msgID + 1)];  if ([queueState read] < [queueState top]) {      [queueState setRead:[queueState top]]; //realign the top counter  }  //persist the changes from context to database  NSError *error;  if (![managedObjectContext save:&error]) {      NSLog(@"Unresolved Core Data Save error %@, %@", error, [error userInfo]);      exit(-1);  }  }    -(void)add:(InQueueItem *)item  managedObjectContext:(NSManagedObjectContext*) managedObjectContext{  //validate the message id  if (([queueState last] > ([item MSG_ID] - 1))) {      //ignore this message since we have already received a message with this message id       }else if (([queueState last] < ([item MSG_ID] - 1))) {      //raise an invalid message ID exception => data loss      }else {      if ([queueState last] == 0) {          //need to initialize counters as this is the first entry          [queueState setTop:[item MSG_ID]];          [queueState setLast:[item MSG_ID]];        }else {          //increment last          [queueState setLast:([queueState last] + 1)];      }      //now insert the item into queue      [queueData addObject:item];  }  //persist the changes from context to database  NSError *error;  if (![managedObjectContext save:&error]) {      NSLog(@"Unresolved Core Data Save error %@, %@", error, [error userInfo]);      exit(-1);  }    }    -(void)dealloc{  [queueData release];  [queueState  release];  [super dealloc];  }  @end  

My understanding is that same managed object context can load all the objects of the data model and allows manipulation, but i am not sure why in my case it only works for one object only.


Solution:1

You do not need a separate ManagedObjectContext for each entity. The ManagedObjectContext controls the entire object graph for your entire model regardless of how many entities the model contains. (You can create multiple contexts but not to handle multiple entities.)

If your A fetch is stepping on you B fetch then you most likely are not configuring your B fetch properly. Are you trying to reuse the fetch you created for A by any chance?

Fetches are highly unique. Each fetch is configured separately and you should never reuse them for other entities or alter their fetch parameters (although the class doesn't enforce this.)

You should initialize an entirely separate fetch for B.

Edit01:

Based on the code you posted and the details of the error, I think the problem is most likely that you are not having a proper entity description returned for your InQueueState class.

I would suggest logging the entity returned by:

NSEntityDescription *entityState = [NSEntityDescription entityForName:INQUEUE                                                  inManagedObjectContext:managedObjectContext]  

to make sure it is not nil.

You also (as standard practice) need to check that the NSArray *queueContent is not nil or empty before you make use of it.

Edit02:

Also, this line:

[fetchRequest, entityState, error release];  

You do not need to release either entityState nor error because you do not init or create either one but merely hold references to them returned from other objects.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »