some navigation properties null after importEntities - breeze

I'm using multiple managers in my application. Immediately after creating a new manager, I do this (pseudocode):
newManager.importEntities(existingManager.exportEntities());
in the existing manager, all entities are working as expected, and have all navigation properties populated correctly. but in the new manager, some entities have null navigation properties. the foreign key properties are populated, but the navigation properties are null.
existing manager:
ParentEntity
id = 1
childEntities = [childEntity]
ChildEntity
id = 2
parentEntity = parentEntity
parentEntityId = 1
new manager:
ParentEntity
id = 1
childEntities = []
ChildEntity
id = 2
parentEntity = null
parentEntityId = 1
this is happening for only a couple of entities. the vast majority of entities are correctly created in the manager with navigation properties as expected. but i can't see anything different about the entities that are not importing correctly. what could be causing this to happen?

What version of Breeze are you using?
We had a similar issue reported a while back, but it was fixed on version 1.3.2.
Back then, the issue was happening with backingStore and was related to the initializationFn parameter in registerEntityTypeCtor.
The workaround was to not set initializationFn, but to call a method with initializationFn code after the entity was created.
Once again, the issue above was fixed in version 1.3.2.
If you are using version 1.3.2 or above and still have the problem, please create a small solution reproducing the issue and send it to breeze#ideablade.com
=================================
Edit:
We just released version 1.4.1 which has a fix for a bug where EntityManager.importEntities did not not always fixup relationships completely in complex graphs.
You might want to try this latest version in case the fix on 1.3.2 does not solve your problem.

Related

Breeze - How to preserve changes to fkey property when using expand()

