Don't delete object of destination if it is in another Source in Coredata - ios

I have a many to many relationship where entity are Employee and Department. Everything is going good but when i am trying to learn the relationship delete rules,i couldnot find out the right way.
I want to remove all the employee of the department if Entity Department gets deleted.But not those employee who are in another deparment.
Cascade Delete the objects at the destination of the relationship. For
example, if you delete a department, fire all the employees in that
department at the same time.
But i dont want to remove the employee if they are already in another department.One teacher teaching Swiftmay be in many departments "Computer","Electrical","Civil".How can i acheive that..Tried to use cascade but that removes all the Employees which i have set destination as below:
EDIT: Tried using nullify but deleting Source causes the deletion of all related Destination. However, deleting any single one Source simply causes Destination to forget about that particular Source.I gues,I need something intermediate nullify and cascade?

The following relationships will do what you want I think (I don't have the ability to test the answer here, but don't have rights to just leave a comment so you get the suggestion as an answer)
Employee -> Department Deny (can't fire an employee that is still assigned to a department).
Department -> Employee Cascade (fire all employees you can fire when the department is deleted, ie no longer has a department).
But it seems more reasonable to me set Department -> Employee to Nullify, and then make a separate scan for unassigned employees to fire outside the delete department code. This would also support general maintenance checks for employees that have had all their assignments removed.

In the case of mine i should not set the delete relationship to Cascade.But instead make both delete rule to nullify.And Check as in
class Departments: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
override func prepareForDeletion() {
for teacher in self.teachers!{
if let tempTeach = teacher as? Teachers{
if tempTeach.departments?.count == 1{
self.managedObjectContext?.deleteObject(tempTeach)
}else{
print("this teacher is assigned to another department also so dont delete it")
}
}
}
}
}

Related

Can't insert same Entity (with different related properties-entity) to another Entity

I have an Entity Product who has a collection of Suppliers (which is also an Entity) and i have a Contract Entity who has a collection of Products. A contract can have multiple products, even 2 same products but with a different Supplier.
When i try to add this second product to my contract (same product but different Supplier this time) EF seems to ignore it and doesn't add it to the Contract.Products collection. No errors but it doens't add it. How can i bypass this behavior or setup my model/logic in a way that i can perform this action?
Code:
// THIS CONTEXT RESIDES IN A USING BLOCK
// THE updateContracts.Products ARE COMING FROM ANOTHER CONTEXT and we are receiving this entity as a parameter
// fetching the contract we are updating from database
Contract contractFromDB = ctx.Contracts.Include(s)(...)Where(p => p.ID == updateContract.ID).FirstOrDefault();
// list to populate the products we have added
List<Product> productsToAddToDBContract = new List<Product>();
foreach (Entity.Product product in updateContract.Products)
{
if (!contractFromDB.Products.Any(prod => prod.ProductName == product.ProductName))
{
// tried detaching it but didn't work
//ctx.Entry(product).State = EntityState.Detached;
productsToAddToDBContract.Add(product);
}
}
foreach (Product product in productsToAddToDBContract)
{
// get these from DB and add those to DB contract products
Product productToAdd = ctx.Products.Include(p=> p.Suppliers).Where(prod=> prod.ProductName == product.ProductName).FirstOrDefault();
if (productToAdd != null)
{
// HERE IS WHERE EF DOESN'T ADD THE SECOND PRODUCT (WHICH IS THE SAME BUT FROM ANOTHER SUPPLIER)
contractFromDB.Products.Add(productToAdd);
}
}
Thank you very much.
EDIT:
I tried to remove the line where i fetched my product from Database
Product productToAdd = ctx.Products.Include(...).Where(...).FirstOrDefault();
and just use the product i already fetched before and passing with the updateContract.Products, i got "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects."
so i went and attach the product from this iteration:
foreach (Product product in productsToAddToDBContract)
{
// removed this line:
// Entity.Product productToAdd = ctx.Products.Include(p=> p.Suppliers).Where(prod=> prod.ProductName == product.ProductName).FirstOrDefault();
// and added this one
ctx.Products.Attach(product);
contractFromDB.Products.Add(product);
}
then i received '"Attaching an entity of type 'Supplier' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate." Note that the product already has a supplier attached/included to it but from another context (same as the product's context).
This has to regarding the relation of Product and Supplier i think, or maybe because i am using different contexts, i don't know but i will investigate further and post back.
Feel free to contribute in any way of course :)
The issue was my model structure, i can't elaborate on how it was but the related entities relation was not setup correctly, i have created a new entity that holds a ProductId-SupplierId and others, tweaking it till i got what i wanted. The reason why i got these errors (stated above) was because i didn't attach all the related entities when performing actions, when i refered the proper related entities to my product, the errors went away... :)
Hope this helps someone and forgive me for the somewhat unclear solution, i am in the middle of this project and time is an issue, for know the company wants a working solution not a clean one so i didn't not everything down ...
Kind regards!

How to preserve connection between One-to-One records after deletion?

The question is a little bit tricky, so I'll try to explain it well.
At first, I have 2 entities, Books and Favorites where they are connected as One-to-One.
Books: bookID, title
Favorites: bookID
When I'm tapping on favoriteButton I execute this code:
let favorite = Favorites(context: context)
favorite.bookID = book.id
favorite.book = book
And when I want to check if the book is favorite, I do:
if book.favorite != nil
Now about the problem:
In some moments I have to delete ALL books and add them again, like resetting Books table and when I delete and add a book, it adds it with new ID(inner CoreData ID, not from the server) and thus I loose my connection between Books and Favorites, even the favorite books are still there and still keep all values.
How can I fix that problem? Can I somehow preserve that relationship between records even after deletion?
In web I would set foreign key as bookID and connect with it. In this case I would not loose that connection, but as CoreData connects via inner ID of records, not objects, I'm loosing my connection and cannot check book.favorite anymore, it returns nil.
I hope I could describe my problem. If not, please ask me

