I'm doing a "Preview" action so that you can preview your changes on edits before doing them. Effectively what I'm doing is calling the update function (not action) without saving.
Update function code-bits:
// Updates existing schedules to new values; Working
UpdateSchedules(...);
// Removes existing schedules no longer in date-range; Not working
RemoveSchedules(...);
{
...
foreach(schedule in schedulesToRemove) { db.Entry(schedule).State = EntityState.Deleted; }
db.Schedules.RemoveRange(...);
...
}
// Adds schedules new to date-range; Working
AddSchedules(...);
{
...
db.Schedules.Add(...);
...
}
Retrieval Code:
// The results have the modified and added entities, but they also have the removed entities.
viewModel.Results = db.PaymentRecurringSchedules.Where(s => s.PaymentSetupHeaderID == headerID).OrderBy(s => s.PaymentDate).ToList();
My intention with this code is to not call db.SaveChanges() since it's just a preview, but I still want only the schedules that weren't removed (saved or unsaved).
What I've tried
I tried changing the state of the removed items as you can see above.
I tried doing a .Where(s => db.Entry(s).State != EntityState.Deleted) it didn't like that at all (i.e. execution errors).
How can I get the results where the items I removed but are unsaved will still be filtered out of the results list?
Version: 6.0 (In case it matters)
Don't have VS at hand, but i think that this should work as you requested:
List<Object> deletedEntities = context.ChangeTracker.Entries()
.Where(x => x.State == System.Data.EntityState.Deleted)
.Select(x => x.Entity)
.ToList();
Take care that you may want to filter also to specific Entity type/s.
Related
I'm supporting a production Xamarin Forms app with offline sync feature implemented using Azure Mobile Services.
We have a lot of production issues related to users losing data or general instability that goes away if the reinstall the app. After having a look through, I think the issues are around how the conflict resolution is handled in the app.
For every entity that tries to sync we handle MobileServicePushFailedException and then traverse through the errors returned and take action.
catch (MobileServicePushFailedException ex)
{
foreach (var error in ex.PushResult.Errors) // These are MobileServiceTableOpearationErrors
{
var status = error.Status; // HttpStatus code returned
// Take Action based on this status
// If its 409 or 412, we go in to conflict resolving and tries to decide whether the client or server version wins
}
}
The conflict resolving seems too custom to me and I'm checking to see whether there are general guidelines.
For example, we seem to be getting empty values for 'CreatedAt' & 'UpdatedAt' timestamps for local and server versions of the entities returned, which is weird.
var serverItem = error.Result;
var clientItem = error.Item;
// sometimes serverItem.UpdatedAt or clientItem.UpdatedAt is NULL. Since we use these 2 fields to determine who wins, we are stumped here
If anyone can point me to some guideline or sample code on how these conflicts should be generally handled using information from the MobileServiceTableOperationError, that will be highly appreciated
I came across the following code snippet from the following doc.
// Simple error/conflict handling.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(#"Error executing sync operation. Item: {0} ({1}). Operation discarded.",
error.TableName, error.Item["id"]);
}
}
Surfacing conflicts to the UI I found in this doc
private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem)
{
//Ask user to choose the resolution between versions
MessageDialog msgDialog = new MessageDialog(
String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n",
serverItem.Text, localItem.Text),
"CONFLICT DETECTED - Select a resolution:");
UICommand localBtn = new UICommand("Commit Local Text");
UICommand ServerBtn = new UICommand("Leave Server Text");
msgDialog.Commands.Add(localBtn);
msgDialog.Commands.Add(ServerBtn);
localBtn.Invoked = async (IUICommand command) =>
{
// To resolve the conflict, update the version of the item being committed. Otherwise, you will keep
// catching a MobileServicePreConditionFailedException.
localItem.Version = serverItem.Version;
// Updating recursively here just in case another change happened while the user was making a decision
UpdateToDoItem(localItem);
};
ServerBtn.Invoked = async (IUICommand command) =>
{
RefreshTodoItems();
};
await msgDialog.ShowAsync();
}
I hope this helps provide some direction. Although the Azure Mobile docs have been deprecated, the SDK hasn't changed and should still be relevant. If this doesn't help, let me know what you're using for a backend store.
Entity Framework: 6.1.3.
I have a function that reads a simple table for a record and either updates it or first creates a new entity. Either way it then calls AddOrUpdate and SaveChangesAsync. This function has worked for quite some time without any apparent problem.
In my current situation, however, I'm getting a return value of 0 from SaveChangesAsync. I have a break point just before the save and verified that the record doesn't exist. I step through the code and, as expected, a new entity was created. The curious part is that the record is now in the table as desired. If I understand the documentation, 0 should indicate that nothing was written out.
I'm not using transactions for this operation. Other database operations including writes would have already occurred on the context prior to this function being called, however, they should all have been committed.
So how can I get a return of 0 and still have something written out?
Here is a slightly reduced code fragment:
var settings = OrganizationDb.Settings;
var setting = await settings.FirstOrDefaultAsync(x => x.KeyName == key).ConfigureAwait(false);
if (setting == null)
{
setting = new Setting()
{
KeyName = key,
};
}
setting.Value = value;
settings.AddOrUpdate(setting);
if (await OrganizationDb.SaveChangesAsync().ConfigureAwait(false) == 0)
{
//// error handling - record not written out.
}
I have a scenario in which I download parent entities from an api and save them to a database. I then want, once all of the parents have been saved, to download and save their children.
I've seen (or misunderstood) some comments about how this is a side-effect as I will not be passing the result of the parent save operation to the save children operation. I simply want to begin it when the parents are saved.
Could someone explain to me the best way of doing this?
Perhaps try something like this:
Observable
.Create<int>(o =>
{
var parentIds = new int?[] { null };
return
Observable
.While(
() => parentIds.Any(),
parentIds
.ToObservable()
.Select(parentId => Save(parentId)))
.Finally(() => { /* update `parentIds` here with next level */ })
.Subscribe(o);
})
.Subscribe(x => { });
This is effectively doing a breadth-first traversal of all of the entities, saving them as it goes, but outputting a single observable that you can subscribe to.
Inspired by ruby on rails I want to add a delete callback to entity framework. I've started by overriding SaveChanges() to loop over the tracked entities and create an interface which is called whenever an entity gets deleted.
var changedEntities = ChangeTracker.Entries();
foreach (var changedEntity in changedEntities)
{
if (changedEntity.Entity is IBeforeDelete && changedEntity.State == EntityState.Deleted)
{
IBeforeDelete saveChange = (IBeforeDelete)changedEntity.Entity;
saveChange.BeforeDelete(this, changedEntity);
}
}
This works quite well, but I figured one problem and I don't know how to solve that. If an entity gets deleted within the callback, the Changetracker needs to be updated to resprect the newly deleted items. How can I solve that? Or is there another solution? Or do I do it wrong?
Good question. If I understand you correctly, your BeforeDelete implementations might delete a different entry that also needs to have BeforeDelete called on it. This could recursively go forever, so the only thing I could think of would be to recursively check the change tracker entries to see if new ones were added after the last batch was processed.
Untested:
public override int SaveChanges()
{
var processed = new List<DbEntityEntry>();
var entries = ChangeTracker.Entries();
do
{
foreach (var entry in entries)
{
processed.Add(entry);
if (entry.Entity is IBeforeDelete && entry.State == EntityState.Deleted)
{
IBeforeDelete saveChange = (IBeforeDelete)entry.Entity;
saveChange.BeforeDelete(this, entry);
}
}
} while ((entries = ChangeTracker.Entries().Except(processed)).Any());
return base.SaveChanges();
}
you can filter out uncommited entity changes...
var changedEntities = Context.ChangeTracker
.Entries<TEntity>()
.Where(e => e.State != EntityState.Detached)
.Select(e => e.Entity);
and lock on the rest in your "if" block
lock(changedEntity.Entity){ ... interface code }
I am investigating slow performance over the network of my application which uses EntityFramework version 5. One option I tried was to retrieve only the Id field, instead of the entire object. However, examining in Wireshark, I see that all objects are transferred anyway. In other words, the following two code blocks cause exactly the same network activity. Would anyone know how I can cause the db to only return Ids in the first query?
List<long> test = dbContext.UserActivities
.Where(ua => ua.Activity.Id == activityId)
.Select(ua => ua.User)
.ToList()
.Select(u => u.Id)
.ToList();
List<long> test = dbContext.UserActivities
.Where(ua => ua.Activity.Id == activityId)
.Select(ua => ua.User)
.ToList();
.ToList() materializes the object. Essentially, executing the query. Anything after is LINQ to Objects.
try something like this:
List<long> test = dbContext.UserActivities
.Where(ua => ua.Activity.Id == activityId)
.Select(ua => ua.User.Id).ToList();
List<long> test = dbContext.UserActivities
.Where(ua => ua.Activity.Id == activityId)
.Select(ua => ua.User.Id).ToList();