I develop a server with persistent client connections (non request based). As I keep track of each connected client state in memory it would be strange if I load entities each time when I need to access such client data.
So I have my detached entities and when I need to perform any changes I don't apply them directly but instead pass these changes and detached entity as a request to GameDb class. It performs changes on this entity and than loads the same entity from the db to perform the same changes again on session-owned entity so NH can track these changes.
I could use Merge but it's much slower because NH should load all entity data (including lazy collections which could be unmodified) to check each property for changes. In my case the performance is critical.
An example:
public void GameDb.UpdateTradeOperation(UserOperation operation, int incomeQuantity, decimal price)
{
if (operation == null) throw new ArgumentNullException("operation");
if (operation.Id == 0) throw new ArgumentException("operation is not persisted");
_operationLogic.UpdateTradeOperation(operation, incomeQuantity, price);
try
{
_factory.Execute(
s =>
{
var op = s.Get<UserOperation>(operation.Id);
_operationLogic.UpdateTradeOperation(op, incomeQuantity, price);
if (op.User.BalanceFrozen != operation.User.BalanceFrozen)
throw new Exception("Inconsistent balance");
}); // commits transaction if no exceptions thrown
}
catch (Exception e)
{
throw new UserStateCorruptedException(operation.User, null, e);
}
}
This approach brings some overcomplexity as I need to apply each change twice and check if the result states are equal. It would be easier if I could use NH Session to monitor entity changes. But it's not recommended to keep NH session opened for a long time and I could have thousands of such long lived opened sessions.
Also it forces me to split my entities and common logic. The problem is that GameDb class doesn't know from which context it's called and can't request any additional data for its operation (e.g. current prices or client socket inactivety timer or many other things) or it may need to conditionaly (by its decision) send some data to the client. Of course I can pass a bunch of delegates to GameDb method but it doesn't seem to me as a good solution.
Can I use Session.Lock to attach my unchanged detached entities so I don't need to perform the changes twice? What LockMode should I use?
Can I use a better approach here? If I keep one opened session per client but commit or rollback transactions quickly will it still open a lot of connections? Will the session keep entities state after the transaction is completed?
What kind of concurrency issues I can experience with long lived per-client-sessions:
If I operate each user entities only from its own thread fiber (or lock)?
If I request another user profile for readonly from "wrong" session (from that session's thread)?
I think what you need to do is to use a second level cache, and store Id of entities per connected client instead of storing entities in the memory.
When client connects you can fetch the entities using Id you storing, which will not even hit the database in subsequent requests as it will fetch entities from the second level cache and you do not need to worry about change tracking.
http://ayende.com/blog/3976/nhibernate-2nd-level-cache
I tried to use Session.Lock (LockMode.None) to reattach detached entities to the new session and it works. It adds an object to the session as clean and unchanged. I can modify it and it will be stored to the database with the next transaction commit.
This is better than merge because Nhibernate does not need to look at all the properties to find out what is changed.
if I change at least one property it updates the whole object (I mean all the properties but without collections and entity links if they are not touched). I set DynamicUpdate = true in the entities mapping and now it updates only changed properties.
If I change any property of the dettached entity outside of its current session, the next call to Session.Lock throws an exception (especially if I change the collection content the exception states "reassociated object has dirty collection"). I do those changes outside of the session because I don't need to save them (some stuff with references).
Very strange, but it works perfectly when I call Lock twice!
try
{
s.Lock(DbEntity, LockMode.None); // throws
}
catch
{
s.Lock(DbEntity, LockMode.None); // everything ok
}
Also for collections: before I came to the solution above I casted them to IPersistentCollection and used ClearDirty().
What about concurrency? My code unsures that each thread fiber updates only its user and nobody except this fiber has write access to the entity.
So the pattern is:
I open a session, get an entity and store it somewhere in the memory.
When I need to read its property - I can do it at any time and very fast.
When I want to modify it I open a new session and perform Lock() on it. After applying changes I commit the transaction and close the session.
Related
EF Core 6 and .NET 6.
Suppose all my entities have a LastUpdateAt property, which is a DateTime that gets updated every time an entity is added or modified.
I get an entity from the context and show it to the user (web page, WPF window, whatever). At some point, the user clicks a Save button.
Before I save, I want to check if the entity has been updated by someone else since I got my copy. However, I'm struggling to see how to do this.
If I query the context, it just gives me back the entity I already have (including any changes my user has made).
If I refresh the entity, it overwrites the one in my context, losing my user's changes.
How do I check if the database version has a newer time stamp than the one in my context?
Thanks
Moving the discussion here since I need to paste longer text. In this article it's said, during SaveChanges(), if the DATABASE version was modified in the mean time it will throw DbUpdateConcurrencyException. In that exception you have all 3 values and YOU can decide on how to resolve the conflict:
Resolving a concurrency conflict involves merging the pending changes from the current DbContext with the values in the database. What values get merged will vary based on the application and may be directed by user input.
There are three sets of values available to help resolve a concurrency conflict:
Current values are the values that the application was attempting to write to the database.
Original values are the values that were originally retrieved from the database, before any edits were made.
Database values are the values currently stored in the database.
If you are loading an entity, keeping a DbContext instance open, updating that entity, then saving to the same DbContext instance then by default you are relying on EF to manage concurrency. This follows a "last in wins". You can let EF manage the concurrency by adding a [ConcurrencyCheck] on the LastUpdateAt property or using a Row Version via [Timestamp]. This will cause EF to fail updating if the underlying data has been updated. From there you have to decide how you want to handle it.
If you want to perform the concurrency check yourself then there are a couple of options.
Structure your code to shorten the lifespan of the DbContext using either detached entities or projected View Models. This will generally have flow-on benefits to your code performance as the original longer-lived DbContext can easily find ways to cause bloat, or accumulate "poisoned" entities if alive too long. Automapper is a great tool to assist here where you can use ProjectTo to get the view models, then Map(source, destination) to copy the values across afterward. In this way you load the data including the last modified at value, make your changes, then when saving, you load the data, validate the modified at etc. then copy the values across and save.
Scope a DbContext instance to check the data before saving.
.
private DateTime getFooLastUpdateAt(int fooId)
{
using(var context = new AppDbContext())
{
var lastUpdateAt = context.Foos
.Where(x => x.FooId == fooId)
.Select(x => x.LastUpdateAt)
.Single();
return lastUpdateAt;
}
}
This could use an injected DbContext factory or such to create the DbContext instance..
I have seen other questions about this same error, but I am unable to correct the error with those suggestions in my code; I think that this is a different problem and not a duplicate.
I have an app that makes a series of rules, of which the user can set properties in the GUI. There is a table of Rules in a connected database, with the primary key on the Rule.Id. When the user saves changes to a rule, the existing rule gets "IsActive=0" to hide it, then a new database record is made with the properties from the GUI written to the database. It looks to the user as though they have edited the rule, but the database actually sees a new rule reflecting the new properties (this allows for a history to be kept), connected to the old rule by another reference field.
In the C# code for the app, the View Model for each rule contains an EF Rule object property. When the user clicks "save" I use the parameters set in the view to build the ruleViewModel.Rule for each ruleViewModel they want to save, with porperties matching the GUI. The MainViewModel contains the DbContext object called dbo, so I use the ruleViewModel.Rule to write to the mainViewModel.dbo.Entry which I save to the Entity Framework. Here are the three basic steps performed for each saveable Rule View Model:
// get the rule from the GUI and use it to make sure we are updating the right rule in EF (which is connected to the mainViewModel)
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
// set the values in the EF item to be those we got from the GUI
ruleViewModel.MainViewModel.dbo.Entry(dboItem).CurrentValues.SetValues(ruleViewModel.Rule);
// Save the differences
ruleViewModel.MainViewModel.dbo.SaveChanges();
If the user only saves a single rule, it all works fine, but if they subsequently try to save another, or if they save more than one at once, they get the following error, which is return by the ..SetValues(..) line:
Message = "The property 'Id' is part of the object's key information and cannot be modified. "
I see from other questions on this subject that there is a feature of EF that stops you from writing the same object twice to the database with a different Id, so this error often happens within a loop. I have tried using some of the suggestions, like adding
viewModel.MainViewModel.dbo.Rules.Add(dboItem);
and
viewModel.MainViewModel.dbo.Entry(dboItem).Property(x => x.Id).IsModified = false;
before the SaveChanges() command, but that has not helped with the problem (not to mention changing the function of the code). I see that some other suggestions say that the Entry should be created within the loop, but in this case, the entries are all existing rules in the database - it seems to me (perhaps erroneously) that I cannot create them inside the save loop, since they are the objects over which the loop is built - for each entity I find, I want to save changes.
I'm really confused about what to do and tying myself increasingly in knots trying to fix the error. It's been several days now and my sanity and self-esteem is beginning to wane! Any pointers to get me working in the right direction to stop the error appearing and allow me to set the database values would be really welcome as I feel like I have hit a complete dead end! The first time around the loop, everything works perfectly.
Aside from the questionable location of the DbContext and view models containing entities, this looks like it would work as expected. I'm assuming from the MVVM tag that this is a Windows application rather than a web app. The only issue is that this assumes that the Rule entity in your ruleViewModel is detached from the DbContext. If the DbContext is still tracking that entity reference then getting the entity from the DbContext again would pass you back the same reference.
It would probably be worth testing this once in a debug session. If you add the following:
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
bool isReferenceSame = Object.ReferenceEquals(dboItem, ruleViewModel.Rule);
Do you get an isReferenceSame value of True or False? If True, the DbContext in your main view model is still tracking the Rule entity and the whole get dboItem and SetValues isn't necessary. If False, then the ruleViewModel is detached.
If the entities are attached and being tracked then edits to the view model entities would be persisted when you call a SaveChanges on the DbContext. (No load & SetValues needed) This should apply to single or multiple entity edits.
If the entities are detached then normally the approach for updating an entity across DbContext instances would look more like:
var context = mainViewModel.dbo;
foreach( var ruleViewModel in updatedRuleViewModels)
{
// This should associate the Entity in the ruleViewModel with the DbContext and set it's tracking state to Modified.
context.Entry(ruleViewModel.Rule).State = EntityState.Modified;
}
context.SaveChanges();
There are a couple of potential issues with this approach that you should consider avoiding if possible. A DbContext should be kept relatively short lived, so seeing a reference to a DbContext within a ViewModel is a bit of a red flag. Overall I don't recommend putting entity references inside view models or passing them around outside of the scope of the DbContext they were created in. EF certainly supports it, but it requires a bit more care and attention to assess whether entities are tracked or not, and in situations like web applications, opens the domain to invalid tampering. (Trusting the entity coming in where any change is attached or copied across overwriting the data state)
Some previous code I had written used the Find() method to retrieve single entities by their primary key:
return myContext.Products.Find(id)
This worked great because I had this code tucked into a generic class, and each entity had a different field name as its primary key.
But I had to replace the code because I noticed that it was returning cached data, and I need it to return data from the database each call. Microsoft's documentation confirmed this is the behavior of Find().
So I changed my code to use SingleOrDefault or FirstOrDefault. I haven't found anything in documentation that states these methods return cached data.
Now I am executing these steps:
Save an entity via EF.
Execute an UPDATE statement in SSMS to update the recently saved
record's Description field.
Retrieve the entity into a new entity variable using SingleOrDefault
or FirstOrDefault.
The entities being returned still have the old value in the Description field.
I have run a SQL trace, and verified that the data is being queried during step 3. This baffles me - if EF is making a round trip to the database, why is it returning cached data?
I've searched online, and most answers apply to the Find() method. Furthermore, they suggest some solutions that are merely workarounds (dispose the DbContext and instantiate a new one) or solutions that won't work for me (use the AsNoTracking() method).
How can I retrieve my entities from the database and bypass the EF cache?
The behaviour you're seeing is described in Microsoft's How Queries Work article under point 3:
For each item in the result set
a. If this is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance
If so, the existing entity is returned
It's described a little better in this blog post:
It turns out that Entity Framework uses the Identity Map pattern. This means that once an entity with a given key is loaded in the context’s cache, it is never loaded again for as long as that context exists. So when we hit the database a second time to get the customers, it retrieved the updated 851 record from the database, but because customer 851 was already loaded in the context, it ignored the newer record from the database (more details).
All of this is saying that if you make a query, it checks the primary key first to see if it already has it in the cache. If so, it uses what's in the cache.
How do you avoid it? The first is to make sure you're not keeping your DbContext object alive too long. DbContext objects are only designed to be used for one unit of work. Bad things happen if you keep it around too long, like excessive memory consumption.
Do you need to retrieve data to display to the user? Create a DbContext to get the data and discard that DbContext.
Do you need to update a record? Create a new DbContext, update the record and discard that DbContext.
This is why, when you use EF Core with dependency injection in ASP.NET Core, it is created with a scoped lifetime, so any DbContext object only lives for the life of one HTTP request.
In the rare case you really do need to get fresh data for a record you already have an object for, you can use EntityEntry.Reload()/EntityEntry.ReloadAsync like this:
myContext.Entry(myProduct).Reload();
That doesn't help you if you only know the ID though.
If you really really need to reload an entity that you only have the ID for, you could do something weird like this:
private Product GetProductById(int id) {
//check if it's in the cache already
var cachedEntity = myContext.ChangeTracker.Entries<Product>()
.FirstOrDefault(p => p.Entity.Id == id);
if (cachedEntity == null) {
//not in cache - get it from the database
return myContext.Products.Find(id);
} else {
//we already have it - reload it
cachedEntity.Reload();
return cachedEntity.Entity;
}
}
But again, this should only be used in limited cases, when you've already addressed any cases of long-living DbContext object because unwanted caching isn't the only consequence.
Ok, I have the same problem and finally found the answer,
You doing everything right, that's just how EF works.
You can use .AsNoTracking() for your purposes:
return myContext.Products.AsNoTracking().Find(id)
make sure you addedusing Microsoft.EntityFrameworkCore; at the top.
It works like a magic
I am currently having problems getting my Update function working, the function first loads an entity using session.Load() and then uses session.SaveorUpdate().
My problem is that if I do not load the session first nhibernate will not know the the relationships and therefore try and insert data which is already there and when I do load the entity first, the updated entity is overwritten by the data already in the database.
public void Update(T Entity, bool load)
{
using(ISession session = this.helper.GetSession())
{
using(ITransaction transaction = session.BeginTransaction())
{
if(load)
{
session.Load(Entity, Entity.ID);
}
session.SaveOrUpdate(Entity);
transaction.Commit();
session.Flush();
}
}
}
In a nutshell:
load object and then bind it with new values (changes will be persisted on session.Flush() without any explicit Update() call) or
create new C# instance with bounded values, including ID, and call session.Update(myInstance)
The more complex answer could be found in one of the doc chapters:
9.4.2. Updating detached objects
Many applications need to retrieve an object in one transaction, send it to the UI layer for manipulation, then save the changes in a new transaction. (Applications that use this kind of approach in a high-concurrency environment usually use versioned data to ensure transaction isolation.) This approach requires a slightly different programming model to the one described in the last section. NHibernate supports this model by providing the method ISession.Update().
// in the first session
Cat cat = firstSession.Load<Cat>(catId);
Cat potentialMate = new Cat();
firstSession.Save(potentialMate);
// in a higher tier of the application
cat.Mate = potentialMate;
// later, in a new session
secondSession.Update(cat); // update cat
secondSession.Update(mate); // update mate
The usage and semantics of SaveOrUpdate() seems to be confusing for new users. Firstly, so long as you are not trying to use instances from one session in another new session, you should not need to use Update() or SaveOrUpdate(). Some whole applications will never use either of these methods.
Usually Update() or SaveOrUpdate() are used in the following scenario:
the application loads an object in the first session
the object is passed up to the UI tier
some modifications are made to the object
the object is passed back down to the business logic tier
the application persists these modifications by calling Update() in a second session
So, we can get an instance of some entity in one session... and close that session. Such object could be even totally brand new C# instance - with all its properties being bounded by some upper layer (e.g. MVC binder, or Web API formatter)
Later, we can use that instance and call session.Update(myInstance). NHibernate will take the ID of that entity and issue the proper update statement.
Another way could be to call Merge:
The last case can be avoided by using Merge(Object o). This method copies the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. The method returns the persistent instance. If the given instance is unsaved or does not exist in the database, NHibernate will save it and return it as a newly persistent instance. Otherwise, the given instance does not become associated with the session. In most applications with detached objects, you need both methods, SaveOrUpdate() and Merge().
read more in the doc
I have a method that gets all the records from a particular database, then stores it in the cache. The next time that method is called, it first checks the cache to see if it can simply return a cache version, if that cache object hasn't been expired.
Question: how do I trigger a method everytime dataContext.SubmitChanges() is called? For example, if I get all the books from the Book table and store it in Cache["AllBooks"], I want this cache object to be cleared on any crud operations related to the Book table.
What I'm currently doing:
var b = dataContext.Books.Where(x => x.BookId == 4).SingleOrDefault();
b.Title = "new title for book w/ id of 4";
dataContext.SubmitChanges();
ClearBookCache();
later...
private void ClearBookCache() {
CustomCachingSystem.Clear["AllBooks"];
}
What I want: the ClearBookCache() to be automatically triggered on any Book table crud operations, vs. me having to remember to call it everytime I do a crud operation on the Book table.
Note: I wouldn't want that ClearBookCache() method to be called if I do a crud operation on a table that's unrelated to the Book table.
I hope this makes sense!
You could use the SqlDependency Class. Basically, it will let you detect changes to the queried data (if you use M$ SQL Server 2005+).
You may want to look into Service Broker technology (if you use M$ SQL Server 2005+).
Detecting it directly in your application probably won't be enough - you won't be able to detect any changes that were performed outside of your application.
Take a look at DataContext.GetChangeSet() - you could inherit from DataContext and override the SubmitChanges methods to clear the relevant caches based on the ChangeSet contents and then call MyBase.SubmitChanges(...).