I have a Department entity which has a one-to-many relationship to an Employee entity. During runtime, for each Department object I want to maintain a sorted array of Employee objects (sorted according to some attribute) which will be used as a model in various view objects.
From what I read there are two ways to represent the sorted array:
Rep1: defining a fetched property in Department (sorting is done by Core Data);
Rep2: maintaining an instance variable sortedEmployees (NSArray of Employee objects) in Department (sorting is done by my code).
Now my real problem is, when the app starts up I have a large number of Departments and Employees to fetch from the persistent store. I want to perform the sorting in the background so as not to block the UI.
I have tried several methods which all ended up in futility:
Method1 (using Rep1): In background threads (using an NSOperationQueue), fetch all Departments, then execute each one's fetched property. Then back in the main thread, find the corresponding Department object in the main thread's managed object context (MOC) (by NSManagedObjectID) and use the fetched property.
Problem with Method1: The fetched property in the main thread's Department object isn't populated even after the background Department's fetch is done, which means that it's re-fetched in the main thread Department! This defeats the whole purpose of background fetch. Am I wrong in assuming that a fetched property, once fetched, exists in all MOCs?
Method2 (using Rep2): In background threads (using an NSOperationQueue), fetch all Departments, then compute each one's sortedEmployees array. Then back in the main thread, find the corresponding Department object in the main thread's MOC (by NSManagedObjectID) and set the aforementioned array to this object.
Problem with Method2: The objects in the sortedEmployees array are Employee objects belonging to the background thread's MOC. When the main thread attempts to access them later, crash would occur with something like "NSManagedObject with ID ... has been invalidated." This is because each thread must have its own MOC (and its associated managed objects).
So how does one sort managed objects in the background at all, if the managed objects cannot exist across thread boundaries? Am I missing something?
Pius,
You aren't missing anything. You just need to convert your sorted array to the new MOC. Here's a fragment:
NSMutableArray *ma = [NSMutableArray arrayWithCapacity: array.count];
for (mo in array) {
[ma addObject: [moc objectWithID: mo.objectID]];
}
return ma;
That will migrate your array. As I understand it, this is an efficient process. Furthermore, you can depend upon the row cache for high performance access to properties.
Andrew
Related
So im using EFBulkInsert https://efbulkinsert.codeplex.com/
The problem is I have child objects I need to set the ID of the inserted parent objects.
Previously after inserting the parents objects I've tried to rely on the context to return the id's and set them on the child objects - then use EFBulkInsert to insert the child objects - every now and then the context gets confused even after recreating the context and I get the wrong id on the child objects.
Does anyone have a good pattern / strategy for setting the parent id on the child object I should mention i'm doing this for a batch of 1000 parent objects. So I don't particularly want to get the id's from the database after SaveChanges for the parent object unless it's performant.
The best idea I have is to add two temporary columns, firtst to hold an original Id, and second to hold parentId. After bulk insert update proper columns. This method requires privilages to modify the table.
I had a similar problem when I couldn't modify database, so I set AutoDetectChangesEnabled and ValidateOnSaveEnabled to false, but results were not very satisfied.
I'm creating an application in which I have several entities and now I need to filter the content of third combobox dynamically. I explain myself better. I have 3 combobox (building, floor and department), I would like first to show me all the buildings included, but the second should show only selected before the plans for the building, the last I should be select only the departments of the building and the plan you choose. How can I do this? To simplify attaching some photos.
You simply drill down with predicates, if you use single fetch requests to Core Data.
However, your relationships are not set up correctly. For example, there is an edificio attribute in Particelle. If it refers to an building, it should be a relationship to a Edifici object, not some kind of foreign key. There are no foreign keys in Core Data, just relationships.
If you do this, everything becomes much easier by using a NSFetchedResultsController. You can now simply traverse the object graph without any specific fetching.
The scheme could be something like this (maybe need to change the order):
Anno <--->> Particella <---->> Edificio <---->> AreaRischio
Now you can simply tell the fetched results controller to start fetching all Anno entities. Then you drill down with simple dot notation:
NSSet *listForNextTable = selectedAnnoObject.particelle;
and further with
NSSet *listForNextTable = selectedParticellaObject.edifici;
etc. You see, it gets really simple.
I'm doing this on a table with ~43k rows:
MyDbContext.Stores.Load();
MyDbContext.Stores.Local.Count.Dump(); //horrible performance!
I can see through the profiler that the first instruction fires up the select statement to fetch all rows. Actually the second instruction returns the correct value but after ~12 seconds, and it is not what I was expecting considering that all data should be in memory.
What is wrong (or what is its real purpose) with .Local in Entity Framework?
I think you should do this:
var stores = MyDbContext.Stores.ToList();
// stores is in memory after executing .ToList()
var count = stores.Count();
DbSet.Local Property
This property returns an ObservableCollection that contains all
Unchanged, Modified, and Added objects that are currently tracked by
the context for the given DbSet. The returned observable collection
stays in sync with the underlying DbSet collection and the contents of
the context. This means that you can modify the observable collection
or add/remove entities to/from the underlying DbSet collection (that
includes adding entities by executing a query) and both collections
will be synchronized.
This property is often used in data binding applications.
We have:
2 threads;
Each thread has its NSManagedObjectContext;
Each thread has its NSFetchedResultsController;
The delegates of NSFetchedResultsControllers not null;
In the main thread NSFetchedResultsController is used to work with tables;
In the secondary thread NSFetchedResultsController is used only for data access.
If you have added new objects, then changes are merged with the contents of the second thread NSManagedObjectContext.
If the objects were added, then NSFetchedResultsController updates the data in accordance with the request.
If the properties of existing objects were modified, then NSFetchedResultsController not update the data.
Why this happens?
Is your update code path separate from your insert code path? If so, have you verified that your update code path is saving changes on the context?
The mostly likely cause is that you did not implement the FRC delegate method:
– controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
... properly. Specifically you need to check for a change type of NSFetchedResultsChangeUpdate and then update the table row cell for that particular object with the changed value.
I have two instances of a program that manipulate same Northwind database.
When I add some records to the database from one of the instances (for example adding some orders to Orders table with a customer foreign key John), I can query these new records from the other instance of the program properly. The problem begins when I want to access these new records using John.Orders. In this situation, the second instance of the program does not see newly added records. What should I do?
The problem you are having is probably related to the time you keep the LINQ to SQL DataContext class alive. It should typically be destroyed after each unit of work you do with it (since it follows the 'unit of work' design pattern), which typically means after each use case / business transaction.
You are probably keeping the DataContext class alive during the entire lifetime of the application. The DataContext class is not suited for this, because it will cache all objects it had once retrieved meaning that your data will get stale.
Create a new DataContext class for every operation or every time the user opens a new form / screen.