Core Data applying delete rule when updating

Beginner in Core Data here, there is something very basic in Core Data that I don't understand. I know the delete rules, such that if my object gets deleted, if it has relationships that are cascade for example, those relationships will get deleted as well. But what happens on updates?
Example:
Person has a relationship to a car. Delete rule is cascade.
Person --> Car
If Person is deleted, Car will be gone too.
But now what if Person just points to another Car, the previous Car will NOT be deleted and will just be dangling in the DB.
Any solutions to this?
I figured ideally you should delete the first car before setting the new one, but this is automatically done from a server fetch.
If this is the behavior you want in all cases you could override the managed object subclass method to set the new relationship. In the method, check first if another object exists and delete it if desired.
E.g.
-(void) setCar:(Car *)car {
if (Car* oldCar = self.car) {
[self.managedObjectContext deleteObject:oldCar];
}
[self willChangeValueForKey:#"car"];
[self setPrimitiveValue:car forKey:#"car"];
[self didChangeValueForKey:#"car"];
}

Vinelab/NeoEloquent - Realtionship to a exisiting node

I am using Vinelab\NeoEloquent for a new project for Neo4j. I have a registration where people can supply interests (as nodes). When registering, first the user is created, then the interests are created (if they don't exist), an given a relationship of "Interested_In" from the person to the interest nodes.
Problem I am having is when the interest already exists (unique by name property) and I just want to create a relationship from the person created and the Interest node.
In my People Model I have (Since 1 person can have many interests):
public function interest(){
return $this->hasMany('App\Models\Interest','Interested_In');
}
In my create user function in my repository I have a section where it searches the Interests and returns one if one exists. Problem is I don't know how to then create the realtionship from People to Interest. I try to save it as
$interests = explode(',',$input['tags']);
$int = new NeoInterestRepository();
foreach($interests as $interest){
$existing = $int->find($interest);
if(count($existing)){
$person->interest()->save($existing);
}else{
$my_interest = $int->create($interest);
$person->interest()->save($my_interest);
}
}
But I get 'Argument 1 passed to Vinelab\NeoEloquent\Eloquent\Relations\HasOneOrMany::save() must be an instance of Illuminate\Database\Eloquent\Model, instance of Illuminate\Database\Eloquent\Collection given'
I see the associate() function but its only for BelongsTo which doesn't seem to apply.
Any ideas?

iOS Core data: How to setup delete rule in relationship [duplicate]

I'm a little fuzzy on how the delete rules for relationships in Core Data work, at least beyond the simple cases described in the documentation.
Most of those cases, and most of the answers I've seen for questions here, use a model where the object on left side of a one-to-many relationship "owns" the objects on the right side: e.g. a Person has PhoneNumbers, and if you delete the person you delete all their associated numbers. In that kind of case, the solution is clear: Core Data will handle everything for you if you set the relationships like so:
Person --(cascade)-->> PhoneNumber
PhoneNumber --(nullify)--> Person
What I'm interested in is the opposite: A to-many relationship where the "ownership" is reversed. For example, I might extend the CoreDataBooks sample code to add an Author entity for collecting all info about a unique author in one place. A Book has one author, but an author has many books... but we don't care about authors for whom we don't list books. Thus, deleting an Author whose books relationship is non-empty should not be allowed, and deleting the last Book referencing a particular Author should delete that Author.
I can imagine a couple of ways to do this manually... what I'm not sure of is:
does Core Data have a way to do at least some of this automagically, as with relationship delete rules?
is there a "canonical", preferred way to handle this kind of situation?
You could override prepareForDeletion in your Book class and check if the author has any other books. If not you could delete the author.
- (void)prepareForDeletion {
Author *author = self.author;
if (author.books.count == 1) { // only the book itself
[self.managedObjectContext deleteObject:author];
}
}
Edit: To prevent deletion of an author with books you could override validateForDelete or even better: don't call deleteObject with an author with books in the first place
Rickstr,
Check below for the relationships to get your two criteria done.
Author -- (Deny) -->> Books
deleting an Author whose books relationship is non-empty should not be allowed
DENY: If there is at least one object at the relationship destination, then the source object cannot be deleted.
Book -- (Cascade)-- > Author
deleting the last Book referencing a particular Author should delete that Author
You cannot delete the Author, as our first rule is saying, if there are any Books which are non-empty should not be deleted. If they are not present the Author gets deleted.
I think theoretically it should work. Let me know, if this works or not.
Similarly to Tim's solution, you can override the willSave method in your Author NSManagedObject subclass. Note that if you do use Tim's solution, I highly recommend filtering the books set for books that haven't been deleted; this way if you delete all of the Author's books at the same time, the Author will still be deleted.
- (void)willSave {
if (!self.isDeleted) {
NSPredicate *notDeletedPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary<NSString *,id> *bindings) {
return ![(NSManagedObject *)evaluatedObject isDeleted];
}];
NSSet *filteredBooks = [self.books filteredSetUsingPredicate:notDeletedPredicate];
if (filteredBooks.count == 0)
[self.managedObjectContext deleteObject:self];
}
[super willSave];
}
The following worked for me:
Set the deletion rule on the 'book' relationship of your author entity to 'Deny' meaning that as long as there is a book linked to your author it cannot be deleted.
Subclass your book entity and override the prepareForDeletion() function as follows:
public override func prepareForDeletion() {
super.prepareForDeletion()
do {
try author.validateForDelete()
managedObjectContext?.delete(author)
} catch {}
}
Validate for delete will throw an error unless the book relationship is empty.
You can optionally handle the error.

Resources