How to set a reference from one object to another? - iphone

SOLVED
I misspelled the property department -> Department.
Thanks for the answers and a big SORRY
Hi,
Imagine the obvious model of employees and departments, where each employee must refer to a single department, and each department may have none to many employees.
For Employee I have a pointer to Department, and I set its value to the address of the object of the relevant Department.
For Department I have a NSSet, to which I can add employees.
The problem is that when I create a new employee and a new department, the objects exist in the memory at some place. The reference from Employee to Department, and vice versa, contains these addresses. When I'll fetch the data it will be located at a different location. So it is not clear how the connection between these objects is done.
Here is the code as I understand it should be:
// Fetch Plist object
NSDictionary *plistDictionary = [[NSDictionary alloc] initWithDictionary:plistData];
// Create Department Entity
Department *department = (Department *)[NSEntityDescription
insertNewObjectForEntityForName:#"Department"
inManagedObjectContext:managedObjectContext];
// Set properties
[department setName:[plistDictionary valueForKey:#"department"]];
// Create Employee entity
Employee *employee = (Employee *)[NSEntityDescription
insertNewObjectForEntityForName:#"Employee"
inManagedObjectContext:managedObjectContext];
// Set properties
[employee setFileName:[plistDictionary valueForKey:#"picture"]];
[employee setName:[plistDictionary valueForKey:#"name"]];
// Set relationship
[employee setDepartment:department];
[department addEmployee:employee];
// Save data to core data
[self saveAction];
And here is the Error I get:
2010-04-08 16:06:11.295 Paparazzi2[2656:207] ERROR:saveAction. Unresolved Core Data Save error Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x3d1c6f0 "Operation could not be completed. (Cocoa error 1570.)", {
NSLocalizedDescription = "Operation could not be completed. (Cocoa error 1570.)";
NSValidationErrorKey = department;
NSValidationErrorObject = <Employee: 0x3d13a30> (entity: Employee; id: 0x3d16670 <x-coredata:///Employee/t8C950118-C388-4020-8CD9-1F49138A94193> ; data: {
picture = "mike.jpg";
name = "Mike Smith";
department = nil;
});
}
Thanks,
Tzur.

Hey Tzur, one problem with the code you provide is that you're trying to assign the department relationship a value that is not a managed object.
You should first create the Department object if it doesn't already exist, and use that object as the value of the department relationship.
I would replace this line:
[employee setDepartment:[object valueForKey:#"department"]];
With this:
Department *myDepartment = [self fetchDepartmentNamed:[object objectForKey:#"department"]
inManagedObjectContext:[self managedObjectContext]];
if (!myDepartment) {
/* no existing department with that name.. create one */
myDepartment = (Department *)[NSEntityDescription insertNewObjectForEntityForName:#"Department" inManagedObjectContext:[self managedObjectContext]];
[myDepartment setName:[object objectForKey:#"department"]];
}
/* assign the relationship to the department managed object */
[employee setDepartment:myDepartment];
This assumes you have defined a method -(Department *)fetchDepartmentNamed:inManagedObjectContext:] that will build a fetch request and get a Department with the name you provide (also assumes your department names are unique)
Alternatively, you could just add the employee to myDepartment's "employees" relationship, and it would achieve the same result.
Ok, so you have updated your code, and now you have this:
// Set relationship
[employee setDepartment:department];
[department addEmployee:employee];
You should have done, one or the other... not both. Check in the managed object model that you have selected the "Inverse:" of the relationship for both entities. If you set one of the relationships, Core Data will manage the reverse (inverse) of the relationship.

You can't use nil in an NSDictionary. If you haven't synthesized classes to represent your managed objects, you have to use the NSNull class. So, assuming employee is an instance of NSManagedObject and represents your Employee entity:
[employee setValue:[NSNull null] forKey:#"department"];
This is used to sever whatever relationship this instance of Employee had with its previous department. If you do set this to nil, however, the department attribute must be optional or you will have a validation error when you try to save your object graph (context).

Related

Core Data - Swift - Fetch Entities using One-to-Many relationship between them [duplicate]

I have a tableview(table1) of a coredata entity that shows a row of users, and another tableview(table2) that shows another entity that connected with (table1) entity in one to many relationship the (table2)shows up when tap in (table1) selected row, my question is how to show the data on (table2) for the user in selected row from (table1)
an example code:
let fetchdata:NSFetchRequest<entity2> = entity2.fetchRequest()
do {
userList = try myContext.fetch(fetchdata) [of entity1 object]
}catch{
print(error)
}
I already transferred the (table1) object to (table2) as nsobject .
is there any way to do my function in the right way
In your data model in Xcode you have defined the one-to-many relationship between entity1 and entity2, the name of that relationship is also a property on the entity1 class (and the same for the inverse relationship on entity2) that you can use to access all entity2 that belong to an entity1 instance.
So if the relationship is named `childrenĀ“then it could look like this
let user = userList[indexPath.row]
let entity2List = user.children //This is a Set
I solved the problem by using the NSPredicate to connect the entity2 data with that attribute object from entity1 by using this line of code
let myFetch:NSFetchRequest<entity2> = entity2.fetchRequest()
let myPredicate = NSPredicate(format: "toEntity1-relationship == %#", (myTransferdObject?.name!)!)
myFetch.predicate = myPredicate
do {
usersList = try myContext.fetch(myFetch)
}catch{
print(error)
}

NSPredicate for one-to-many relationship

Assuming a Department entity and an Employee entity with a one-many relationship
Assuming relationship called employees on Department and department on Employee
Assuming Employee has an attribute name
I need to fetch all Departments that don't have an Employee called "Bob"
Based on other answers on SO that I've read, I've tried this:
name = #"Bob";
predicate = [NSPredicate predicateWithFormat:#"ALL employees.name != %#", name];
but it doesn't seem to work. Any ideas?
Thanks.
Do do not need a subquery here:
name = #"Bob";
predicate = [NSPredicate predicateWithFormat:#"NOT ANY employees.name == %#", name];
For some reason, the "ALL" aggregate does not work with to-many relationships, but the "ANY" aggregate works.

NSPredicate to query parent and child

I have a data model setup with three entities, Course, Student, TestScores.
They are linked in too-many relationships like this:
Course <---->> Student <---->> TestScores
So a Course would have several Students, who in turn could have several TestScores (or no test scores)
The Course entity has a Name attribute. TestScores is a simple entity which just contains a testScore int attribute.
I want to be able to get an array of Students who have at least one textScore of 100, ordered by Course name. Is this possible with NSPredicate?
I think you could have your predicate as
ANY testScores.score == 100
Then put it all together in a fetch request:
NSFetchRequest *req = [NSFetchRequest fetchRequestForEntityNamed:#"Student"];
req.predicate = [NSPredicate predicateWithFormat:#"ANY testScores.score == 100"];
req.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"course.name" ascending:YES]];

Fetching and sorting across entities in Core Data

Lets say I have a model similar to the one below, and I need to Fetch all 'Person' of a specific Company.companyName sorted by personRole.roleWeight
This is the model I have at the moment:
Entity: Company
Attributes: companyName
Relationships: companyRole
Entity: Role
Attributes: roleName, roleWeight
Relationships: rolePerson, RoleCompany
Entity: Person
Attributes: personName
Relationships: person Role
Here is a simple diagram of the relationship:
Company --< Role >--< Person
Is there a way to do this?
If I need to modify the model I would be happy to do so. All suggestions are welcome.
Thanks,
You can't sort by role weight because it is possible to have more than one role that fits.
You also can't come at it from the Role (as opposed to the Person) because you have a many-to-many between role and person.
You should re-think your design because having that many-to-many there does not make much sense. A little bit of data de-normalization, changing that many-to-many to a one-to-many and duplicating the rolename and roleweight values would solve the issue.
Update
Assuming you changed the design to:
Company --< Role >-- Person
Then the solution gets much easier:
- (NSArray*)peopleSortedByWeightInCompany:(NSString*)company inManagedObjectContext:(NSManagedObjectContext*)moc
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:[NSEntityDescription entityForName:#"Role" inManagedObjectContext:moc]];
[request setPredicate:[NSPredicate predicateWithFormat:#"company.name == %#", companyName]];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"weight" ascending:YES];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
NSError *error = nil;
NSArray *roles = [moc executeFetchRequest:request error:&error];
[request release], request = nil;
NSAssert2(roles != nil && error == nil, #"Error fetching roles: %#\n%#", [error localizedDescription], [error userInfo]);
NSArray *people = [roles valueForKeyPath:#"#distinctUnionOfObjects.person"];
return people;
}
You basically fetch the Role entities sorted by weight and filtered by the company name. From that array of Role entities you then use KVC to gather all of the person objects on the other end of the relationship which will retrieve them in order.
Is there a reason that Role is the monkey in the middle and not person? It would make this task easier but maybe you have other things you are doing with the data that precludes this option. If each person had one role and one company you could then create a sort descriptor for role.roleWeight and use NSSet's sortedArrayUsingDescriptors: method on the Company in question's employees relationship set. That would give you a new sorted array with a list of all the Person entities attached to a given Company sorted by roleWeight (you might want to include a secondary sort descriptor to take care of matching roleWeights).

Accessing the relationship of a relationship with Entity Framework

I the School class I have this code:
from student in this.Students where student.Teacher.Id == id select student
The Student class there are two relationships: Teacher and School. In the School class I'm trying to find out all the students whose Teacher has a given id.
The problem is that I get
System.NullReferenceException: Object reference not set to an instance of an object.
in the statement
student.Teacher.Id
I thought of doing this.Students.Include("Teacher"), but this.Students doesn't have such a method. Any ideas how can I perform that query?
You show this line of code:
from student in this.Students where student.Teacher.Id = id select student
First, the = should be a ==
Is that just a typo?
Second, you don't need Include for the following, corrected query, if you don't dereference Teacher later on:
var q = from student in SomeObjectContext.Students
where student.Teacher.Id == id
select student;
LINQ to Entities doesn't require Inlcude for just the where clause.
You would need Include if you later iterated the results and dereferenced Teacher:
foreach (var student in q)
{
Console.WriteLn(student.Teacher.Id);
}
Third, you show this error:
System.NullReferenceException: Object reference not set to an instance of an object.
This is not a LINQ to Entities error. Is the error actually on that line of code, or is it somewhere else?
Also, what's this? If it's not an ObjectContext, then you are in likely LINQ to Objects, not LINQ to Entities. In that case, you wouldn't have Include available. If this is the case, then where does this.Sbudents come from?
I've found the debugger let me walk throu each iteration of the query:
from student in this.Students where student.Teacher.Id == id select student
so I've got to see student.Teacher.Id == id many times. Every time I was debugging it didn't happen and everything worked just fine. I turned the expression into:
from student in this.Students where student.Teacher != null && student.Teacher.Id == id select student
and it not only stopped crashing, but it worked fine as well (all students where selected as expected). I'm not totally sure why.
The Include clause can be used in linq to entities query. For example:
using (YourDataContext db = new YourDataContext())
{
var query = from s in db.Students.Include("Teacher")
where s.Teacher.ID == 1
select s;
//etc...
}
I guess that this.Students is collection pulled into memory already, so you might consider this code in the part you are retrieving students from dbs. If you want to load Teacher of the Student later on ( very important is that student entities are tracked by ObjectContext!) you can use the Load method on TeacherReference property, but for every student in this.Students collection separately:
student.TeacherReference.Load();
Hope this helps