I use Xamarin Forms with azure-mobile-apps-net-client with the .net backend. What I noticed is, that if I change a value in my mobile app for my model like
var dog = get_dog_from_sqlite_database();
dog.Color = "black";
and call
await dogTable.UpdateAsync(dog);
and then sync with the server, the Delta<Dog> patch object in the
public Task<Dog> PatchDog(string id, Delta<Dog> patch)
method in the backend, contains every property from my dog model, although changing just one value.
Is it possible to change some settings, that just changed values are patched to the backend? I ask, as I have to do some restrictions on who can change what values, so my backend code would be cleaner as I just have to look if a forbidden property was changed and then throw an exception.
No - when we do offline sync, we don't necessarily know which fields have changed - we don't keep that granular information. We just keep the new record. You can check out the operations queue in the SQLite database to confirm this.
Related
I use the repositoryFactory in a custom plugin's Vue file in Shopware 6. When I save an entity I do this:
this.settingsRepository
.save(this.setting, Shopware.Context.api)
.then((result) => {
In case the person sends the form and this function is called, I want to get back the id of the setting in case the person hit's the save button again. But then the entity needs to be updated and not created. As I see from the response, there is an id, but it's in config.data -> json serialised.
How's the default way of solving this issue?
The best practice would be to re-fetch the entity after persisting it. This is because some entities may have fields that get automatically computed or indexed server-side and you'd probably always want to have the entity in its actual current state. If you're absolutely sure you don't want to fetch the entity again, you could manually set the _isNew flag to false after persisting:
this.setting._isNew = false;
This will then cause the save function to use the update instead of the create route. Keep in mind that this is actually kind of an internal property, as there is no setter for it and as already mentioned fetching the entity again is encouraged.
Also you shouldn't have to worry about the id. It should've already been generated client-side and set to the entity, when using the repository to create a new entity like that:
this.setting = this.settingsRepository.create();
I recently load several versions of MEANJS (meanjs.org) to understand the file structure better and view the changes.
In 4.0, articles.client.controller.js to be specific they have:
and I'm able to make changes to new Articles there as I appended new fields to the mongoose Schema.
$scope.create = function () {
// Create new Article object
var article = new Articles({
title: this.title,
content: this.content
});
In 4.1, it comes like this.
// Create new Article object
var article = new Articles({
title: this.title,
content: this.content
});
Now with 4.2, I don't see that in articles.client.controller.js,
vm.article = article;
I have my modified Schema version. How to make changes to the creation of a new Articles object? This is a good question for upgrading app from 4.0, 4.1 to 4.2.
It’s changed slightly.
Trying to use the resources directly as was done in version 4 may cause problems where the page is ready but not the resource (Article).
To get around this problem angular uses resolves which which use promises to handle the timing issues.
The important thing to know is that the promise will give you some answer at some point in the future - Just, it may not be the answer you'd like!
Either way, it always tells you once it has the answer - or more correctly once it has resolved.
Angular uses the promises to help out with the timing issues mentioned above. The resolves are keyed to a promise and will only load the controller once the resolve(s)... erm... resolve!
This means we will always have articles when we expect.
Promises, resolves - Let me at'em - Let me at'em!!
The resolves option is used in the updated articles.client.routes. Here we see that articleResolve is keyed to getArticle which isn't a promise itself but is instead a function which returns one (which is just a good!)
If we look at the lines below we can see how we create this promise returning function. It's a function which uses Angular's $stateParams (to inspect the state) and fill in the articleId for the requested article. We get the articles using the injected and familiar Articles service.
In your case you want to know how new articles are created so we must travel a little further into the articles service which has recently been updated.
This is almost the same as the Articles service which you are used to using, however the additional lines add an extra method to this service which allow it to create, or if existing to save the article details.
These lines are how we extend a service in angular and the below implementation basically checks the article to see wether it has the ._id property. This is the string representation of the .id property that all saved mongo db documents get.
It uses this information call the appropriate method.
Finally, back where we began
In the controller we see the earlier created promise key articleResolve used as the second injected argument; As if to say "when you have this articles service resolved use it as this second parameter when I'm injecting the arguments".
When we look at the controller definition, we notice that the corresponding second parameter is named article.
Background: Within any controller this actually points to the scope (or $scope). As convention†, and to make things in angular look like standard JavaScript where we often say var that = this, we create a variable to reference our scope.
Within the controller we attach this article to the scope so that it is accessible in the views via vm.article.
Fin!
† Graze at Papa John's style guide when you get chances and slowly evolve your code style to match it. It will help you avoid traps and as a side effect makes lot of the angular code examples/tutorial more understandable, especially where the authors also follow it.
I am trying to implement iCloud sync in my Core Data app. I am not that pro in programming and this is really an advanced topic I learned... I found that Core Data sync Framework "Ensembles" by Drew McCormack. It seems to make iCloud Sync much easier.
I integrated it in my App and syncing does work quite well as long as I add new objects to my Core Data model. But when I delete an object, it creates duplicates. And then duplicates from duplicates. I ended up having the same Entry (object) like 3-4 times...
Why is that? What am I doing wrong? I did some research and my guess is that global identifiers could solve this?
What are global identifiers? My guess is that they help to avoid duplicates!? But how do I set this? I really have no idea, did a lot of research but couldn´t find an answer to that.
Thanks for help!
Update:
Thanks for help! I read the readme and the book, but since i am beginner not everything is clear to me.
I think I understand the use of global identifiers in Ensembles now, but I don´t know if I´m doing it correctly.
If I understand it right, I have to assign an identifier to each object. I can do this by storing it in an attribute. This identifier can be anything as long as it is unique and a NSString?
In my app the user can store different things, let´s say name, text, title, date and so on. The app is based on the Master-Detail-View template in Xcode and uses Core Data. My Core Data model has only a single entity with some attributes, most are strings and a NSDate. No relationships or anything. If the user hits "+" a new object is created and I store the things the user enters in the attributes.
What I did to add global identifiers is to add a new attribute that stores it.
So when a new object is created i do
/// I did find that to use as identifier !?
NSString *taskUniqueStringKey = newManagedObject.objectID.URIRepresentation.absoluteString;
/// and store it in the attribute.
[newManagedObject setValue:taskUniqueStringKey forKey:#"coreDataObjectID"];
Then i use this:
- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble globalIdentifiersForManagedObjects:(NSArray *)objects
{
return [objects valueForKeyPath:#"coreDataObjectID"];;
}
This seems to work for me. But am I doing it right? Is this the right place to assign a global identifier? I have no awakeFromInsert !?
If this is working, I got the next problem. My app is already live and older entries that the user saved before the update will be missing the global identifier. What can I do about that? I thought what I already got and what is unique and the only thing I can think of is an attribute that saves [NSDate date] when the object is created.
I was trying to use this but I failed because Ensembles will only accept NSString and not NSDate!? Can I use this date attribute, is this unique enough and working as gloabl identifier? And if yes, could you please give me code example in how to convert this from date to string?
Syncing with Ensembles works quite good. No duplicates anymore, you can just switch off iCloud and the entries stay and switch it on again and it syncs like it should without loosing locally stored objects or so. Ensembles is really cool! I am seeing some minor strange behaviors like sometimes sync takes long, sometimes it´s really quick and if I edit things in a short time period on two different devices it gets a bit messed up like an object that I just deleted reappears. But I guess that´s normal? If I take some time between using the app on the different devices everything works fine.
Do I understand it right, there is only that one method to call for sync:
- (void)syncWithCompletion:(void(^)(void))completion
{
if (self.ensemble.isMerging) return;
if (!self.ensemble.isLeeched) {
[self.ensemble leechPersistentStoreWithCompletion:^(NSError *error) {
if (error) NSLog(#"Error in leech: %#", error);
if (completion) completion();
}];
}
else {
[self.ensemble mergeWithCompletion:^(NSError *error) {
if (completion) completion();
}];
}
and you just call it if needed? There is nothing else like doing merge without leeching before, or a method like "this is the actual status - save it like it is now" ?
There are different points in the app where you want to sync. On app start and when terminating will be a good point. In my app there are two points where I should sync I guess: when adding an object and save it to Core Data and when I save changes to the object. I could also provide a button like "sync now". Is this a good approach and do I always just call
[self syncWithCompletion:NULL];
Another question that came up. Can I exclude objects from sync with Ensembles? My app loads tutorial entries as objects once on first app start. I don´t want to sync them if that´s possible somehow?
Thanks a lot for your help! If I could help you with anything like localizing in german or so let me know ! ;)
Yes, this is almost certainly due to not setting up global identifiers for your objects, or at least not doing it properly.
When you leech your ensemble, the local persistent store is imported into the sync data. Without global identifiers, Ensembles will assign random ids to your objects, so it can track them across devices.
Duplicates arise when you leech a second device that has the same data. Ensembles has no way to know that the data represents the same logical objects as on the other device, so it again assigns random ids. Effectively, it treats the objects on each device as being completely independent, so that all end up in your data set after syncing.
The solution is global identifiers. By implementing a CDEPersistentStoreEnsemble delegate method, you can provide Ensembles with global ids, which it can use to identify which objects on different devices belong together.
What should you use for global ids? Often, just a UUID, though for singleton like objects you will just want to pick an id.
You can initialize them in awakeFromInsert. You can store the global ids in attributes on your entities. (Note that if you are migrating an existing app, you will want to check with a fetch if the global ids have been generated BEFORE you try to leech the store for syncing.)
More details are in the README on GitHub and in the book at leanpub.
Update
To answer your update questions:
Yes, an identifier just has to be a string, and immutable. It should not change once assigned.
The NSManagedObjectID is not a very good global identifier, in that it will be different on different devices. We really want something that is global across devices.
If you are starting from scratch, using NSUUID is a good approach. Just create a unique id, and store it in the object.
If you have an existing app, and it has been syncing via another mechanism, you need to come up with a way to provide the same global identifiers on each device. One way to do that is mash up the object properties in some way. Usually that will give you a pretty-close-to-unique value, and it will be good enough for the transition.
As an example, you do a quick fetch, and discover that your objects don't yet have global ids. You go through the objects, and set the global ids to a string comprised of creationDate + text. (You could even shorten this by taking a hash, but it probably isn't that important.) After this initial 'migration' to global identifiers, you would just use UUIDs for any newly created objects.
Note that you don't have to use awakeFromInsert. That is simply a convenient place to put it. As long as you assign the global identifier before saving the object you should be fine.
The easiest way to get a string from an NSDate is to call the description method, but another way would be to get a double using timeIntervalSince1970, and turning that into a string. (Be careful with dates as unique identifiers on their own: often objects created together will have the same creation date.)
You are correct about how you should do a sync: you can simply call syncWithCompletion:.
To answer the question about excluding objects: You can't exclude individual objects, mainly because it could become tricky when those objects have relationships to synced objects. You can handle these objects in one of two ways:
Put them in a separate persistent store, and add that store to the same persistent store coordinator.
Sync the objects, but give them global ids manually, so that the objects are treated the same on each device. Eg. You could just give global ids as 'Sample1', 'Sample2', etc.
To integrate Drew's answer, I guess the two steps are the following.
1 Implement CDEPersistentStoreEnsemble delegate method (see README)
- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble
globalIdentifiersForManagedObjects:(NSArray *)objects {
return [objects valueForKeyPath:#"yourUniqueIdentifier"];
}
2 Generate the unique identifier for a NSManagedObject subclass
- (void)awakeFromInsert {
[super awakeFromInsert];
if (!self.yourUniqueIdentifier) {
self.yourUniqueIdentifier = [[NSUUID UUID] UUIDString];
}
}
In awakeFromInsert you can initialize special default property values, like for example an identifier.
The check is necessary, for example, when you have parent-child contexts. Otherwise you are overwriting the identifier previously set. See Why is awakeFromInsert called twice?.
We are automating tests using selenium, and I would like to be able to update the test to their latest status based on the result of the automated test.
I'm able to identify the test, but it appears the Status property does not have a setter.
It sounds like you're using the Object Model library.
The Object Model library does not, in fact, have a setter for that property. It may expect you to use an Operation on that asset to adjust status, such as .Close()
The SDK API library has more fine-grained access and allows more arbitrary asset editing. You may also hit the rest-1.v1 API endpoint directly with an XML body describing your attribute change. You'd need to know the ID of the TestStatus list item you want to set it to, and do a single-valued relation update
Do you have code you can share?
Once I have a VersionOne.SDK.ObjectModel.Test object I was able to do the following:
Test test = null;
test = FindTest(regressionTest); // This finds the Test object.
test.Status.CurrentValue = status;
test.Save();
I'm just starting to play around with / learn NHibernate for a personal project, and feel like I'm not "getting" something. I'm used to having apps work like this:
Presentation layer --> Business Layer --> Persistence layer. So for example, my presentation layer would call BusinessLayer.GetCustomer(id). In that method I would check that the caller has the right to fetch the customer. OK, that still works just fine with NHibernate. My question though is how do I have any security on Updates, Adding, and Deletes? For example, Suppose I want to modify the customer I got back. Normally, I would have done this:
customer.FirstName ="Mike";
BusinessLayer.UpdateCustomer(customer);
Again, the UpdateCustomer method would check that you can update this customer. OK, but with NHibernate, to update the customer I'm simply going to set the FirstName. I then don't NEED to call Update since it's all transparent. That's the point of Hibernate right: "Transparent and Automated Persistence." So how do I have my security checks in place with this transparency? I simply don't trust myself enough to not make a mistake and do something like:
List<string> customerNames = new List<string>();
foreach (Customer c in GetCustomersThatLikeStuffThatILike()) {
string custName="";
c.CustomerName = custname; //Oops. I meant to say: custname = c.CustomerName;
customerNames.Add(custName);
}
Oops. I just wiped out all the customers' names in the database. Is this contrived? Yes. Now, if I had some sort of BusinessLayer check in place, this would just throw an exception because that user can't update other users' names.
Another issue. Suppose I'm an admin and can really do anything in the system and I have code like this:
//Display a customers orders that haven't shipped yet:
var Orders = Customer.Orders;
Orders.RemoveAll(order => order.HasNotShipped);
Grid.DataSource = Orders;
Grid.DataBind();
OK, so here I just wanted to filter the orders, but I accidentally ended up deleting them from the database. The transparency caused me to do something very bad. How does one mitigate that risk?
I really want to use NHibernate but I don't want to do it the wrong way. Help! :)
For your security question, look at the NHibernate Inteceptor documentation. Interceptors let you hook into the session lifecycle and will enable the functionality you are looking for.
The second issue is why you create repositories and expose only the functionality you need. If an application doesn't need the RemoveAll function (and most don't), then don't expose it.