DbContext discard changes without disposing
How about wrapping it in a transaction?
using(var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted })){
// Do something
context.SaveChanges();
// Do something else
context.SaveChanges();
scope.Complete();
}
Undo changes in entity framework entities
There is no revert or cancel changes operation in EF. Each entity has ObjectStateEntry
in ObjectStateManager
. State entry contains original and actual values so you can use original values to overwrite current values but you must do it manually for each entity. It will not reveret changes in navigation properties / relations.
The common way to "revert changes" is disposing context and reload entities. If you want to avoid reloading you must create clones of entities and modify those clones in new object context. If user cancel changes you will still have original entities.
How to discard changes to context in EF Core
The code below worked for me. However, if someone posts a cleaner solution (I still expect there to be something out of the box with EF) I will accept it.
private void ResetContextState() => _context.ChangeTracker.Entries()
.Where(e => e.Entity != null).ToList()
.ForEach(e => e.State = EntityState.Detached);
What if I call DbContext.Dispose without calling DbContext.SaveChanges?
what happens if I simply dispose of my dbContext instance without calling dbContext.SaveChanges() or manipulating the entity states
Nothing. The instances that were previously attached to the now disposed DbContext
instance continue to exist as any normal instances would assuming there is a handle on those instances somewhere by something. If there is not the memory will be release and eventually garbage collected as any other normal instance of something would if there is no handle to it. The state of the entities in memory stays as is, nothing in memory is automatically reverted back. The database store also stays "as is" meaning there is no call from the DbContext to the datastore.
am I leaving anything in an unstable state by rejecting changes in this way
No, at least not in the data store. In memory it is hard to say, it depends on where the code left off and what the dependencies were on the modifications up until that point. Lets assume its a stateless asp.net application, maybe the request simply ends and in this case nothing unstable should happen with any of the following requests as they should retrieve anything needed back from the data store.
If its something more long lived like a windows app then you might have to manually make sure that any pointers/handles to instances that were previously being tracked are either updated with the now reverted in-memory state or release those pointers.
As far as any new DbContext instances are concerned they all operate independent of each other so there is no continuation between them. The new DbContext does not know about state being tracked or state that had been tracked by any other DbContext instance.
Entity Framework Core dbContext behavior after rejecting changes
The Change Tracker is scoped to a single DbContext, which should be scoped to a single API request. Also you don't create a new ChangeTracker. The DbContext already has one.
var ChangeTracker = _context.ChangeTracker;
Does Entity Framework's DbContext save changes if no changes were made?
It uses EntityState to determine that there is nothing to commit and so does not waste resources.
http://msdn.microsoft.com/en-us/library/system.data.entitystate%28v=vs.110%29.aspx
Using EF Core with WPF and I'd like to revert to initial state when a user press Cancel instead of OK on a dialog, is that possible and how?
Q: Is that possible and how?
A: Sure.
The most straightforward way is to only "SaveChanges()" when you're "Done" (when the user clicks OK).
Another approach might be to make copies of the original objects, and perform in-process operations on the copies.
If you're continually updating and saving work-in-progress to the DB (possibly an unwise design), then you can also leverage transactions, and rollback if the user hits Cancel.
Example:
https://learn.microsoft.com/en-us/ef/core/saving/transactions
using var context = new BloggingContext();
using var transaction = context.Database.BeginTransaction();
try
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
context.SaveChanges();
var blogs = context.Blogs
.OrderBy(b => b.Url)
.ToList();
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
transaction.Commit();
}
catch (Exception)
{
// TODO: Handle failure
}
Is there a way to reset a DbContext without disposing and reinstantiating it?
I was not able to come up with a method to reset the global DbContext. I was able to solve my problem, however, by injecting a DbContextLocator
into any class that needs a DbContext instead of passing the DbContext itself.
My goal was to maintain a global DbContext, but allow it to be reset whenever needed (such as after a database rebuild or import).
My solution uses an abstract base class and a concrete class.
Base Class
using System.Data.Entity;
namespace CommonLibrary.Database
{
public abstract class DbContextLocator
{
private DbContext _dbContext;
public DbContext Current
{
get { return _dbContext; }
}
public DbContextLocator()
{
_dbContext = GetNew();
}
public virtual void Reset()
{
_dbContext.Dispose();
_dbContext = GetNew();
}
protected abstract DbContext GetNew();
}
}
Concrete Class
using System.Data.Entity;
using CommonLibrary.Database;
using ExperimentBase.EntityModels;
namespace MainProject.Models
{
public class MainDbContextLocator : DbContextLocator
{
public new MainDbContext Current
{
get { return (MainDbContext)base.Current; }
}
protected override DbContext GetNew()
{
return new MainDbContext();
}
}
}
Usage
Get the current DbContext:
var dbContext = dbContextLocator.Current;
Reset the DbContext:
dbContextLocator.Reset();
//Note: normally followed by code that re-initializes app data
Edit
Based on Shimmy's feedback, I made DbContextLocatorBase
into a generic. (I'm also now implementing IDisposable
.)
Base Class
public class DbContextLocator<TContext> : IDisposable
where TContext : DbContext, new()
{
private TContext _dbContext;
public TContext Current
{
get { return _dbContext; }
}
public DbContextLocator()
{
_dbContext = GetNew();
}
public virtual void Reset()
{
_dbContext.Dispose();
_dbContext = GetNew();
}
protected virtual TContext GetNew()
{
return new TContext();
}
public void Dispose()
{
_dbContext.Dispose();
}
}
Concrete Class (optional, since the base class is no longer abstract)
public class MainDbContextLocator : DbContextLocator<MainDbContext> { }
Related Topics
How to Read/Stream a File Without Loading the Entire File into Memory
How to Embed Multiple Images in Email Body Using .Net
Passing Array of Strings to Webmethod with Variable Number of Arguments Using Jquery Ajax
ASP.NET MVC 4 C# Httppostedfilebase, How to Store File
How to Get the Local MAChine Name in C#
Why Doesn't Dictionary<Tkey, Tvalue> Support Null Key
Xml Error: There Are Multiple Root Elements
Creating Directories in a Ziparchive C# .Net 4.5
Ternary Operator Vb VS C#: Why Resolves Nothing to Zero
How to Confirm That Mail Has Been Delivered or Not
How to Pass an Event to a Method
How to Show the "Open With" File Dialog
Ftpwebrequest Ftp Download with Progressbar
What Are the Differences Between Various Threading Synchronization Options in C#
How to Ignore a Property When Serializing Using the Datacontractserializer