Change tracking with EF over time? - entity-framework

I'd like to know what is the best practice to track and/or persist changes over time if I use EF. I'd like to get started with EF for a new project. What I need is a kind of change history.
That's how I did it before: If a record was created it was saved with an ID and with the same ID as InvariantID. If the record was updated i marked it as deleted and created a new record with the new values and a new ID but the same InvariantID. Like this I always had my current record but a history of changes as well.
This works perfectly fine for my scenarios. The amount of historical records is not an issue because I use this usually only for data that's not changing very often.
Is this build in EF somehow or what's the best way to get this behavior for EF?

No it is not build into EF and it will not work this way. I even don't think that it is a good approach on the database level because it makes referential integrity very complex.
With EF this will work only if you use following approach:
You will use conditional mapping for your entity - condition will be IsDeleted = 0. It will ensure that only non deleted entities will be used in queries.
You will have mapped stored procedure for delete operation to correctly set IsDeleted = 1 instead of really deleting the record
You will have to manually call DeleteObject to delete your record and after that you will insert new record - the reason is that EF is not able to deal with scenario where entity change its PK value during update.
Your entities will not be able to participate in relations unless you manually rebuild referential integrity with some other stored procedure
You will need stored procedure to query historical (deleted) records

Related

entityManager.merge(object) is persisting only the last record from list

I have two records in my ArrayList<Object> but when I'm trying to insert these records in database, only the last record gets saved into DB.
I'm using Spring data JPA entityManager.merge() method since I have custom sequenceGenerator to be used for Entities.
summaryList.stream().forEach{(summary -> entityManager.merge(summary)};
On debugging I get two records in summaryList but when I check my DB table, only one record gets inserted.
Hibernate generates a SELECT statement first to fetch the latest state of the underlying database record, and then, it copies the detached entity state onto the newly fetched managed entity. This way, the dirty checking mechanism can detect any state change and propagate it to the database.
While for IDENTITY and SEQUENCE generator strategies, you can practically use merge to persist an entity, for the assigned generator, this would be less efficient.
You can actually fix this issue by adding a version property to your entity.
#Version
private Long version;

entity framework concurrency: transactions or concurrency fixed?

I need to make stock control, so I need to ensure that when I modified the amount of product, is doing in the right way. I am using Entity framework 4.0
For example, if I use a transaction, when I load the record from the database, the recored is blocked, so I can substract or add to the loaded amount, the number of items that I need. However, this block the record in the database and perhaps for performance reasons is not the best way. This makes me ask when to use transactions with EF.
The other option is to use the concurrency fixed of entity framework, using a timespan column to detect if the record has been changed. In this case, if the record has been modified between my load and my update, I get the exception of concurrency. But it could occur that in my exception handler, if I update my context with the database data, between my refresh and the savechanges could be changed again.
Other problem is I finally can save the changes. For example, I have 10 units, I need to substract 8 but between my load and my update, other person substract 5 units. If I subtract 8, then in stock I have -3 units. This is not possible. If I have a transaction, I load the record, is blocked, so I can check if I have enough units, if yes, I can subtrack, if not, I send an exception.
So my question is, I know that EF is a transaction by itself, but it exists also transactions in EF, so it would be useful in some cases. When to use EF and cocurrency fixed and when to use transactions?
Thanks.
Daimroc.
Transactions should not be used to solve concurrency. You should make transactions as short as possible to not block your databases. The way to go here is optimistic concurrency - in the database (SqlServer) you create a rowversion column that changes automatically each time a row is modified. You use it as concurrency token. When saving changes EF checks this against the value on the entity and if they don't match it throws an exception.
Note that for SaveChanges EF always creates a transaction since typically you save more than one entity and if something goes wrong the database needs to be reverted to the original state - otherwise it would be in a corrupt state.
To prevent from going below zero - if you use the optimistic concurrency - you won't be able to save if the value in the database changed since the concurrency token will be different because row was modified and therefore the check on the client should be sufficient. Alternatively you could map saving an entity to a stored procedure that will check the value before saving and would return an error if the value after saving would be incorrect.

optimistic concurrency exception was handled by the user code

I'm using MVC4 with Entity Framework 4.1.
Initially we have created an Ado.net entity model from database. In the .edmx file, some of the tables that are in the database are not visible as they dont posses the primary key on particular table.
As our project is moving forward, we need to update to one of the log tables which dont have a primary key field.
So, we modified our .edmx file instead of modifying in the database. our client asked us not to modify the database fields. we have modified the .edmx and created a pk on one of the exisiting field in the table(say tbl_log table).
we are trying to update the tbl_log. But it gives an error message as Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
I've seen much of questions in stack overflow and also googled a bit, but could not find any solution.
Even i've tried refreshing the ObjectStateManager entries but it still points to the same error.
Here is my code
tbl_log log = new tbl_log();
Entity.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified);
log.LoginId = strLoginId;
log.Password = strPassword;
log.IPAddress = strIpAddress;
log.Date_Time = DateTime.Parse(DateTime.Now.ToString());
log.sessionId = new Guid(strSessionId);
Entity.AddTotbl_log(log);
Entity.SaveChanges();// optimistic concurrency error
Please help
Thanks,
Karthik
Your model must represent the database schema. If you add a PK to the model it should exist in the database as well, otherwise you will get errors. It's generally bad practice to not have a PK on any table, even if the table is an audit log table.
What the exception is telling you is that the object tracker cannot determine if the state of the object has changed since the last call to the database. This is because the PK you have set is still 0 even after the framework has sent the insert/update query.
Unfortunately there is no good way to work around this. I would suggest (as I think Microsoft does) to add a primary key column to every table in your database.
EDIT - From comments
As the PK is used to track the object, if you have set the PK to have a StoreGenerationPattern of Identity in your model it will be expecting the value to change. When it doesn't change then it will throw the error your are seeing. Try changing the StoreGenerationPattern to None as then EF won't be expecting your faux-PK to change

