DbContext Changes Date which is Stored in Database Upon Object Graph Creation - entity-framework-6

This question is now a curiosity, more than anything. Dates will be the end of me.
Using EF 6.
I am storing a date and in the same http request, pulling the object back out of the database.
When I look at the SQL which EF sends, the milliseconds of the date in question which are returned are the same as that which are stored in the db (expected behaviour).
BUT, when EF deserializes that into the object graph in memory, the milliseconds are different.
So, I save '2018-10-16 21:46:22.293'
SQL retrieves '2018-10-16 21:46:22.293'
EF deserializes to 2018-10-16 21:46:22.294 !
I created a workaround by hitting the db with a raw ADO.NET query that gets the exact date ('2018-10-16 21:46:22.293').
Even weirder, if I use a fresh DbContext and grab the whole object with that, the date is fine i.e. '2018-10-16 21:46:22.293'
So, it is only when I use the same DbContext that save the data, to retrieve the data that the date gets rounded (or something).
Anyone seen this weird behaviour? Is there a better fix than either raw SQL (ado.net) or a fresh DbContext?
Cheers

You can bypass the EF cache by appending .AsNoTracking() to your retrieval query.

Related

Why NHibernate not reflecting in-memory state when updates made with stored procedure?

I have a process whereby I have an NHibernate session which I use to run a query against the database. I then iterate through the collection of results, and for each iteration, using the same NHibernate session, I call a SQL Stored Procedure (using CreateSQLQuery() & ExecuteUpdate()), which ends up performing an update on a field for that entity.
When it has finished iterating over the list (and calling the SP x times), if I check the database directly in SSMS, I can see that the UPDATE for each row has been applied.
However, in my code, if I then immediately run the same initial query again, to retrieve that list of entities, it does not reflect the updates that the SP made for each row - the value is still NULL.
I haven't got any cache behavior specified against the configuration of NHibernate in my application, and have experimented with different SetCacheMode() when calling the query, but nothing seems to make any difference - the values that I can see directly in the DB have been updated, are not being brought back as updated when I re-query (using Session.QueryOver()) the database (using that same session).
By calling CreateSQLQuery (to update database, single row or multiple rows does not matter), actually you are doing DML-style operation which does not update the in-memory state.
Any call to CreateSQLQuery or CreateQuery will not use/reflect tracking. These are considered out-of-the-scope of Unit Of Work.
These operations directly affect the underlying database neglecting any in-memory state.
14.3. DML-style operations
As already discussed, automatic and transparent object/relational mapping is concerned with the management of object state. This implies that the object state is available in memory, hence manipulating (using the SQL Data Manipulation Language (DML) statements: INSERT, UPDATE, DELETE) data directly in the database will not affect in-memory state. However, NHibernate provides methods for bulk SQL-style DML statement execution which are performed through the Hibernate Query Language (HQL). A Linq implementation is available too.
They (may) work on bulk data. They are necessary in some scenarios for performance reasons. With these, tracking does not work; so yes, in-memory state become invalid. You have to use them carefully.
if I then immediately run the same initial query again, to retrieve that list of entities, it does not reflect the updates that the SP made for each row - the value is still NULL.
This is due to first (session) level cache. This is always enabled by default and cannot be disabled with ISession.
When you first load the objects, its a database hit. You get the objects from database - loop through them - execute commands those are out of Unit Of Work (as explained above) - and again execute same query twice to load same objects under same ISession instance. Second call does not hit the database at all.
It just return the instances from memory. As your in-memory instances are not updated at all, you always get original instances.
To get the updated instances, close the first session and reload the instances with new session.
For more details, please refer to: How does Hibernate Query Cache work

How to execute a LINQ to Entities query, pulling values from an existing resultset variable instead of against the DB