Breeze seems to be reverting the fkey property value when the query expands the associated navigation property -- even when using the preserveChange merge strategy. We need a way to preserve all changes.
Steps: Modify an fkey property of an entity in the cache. Query from the server with expand for the associated nav prop. When the query completes (data merged), the fkey property value is the saved value (was reverted).
To demonstrate the problem I ran the following code fragment two times. The first time loads the cache and modifies entities[0].ProjectAreaRef from 1 to 48. The second time performs the same query to test merging. After the 2nd query orig is 48 and val is 1. The change in the cached entity was overwritten.
var query = new breeze.EntityQuery().from('Issue');
query = query.expand('oProjectArea');
entityManager.executeQuery(query).then(function (x) {
var entities = x.results;
var val = entities[0].ProjectAreaRef;
if (orig && val !== orig)
console.log('orig:' + orig + ' val:' + val);
entities[0].ProjectAreaRef = 48;
I guess one might say that expanding into a nav prop is asking to refresh the relationship. Otherwise, the parent entity retrieved (based on the fkey value on the server) might no longer be associated with the child entity. But, that’s fine for us. Let the parent entity be merged into the cache even though it’s no longer associated with the child entity.
Regardless of whether this is a breeze feature or a bug (IMO it's a bug), how can I get breeze to preserve changes to fkey properties? If it's a bug, do you know of a workaround? If it's a feature, is there another feature that allows me to use expand() while preserving changes to fkey properties?
Using latest version: 1.5.3 ... although same behavior in 1.4.11.
Ok, this was a bug and is now fixed in the latest GitHub repo. This fix will also go out in v 1.5.5 (sometime next month). Nice catch!

Grails 2.4.4 upgrade data binding issue

A pattern we use here for large dynamic forms is to allow the client to edit pretty much the entire graph including children and removal and addition of children is handled in javascript. Failing use case is when user deletes a property of one of the children and for this example we'll say the email address of the second child is removed and the parm is sent empty.
Example Class Structure
Domain X
Long id
String name
SortedSet<Child> children
Domain Child
Long id
Integer position // used to sort
String name (required)
String email (required)
Parms are named in this manner:
x.id = 1
x.name = 'blah'
x.children[0].id = 1
x.children[0].position = 0
x.children[0].name = 'childa0'
x.children[0].email = 'child0#any.com'
x.children[1].id = 2
x.children[1].position = 1
x.children[1].name = 'child1'
x.children[1].email = ''
Current stored values:
x.id = 1
x.name = 'blah'
x.children[0].id = 1
x.children[0].position = 0
x.children[0].name = 'childa0'
x.children[0].email = 'child0#any.com'
x.children[1].id = 2
x.children[1].position = 1
x.children[1].name = 'child1'
x.children[1].email = 'child1#any.com'
The pattern to update has been:
Browser
user edits all and submits changes
parms are sent as shown above and in this use case user accidentally deleted email address that is required (yes submit could be prevented in this simple case but there are more extensive validation rules and this is just a simple example to illustrate issue)
Server
retrieve current parent domain class using id parm
check current stored version against edited version and ensure it has not changed
apply all parms to current domain object in this manner: x.properties = parms.x
This is where it breaks down. With Grails 2.2.2 the email property of child[1] is updated to an empty string and running validation catches the error. Grails 2.4.4 does NOT update the child value. I know the data binding changed and perhaps there are valid reasons for this change but I can't fathom it. This has worked from 1.3.6 to 2.2.2.
One thing to note is that if I do not retrieve the domain class and instead create a new instance with the parms passed in all of the child parms are set correctly.
X x = new X(params.x)
This seems to indicate some Grails issue as the user guide talks about both methods as equal - one to use for new instances and the other for updates but the examples clearly demonstrate that both methods should handle indexed child properties.
Any help or thoughts are appreciated!
I ran into this problem my self upgrading an application from 2.2.4 to 2.4.4.
Setting grails.databinding.useSpringBinder = true for config.groovy
and adding grails-web-databinding-spring to the pom worked flawlessy for me.
Obviously doing this was a lot less hassle than rewriting all the binding code :-)
Looking into this further Grails 2.4.4 documentation states that it will bind sets positionally if they are indexed (ie. [0]).
http://grails.org/doc/latest/guide/single.html#dataBinding
The section titled "Data Binding and Many-ended Associations" states that these parms will be bound and that new instances of the set will be created if their index is greater than the current set. This was true for me in Grails 2.2.2 but NOT in 2.4.4.
HOWEVER, earlier in the documentation in the section "Binding To Collections And Maps" it states clearly: "When updating existing elements in a Set the Map being assigned to the Set must have id elements in it which represent the element in the Set".
If you are upgrading to Grails 2.4.4 and have any complex form data binding I suggest you look there first to decide whether to upgrade. Due to these changes in data binding this has become the most complex upgrade we've encountered yet with Grails.
The other disappointment with 2.4.4 is that it states you can override the new binding and fall back to the old spring binding but you cannot past 2.4 - at least from what I can gather from others' posts and my attempts. Setting the config property as documented and adding the dependency required resulted in startup runtime errors.
Apparently older versions of grails prior to 2.4.4 would match the child instances to params positionally, i.e. using the indexes ([0],[1], etc.) to match the children not caring if the IDs of the instances matched. The new binder does want the IDs of the instances to match before binding. I had never realised I was not placing the ID in a hidden field as it always worked perfectly.
I had a similar problem after migrating to Grails 2.4.4 (from 2.2.2). After a lot of reading and seaching (you must to definitly read http://docs.grails.org/2.4.4/guide/single.html#dataBinding mencioned in user2863513 post) I've resolved it in a very simple way
In my application I had nested domains relationships. Example:
class A {
String name
static hasMany = [b:B]
}
class B {
String name
static hasMany = [c:C]
}
class C {
String name
}
Due to the form complexity, class C object were not binding correcly. I've resolved this defining the C atribute on class B as a List (remember, has many relationships are implemented as Sets by default). So, my class B changed to:
class B {
String name
static hasMany = [c:C]
List c
}
And voila! Everything works perfectly again.

Breeze EntityManager not removing imported detached entites?

I'm using Wards excellent example of implementing many-to-many with breeze. (i'm going off the plunk in his post, cant link to it from here dont know why)
Everything is working great, changes are always saved to database correctly.
I am using 2 different (BreezeJS) EntityManagers: one for edits, and one as my "master". On saving the editor Em to database, it then imports all changes into the master Em so it all stays in sync. This has been working wonderfully for all my other functions.
However, when saving the many-to-many mapping, for some reason any deleted mappings are not removed from the master Em. (When I add mappings they correctly show up in the master Em right away).
Do I need to take another step to get my master Em to remove imported detached entities?
(FYI, everything is saving correctly to server, if I do hard page refresh, all my entities show up correctly).
My code for deleting entity on editor Em:myEntity.entityAspect.setDeleted();
Function below will export changed entities from editor Em:
function exportToMasterAfterSavingSuccess(saveResult){
if(saveResult.entities)
masterEm.importEntities(manager.exportEntities(entities, false));
}
And the corresponding import function on master em:
function importEntities(entities){
var imported = manager.importEntities(entities,{ mergeStrategy: breeze.MergeStrategy.OverwriteChanges});
}
Thanks for uncovering a bug. One shouldn't be able to export or import a detached entity. In future, Breeze will throw if you attempt to do either.
Now I'll discuss your issue, your aims, and what I recommend that you do.
Update master EntityManager after saving deleted entities
As I understand it you maintain a masterEm which has only the saved state of entities. You make and save your changes in a separate editEm. You import entities that you will change into the editEm, make changes, save them, and (if the save is successful), you export the saved entities from editEm and import them back into masterEm. This is a common "sandbox editing" pattern.
Trouble arises when you delete an entity in editEm. After save, that entity is "Detached" in the editEm but it's still in an "Unchanged" state back in the masterEm. How do you communicate the fact that the entity is deleted and remove it from masterEm?
This dilemma exists independent of the "many-to-many" scenario that inspired your question.
I can see why your practice of importing the now-Detached entity from editEm to masterEm seemed to work. Doing that in v.1.5.3 caused the corresponding entity in masterEm to change to the "Detached" state ... which is what you wanted. The bug, as you saw it, was that the importEntities method didn't handle update of navigation properties properly when the imported entity is in a "Detached" state. You proposed teaching importEntities to "do the right thing" in that scenario.
What actually happened here is that you discovered a bug. You should never have been able to export a "Detached" entity and you shouldn't have been able to import one either. Breeze should have thrown an exception when you tried to export or import a "Detached" entity.
There are all kinds of reasons why asking an EntityManager to export/import "Detached" entities is a bad idea. I leave explication of those reasons for another day.
Rather than "solve" the problem of importing related "Detached" entities, we will throw an error.
This means that your partial solution will cease to work, leaving you apparently worse of than you are today. Fortunately, I have an alternative approach for you. I've written this utility function and tested it in DocCode:
function updateMasterWithSaveResult(masterEm, sourceEm, saveResult) {
var imports = [];
var deletes = [];
saveResult.entities.forEach(function(entity) {
if (entity.entityAspect.entityState.isDetached()) {
deletes.push(entity);
} else {
imports.push(entity);
}
});
var exported = sourceEm.exportEntities(imports, {
includeMetadata: false,
asString: false // as JSON
});
masterEm.importEntities(exported);
deletes.forEach(function(detached) {
var entity = masterEm.getEntityByKey(detached.entityAspect.getKey());
entity && entity.entityAspect.setDetached();
});
}
Updated documentation
I just added this to our "Cool Breeze" documentation almost verbatim.
Please try this with the latest released version of breeze (1.5.3). A very similar bug was fixed there.
And just to be clear, and export can never include 'detached' entities ( only 'deleted' ones). Detached entities are, by definition, no longer attached to an EntityManager so the EntityManager no longer knows anything about them.
I'm having the exact same issue. I'm not doing many-to-many but still doing edits/adds/deletes in a separate entityManager.
I can confirm that the export DOES include Detached entities, and on import into the master entityManager with MergeStrategy.OverwriteChanges, those entities do become detached.
However, the newly detached entities are still associated with any related entities.
Reattaching and detaching seems to get everything back in sync as far as I can tell:
var result = manager.importEntities(imports);
result.entities
.filter(function (entity) { return entity.entityAspect.entityState.isDetached(); })
.forEach(function (entity) { manager.attachEntity(entity); manager.detachEntity(entity); });
Breeze 1.5.3
Github issue with pull request:
https://github.com/Breeze/breeze.js/issues/75

my sqlite3 DB doesn't show column values in device but it does in simulator

My DB is not getting copied over to my device, but it does to the simulator.
Here is what I am doing:
Create a new sqllite3 db from terminal:
sqlite> create table myTable (id integer primary key, name text);
sqlite> insert into myTable (name) values ('john');
sqlite> select * from myTable;
1|john
This creates a db in this path: users/John/iosApp.db
Then I close the terminal and copy that db to my xamarin project and set its buildAction to 'content'.
Here is my model:
[Table("myTable")]
public class MyTable
{
[PrimaryKey, AutoIncrementAttribute, Column("id")]
public int ID {get; set;}
[Column("name")]
public string Name { get; set; }
}
And I do this to copy the db to the Document folder:
string pathToDatabase = "iosApp.db";
userPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), pathToDatabase);
File.Delete (userPath); // delete first and copy next
File.Copy (pathToDatabase, userPath);
var myDB = new SQLiteConnection (userPath);
MyTable myTable = myDB.Get<MyTable> (1);
then I run the app and I set a breaking point after the last line in the code above and I hover over the myTable:
if I am using the simulator, I see the schema and value of 1 for ID and 'john' for Name.
if I am using the device, I see the schema but 0 value for ID and null for Name!
Looking at the path when I am using the device, points to this:
"/private/var/mobile/Applications/277749D4-C5CC-4BF4-8EF0-23B23833FCB1/Documents/iosApp.db"
I loaded the files in using iFunBox and the db file is there with the exact size
I have tried all the following:
Clean All in the project
Rebuild All
removed the 'debug' folder from the project
restarted Xamarin
and even restart the machine
But still the same behavior, what else should I try to be able to see the values of ID and Name?
my sdk version is attached
UPDATE:
After a lot of changes and cleaning up, I managed to display the value of all columns except the identity column displayed as 0. Puzzled, I went back to the xamarin sample project: http://developer.xamarin.com/recipes/ios/data/sqlite/create_a_database_with_sqlitenet/
it displayed the value of the identity correctly.
Trying to bring in similar code to my project, but no success.
To role out the possibility of version issue, I went and downloaded the latest sqlite from this link:
http://components.xamarin.com/gettingstarted/sqlite-net/true
The same behavior... I created a whole new page in my project, used the references the sample used and only has the code to create a sample table. Same behavior, the identity value is displayed in the other project but not mine. This leads me to conclude that there is something completely is wacky in my project. Now I am considering creating a whole new project and move my files to the new one after making sure first that the piece of being able to see the value of my id in my model shown up. Stay toned, I will make sure to update this thread.
If you have any pointers, please share them
I couldn't find a solution to my problem, but I found an alternate method to create the DB that turns out to be even nicer than the original one.
One important thing to note is that in the original problem (details above), the DB code was working for months since I started developing the application. I don't know when it started behaving badly, but I suspect it was due to the download of the new Xamarin 3.0. I can't think of any other reason.
So, to solve my issue, There are two main things I did:
I followed this link on how to create DB and tables and do CRUD operations: http://components.xamarin.com/gettingstarted/sqlite-net/true
This method seems to be the newest way to create DB. It was: published on
June 24, 2014. It has an SQLite.dll, whereas my previous solution was using a SQLite.cs file. So, now I am creating my DB now at runtime.
Something didn't work still with the new method. It was giving me an object null exception error. I didn't spend much time investigating about it. When I provided values for my primary key and identity values, the error went away. Actually, this could have been the solution to my previous problem. I would have tried providing the identity values against the old code, if I am not already happier with the new method.
I hope this helps someone.

