Core Data delete object on reassignment - core-data

In Core Data, is it possible for have objects automatically deleted on reassignment?
For example if I have
class Person: NSManagedObject {
#NSManaged public var name: String?
#NSManaged public var group: Group?
}
class Group: NSManagedObject {
#NSManaged public var people: Set<Person>?
}
and assign a new set to a group's people property:
group.people = [some new set of <Person>]`
and then save the current context. I would like all the previous Person objects in the previous set to be deleted.
Do I have to to call delete on them manually before the new assignment or is there another way to do this?

As explained in the comments, it turns out that the Cascade Delete Rule does not delete a Group's Persons unless you delete the Group. So I've gutted my answer and replaced with this.
If you don't want to delete the Group, you must delete each Person and remove it from the relationship. It is not that bad, though…
if let people = group.people {
for person in people {
if let person = person as? NSManagedObject {
managedObjectContext.delete(person)
}
}
}
group.mutableSetValue(forKey: "people").removeAllObjects()
NSBatchDeleteRequest might also work if you configured it with a predicate to filter only Persons in the relevant group, but unless you have thousands of Persons to delete I would not bother with it.

Related

Non-optional to-many relationship allows nil or empty set

I have a sample data structure:
Table "Groups" and table "Items". Groups can contains zero or more items and item must be linked to minimum one or more groups:
Groups |--------- to-many, optional ---------->>|Items
---------|<<------- to-many, non-optional! -------|--------
groupName| |itemName
items | |groups
I create NSManagedObject subclasses. Both are trivial but here is Items:
class Items:NSManagedObject {
#NSManaged var itemName:String
#NSManaged var groups:Set<Groups> //attention: there is no ! or ?
}
In code below I expecting to catch error:
var item = Items()
item.itemName = "AAAA"
do {
try Items.moc.save() //moc - static field in Items "linked" to ManagedObjectContext
}
catch {
print(error)
}
But there is no errors! item saves to CoreData with empty groups. I can't figure out why? But if I make Items to Groups relation non-optional to-one (in class Items groups field became #NSManaged var group:Groups) exception throws as I expected.
I know, that I can implement in Items class function validateGroups, where I can check if groups nil or empty, but I want to know: is there my mistake that I cant find or it is Core Data bug (or feature)? More, I like to implement storage logic into database (in sql I very like triggers, I can't live without foreign keys, constraints etc).
So, please, help me to understand this. Thanks!
Problem solved, thanks to Willeke!
When I set minimum count in Item's entity relation properties problem disappeared.

How to skip a field during map stage?

I'm having list of employee objects - List
I need to convert it into list of employee transfer objects - List
Assume a field "password" exist in both the classes.
In few cases i need the password needs to be included from Employee → EmployeeDTO
In few cases i don't need the password and want to be excluded from Employee - EmployeeDTO.
Sample Code Snippet:
List<Employee> employees = employeeRepository.findAll();
// Define the target type
Type targetListType = new TypeToken<List<EmployeeDTO>>() {}.getType();
List<EmployeeDTO> employeeDTOs = modelMapper.map(employees, targetListType);
Please let me know how to skip the fields on mapping/copying.
Take a look the official user manual of Conditional Mapping.
In brief:
You would need to add a new Mapping and use a Condition. Your source and destionation would be:
Source: Employee
Destination: EmployeeDto
First create and custom your Condition. It would be something like this:
Condition<?, ?> isNotZero = new Condition<PersonDTO, Employee>() {
public boolean applies(MappingContext<PersonDTO, Employee> context) {
//Your conidition
return context.getSource().getEmployeeId() != 0;
}
};
Then add Mapping and use the condition:
modelMapper.addMappings(new PropertyMap<PersonDTO, Person>() {
protected void configure() {
when(isNotZero).map(source).setEmployee(null);
}
});
You can find this examples in the ModelMapper GitHub repository. The author has done few more and are well explained:
Link to above example
Here is how I skip fields during the mapping stage:
ModelMapper modelMapper = new ModelMapper();
modelMapper.typeMap(EmployeeDTO.class,Employee.class).addMappings(mapper -> {
mapper.skip(Employee::setPassword);
});

To aggregate or not - order/orderline

About Domain Driven Design, Order and OrderLines are always seen as an aggregate, where Order is the root. Normally, once an order is created, one cannot change it. In my case however, that is possible. Instead each order has a state determining whether the order can be changed or not.
In this case, are both Order and OrderLines their own “aggregate root”? I need to be able to update order lines, so I figure that they should have their own repository. But I do not want to retrieve order lines, and persist them without the order. So this indicates that there’s still an aggregate where Order is the root with a factory method to create order lines (Order.CreateOrderLine(quantity, text, …).
Another approach could be to update the Order when the order lines collection has been modified, and then call UpdateOrder(Order). I would need some way of detecting that only the collection should be updated, and no the Order itself (using Entity Framework).
What do you think?
Order lines shouldn't be an aggregate of it's own, and doesn't need it's own repository. Your aggregate should be setup something like this...
public class Order
{
private List<OrderLine> _orderLines;
private OrderState _orderState;
public IEnumerable<OrderLine> OrderLines
{
get { return _orderLines.AsReadOnly();}
}
public OrderState Status
{
get { return _orderState; }
}
public void DeleteOrderLine(Guid orderLineID)
{
if (Status.IsProcessed)
throw new InvalidOperationException("You cannot delete items from a processed order");
OrderLine lineToRemove = _orderLines.Find(ol => ol.Id == orderLineID);
_orderLines.Remove(lineToRemove);
}
public void AddOrderLine(Product product, int quantity)
{
if (Status.IsProcessed)
throw new InvalidOperationException("You cannot add items to a processed order");
OrderLine line = new OrderLine(product.ProductID, (product.Price * quantity), quantity);
_orderLines.Add(line);
}
}
Entity framework has some built in features to detect changes to your object. This is explained here (conveniently with an order/order lines example): http://msdn.microsoft.com/en-us/library/dd456854.aspx

Fill CoreData ManagedObject property based on another object property

I have app that stores tree structure in CoreData.
There is an ManagedObject, "Item", and it has attributes:
itemId (string)
List item
title (string)
parentId (string)
parent (relationship to Item)
parentTitle (string)
parentId points to another Item object.
How do I make property parentTitle to be filled automatically with title of parent Item ?
While Martin's suggestion is a good solution for derived values, my question on yours is, why would you want this? You are not manipulating the value from the parent at all, ever. Since you are just accessing it, access the parent directly via KVC such as:
Item *item = ...;
NSString *title = [item valueForKeyPath:#"parent.title"];
//Do something with title
The only time you would want to use the keyPathsForValues... functionality is if you are changing something based on that value. If you are just accessing it, use KVC directly.
This is a possibility to achieve the desired functionality:
// implement in Item.m
// manages KVO notifications
+ (NSSet *)keyPathsForValuesAffectingParentTitle
{
return [NSSet setWithObjects:#"parent.title", nil];
}
// getter for parentTitle
- (NSString*) parentTitle
{
return [self valueForKeyPath:#"parent.title"];
}
additionally declare the property for parentTitle as readonly in Item.h
There is no need to declare a Core Data attribute "parentTitle".
The only problem I see with this solution is the following:
Item A is parent of item B
A gets turned into fault
B is still active and some View is bound to B.parentTitle
The view gets a notification because of the dependency declared with keyPathsForValuesAffecting, still object A is already faulted (and on shutdown unable to be unfaulted again) does Core Data manage such faulting&observation problems automatically?

Saving Parent and children

I have a couple of tables in my schema with PK and FK relationships. I have created the DAL using the SubSonic generator. If I create a new parent and its children, how should I save both? Separately or in one shot?
e.g.
Parent.Save();
ChildCollection.SaveAll();
I tried the above, but it does not work because ChildCollection does not have its parent's ID. Is it that I have to assign parent IDs for each child myself or is there an option to save it all in one shot?
Assumption: That your Primary Keys are auto generated by your Data Base. If so, what you will need to do first is Save() the parent and then populate the ParentID property in each of the objects in your ChildrenCollection. Once you have done that, you will be able to Save() your ChildrenCollection.
Parent.Save();
ChildCollection.ForEach(x => x.ParentID = Parent.ParentID);
ChildCollection.SaveAll();
You can use partial classes with custom overload of save function to provide the desired functionality.
public partial class Class1
{
public void Save(bool childern)
{
Save();
if (childern)
{
//based on primary/foreign key SubSonic provides
//methods to fetch child collections related to
//this (primary key) table.
ChildernCollection col = Childerns();
col.SaveAll();
}
}
}

Resources