To provide some context, I'm using jqGrid to conduct a server side search on a datetime field, truncating the time portion server side, and then fetching results via a stored procedure. This is being done using System.Dynamic.Linq, which I've modified slightly as per this question here. I'm using EF6 Code First approach. The relevant portion of the code looks something like this:
//For matching date instead of datetime values
if (wc.Clause.Contains("Date"))
{
wc.Clause = wc.Clause.Replace("DeliveryDate", "DbFunctions.TruncateTime(DeliveryDate)");
}
results = dataFileDB.Database.SqlQuery<Document>("exec [dbo].[sp_getDocumentDetails]").AsQueryable().Where(wc.Clause, wc.FormatObjects);
The problem I'm having is that because the query is fetched using a stored procedure, I get an exception on the last line saying
System.NotSupportedException: This function can only be invoked from LINQ to Entities.
at System.Data.Entity.DbFunctions.TruncateTime(Nullable`1 dateValue)
In other places where I've used a lambda query to fetch directly from the entity itself and apply the where clause filter, the filtering is done correctly.
What I was trying to do (and I'm not sure if this is possible but...) was run an entity query against the results variable and apply the where clause to that variable but I've had no luck. Is this the right way to go or is there another approach I could take?
Thanks in advance!
Got it... I was overcomplicating things too much.
I just converted the DateTime field to a Date field when returning it via the stored procedure. Doing so no longer required me to use the DbFunctions.TruncateTime method, thereby resolving my issue.
Hope this helps someone else!

Entity Framework 4 - ApplyCurrentValues<TEntity> vs. ObjectStateManager.ChangeObjectState

We have an WCF service with an update method that updates a Customer in the DB.
This method get a detached entity from the client.
void UpdtaeCustomer(Customer detachedCustomer);
We came up with two ways to write this method :
1)
context.CustomerSet.Attach(detachedCustomer);
context.ObjectStateManager.ChangeObjectState(detachedCustomer, entityState.Modified);
context.SaveChanges();
2)
Customer customer = context.GetObjectByKey(detachedCustomer.EntityKey);
context.ApplyCurrentValues<Customer>("CustomerSet", detachedCustomer);
context.SaveChanges();
We want to consider the cons\pros of each method. The first one has a clear advantage of having only one trip to the DB. But what are the pros of the second method. (or maybe they don't act quite the same) ?
Use the first approach. There is no general advantage in using second method with detached entity on the contrary it can make things even worse.
Suppose that you use timestamp. Timestamp is special DB type representing row version. Each time the record in database changes the timestamp is automatically increased. Timestamp is used for concurrecny checks and when using with EF it is handled as Computed column. Each time the EF wants to update the record it compares timestamp in database with the timestamp you retrieved when you loaded the object (must be transpored in your entity to client and back). If timestamps are same the record is saved. If they are different an exception is thrown.
The difference between those two methods is that first method uses timestamp from detached object whereas second method uses timestamp from loaded object. The reason is the computed column. Computed values can't be updated in the application.

Entity framework data context not in sync with database?

So, here is the situation -
I insert an item in the database calling the AddtoObject() and then call SaveChanges().
Then, I call a stored procedure to update the currently inserted record.
Then, I call the Save changes() again.
The database when I query it has the correct updated value, but the entity framework
context does not have the updated values..the first time..whenever I refresh the page it gets the value..but the first time it never gets the updated values.
So has anyone faced a similar issue anytime ? What am I doing wrong here ?
The problem is that the EF does not know what your stored procedure is doing, how could it? That work is done at the SQL Server. So after your stored procedure executes, you need to ask EF to update that (and other related) instance by issuing a Refresh() call:
context.Refresh(RefreshMode.StoreWins, myObject);
The StoreWins tells the framework to overwrite values in the instance with values from the database.

Linq to Enities: Result set not getting updated after Stored Procedure Call

In LINQ to Entities, I map the result set of a stored procedure to an entity.
Within the stored procedure, I execute some update statements and return the result set by running a SELECT query and mapping that result set to the entity.
The database rows get updated correctly, but the entities returned are not reflecting the changes. Instead, the data before the update is getting returned?
Any suggestions?
Thank you.
Abe
Actually, it turns out the DataContext.Refresh method solved my problem at
http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.refresh.aspx
Here's my code:
db.Refresh(System.Data.Objects.RefreshMode.StoreWins, affectedProjectTasks);
Thanks Marc for pointing me to the right direction!
Abe
Are the entities in question already cached in the context? (i.e. have you queried them already?)
If so, the identity manager will always give you back the original object (rather than creating a new object with the same identity in the same context). Hence for data that has already been read (by other queries) only the identity/primary-key field(s) are considered.

Resources