Cannot insert new Employee entity using InsertOnSubmit()

I'm facing this exception An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported. when I try to insert a new entity into my Employees table (the master one).
There is a relationship between the master Employees table and the details Orders table, and I'm sure that the relationship between these two tables (and specifically Employee.Orders EntitySet) is the cause of the problem since when I removed the relationship, it returns back to insert into Employees table with no problems.
When I searched for the problem, there was this blog post which I tried to implement but my case is a different than the one in the blog post in these items:
He faces the exception when tries to update (while I try to insert).
The tables architecture is different.
how can I solve this problem?
Here's the insertion code:
Employee emp = new Employee();
emp.Name = empName; // empName is a local variable
// What should I default emp.Orders to?
dc.Employees.InsertOnSubmit(emp);
dc.SubmitChanges();
P.S: My DataContext is defined on class-level in my repository and the exception is being thrown when I call dc.SubmitChanges();. and I didn't Attach any object why does it say that?
Here is an article explaining what you need to do using the Attach and Detach methods:
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx
I am guessing it is trying to save something else besides just the employee object or you aren't showing us the full code in your repository. When you instantiate your DataContext object (dc) try setting DeferredLoadingEnabled = false, and ObjectTrackingEnabled = false and see if it works. If it does, try watching the SQL code in SQL Server Profiler and see if it is modifying other objects that may have came from a different context like the message says.
var dc = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
My bet is on the primary key.
Are you sure the primary key is also set on auto increment?
Did you
try changing the name, does it work then?
What happens if you remove
all rows from your DB? can you insert one then?

Resources