How do I tell EF to ignore columns from my database?

I'm building my EF (v4.0.30319) data model from my SQL Server database. Each table has Created and Updated fields populated via database triggers.
This database is the backend of a ASP.NET Web API application and I recently discovered a problem. The problem is, since the Created and Updated fields are not populated in the model passed into the api endpoint, they are written to the database as NULL. This overwrites any values already in the database for those properties.
I discovered I can edit the EF data model and just delete those two columns from the entity. It works and the datetimes are not overwritten with NULL. But this leads to another, less serious but more annoying, problem... my data model has a bunch of tables that contain these properties and all the tables need to be updated by removing these two columns.
Is there a way to tell EF to ignore certain columns in entities across the entire data model without manually deleting them?
As far as I know, no. Generating the model from the database is going to always create all of the fields from the database table. Well, as long as it has a primary key it will.
It is possible to only update the fields that you want i.e. don't include the "Created" and "Updated" fields in your create and update methods. I'd have to say though, that I'd think it'd be better if those fields didn't even exist on the model at that point. You may at one point see those fields on the model and not remember that they won't get persisted to the DB.
You may want to look into just inserting the datetimes into those fields when you call your create() and update() methods. Then you could just ditch the triggers. You'd obviously want to use a class library for all of your database operations so this functionality would be in one place. To keep it nice and neat, you know?

Entity Framework CTP 5 - Repository pattern - doing updates

How would you do an update operation with CTP 5 using DbContext and using Repository pattern? Earlier with EF 4.0, it could be done like below.
_context.Customers.AddObject(item);
_context.ObjectStateManager.ChangeObjectState(item, System.Data.EntityState.Modified);
Is there any reason as to why EF does not provide an easy way to update "disconnected" entities. I don't want to query the db and copy all the properties to the object that is returned from query. In other words, EF should have update method which takes in the entity (similar to Add method). If the entitykey already exists in the database, update the entity with the current values. i.e. why should we do "Attach" and then copy all the properties to the attached object. To me it seems redundant to copy all the properties of entities just to update when the "disconnected" object already exist.
I believe you can still perform the same method as in your code example to update a disconnected entity with the CTP5 DbContext:
_dbContext.Customers.Add(item);
DbEntityEntry entry = _dbContext.Entry(item);
entry.State = EntityState.Modified;
_dbContext.SaveChanges();
Looking at the generated SQL this creates of course a full update statement on all properties of the customer object including the properties that didn't actually change, since EF doesn't know what's the current state in the database. If you want to avoid that, I guess there is no other way than fetching the current state in the database before the update which could be done this way:
DbEntityEntry entry = _dbContext.Entry(_dbContext.Customers.Find(item.ID));
entry.CurrentValues.SetValues(entity);
_dbContext.SaveChanges();
(Assuming here you have a key ID on your customer object "item".)
This creates a SQL update statement which only includes the properties which indeed have changed compared to the state in the database. I'm not sure if the second way is necessarily the less performant option due to the additional select statement. If the object type is large but only very few properties have changed the overhead of sending a full update statement on all fields might be bigger than a select statement plus a "small" update statement with only the fields which are really required for the update. (But that's only speculation, I'm not a SQL Server specialist.)