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
Related
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?
I have started using Entity Framework 4.1 with Code first. I really love this over the previous ado.net techniques.
I have a very simple situation in which there is a department table and an employee table in database. Each employee belongs to a department and a department has got many employees. The business rule is : While, deleting a department, if there are any associated employees, it should not allow deletion. I can write a small method just before deletion and check the dependancy. However, since my project is a much complex one with more than 300 db tables, I hate to do this. The relationships gets changed frequently which results in lot of changes in all the delete methods of the project.
In ado.net, in similar situations, I used to parse the exception thrown and get the name of the particular Foreign key constraint which was violated. Based on the name of the violated FK constraint, I used to give appropriate error message to client. This technique worked well even in a fairly large project with more than 150 tables.
How do I do a similar thing in EF ? How do I get which FK is violated when SaveChanges() is called ?
You need to get the Message of the inner most exception(usually its a SqlException). Then you can extract the FK name from that string and check which FK is violated.
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
I've been trying to find the answer to this question here. Several people seem to ask similar things, but I don't get the answers. I have an EF entity with a bunch of child entities (one-to-many relationship). I want to be able to delete the "parent" entity and have all the child entities deleted at the same time.
Some people mention "Cascade Delete" should be set on both EF model and database (Sql Server in my case). The problem is:
I have absolutely no idea how to do this (seems to be implied in those answers that you should know, but sorry...)
I have a feeling I've run into a similar problems before and found an answer somewhere that was simpler than setting this Cascade Delete. I may be wrong, maybe it is the only way, but if there is a simpler solution I'd like to know.
In either case, a clear example of how to get this working would be greatly appreciated!
In SQL Managment Studio go to your database and find the table where there should be a foreign key. Add a foreign key to the table pointing to the other table. I assume you know how to setup a foreign key. In the foreign key setup at the bottom of the dialog window you'll see a Delete property. Set it to Cascade. This will cause any dependent rows to be deleted whenever the parent row is deleted. Then go and update your data model in Visual Studio. Everything should be setup for you now.
Here is some relevant documentation on MSDN. Note though that there appears to be an error in the example. I received the following error from the EDMX designer when using this configuration.
Operations cannot be specified on ends with multiplicity '*'.
You should set the OnDelete property to Cascade for the end will be triggering deletes on the other end.
As an example, in a relationship involving customers and orders where you would like to have a customer's orders deleted along with the customer, you should set the OnDelete property for the Customer role to Cascade.
Note that only objects that have been loaded into the ObjectContext will be affected by a cascading delete. You will be relying on the cascading delete that you set in the database to look after any other records.
Im trying to remove an entity which has a unique PK like : 80253
I remove this entity by doing the follow lines of code:
myEntityType1 = getEntityManager().find(MyEntityType1.class, 80253);
getEntityManager().remove(myEntityType1);
getEntityManager().flush();
These bits of code actually deletes the rows from my database and all its cascading objects properly, and Im very happy about this. Now the problem occurs when I now need to create a similar entity that uses the same primary key (which should now be gone right?).
MyEntityType2 myEntityType2 = new MyEntityType2 ();
myEntityType2.copyData(myEntityType1); //access the data from the other object
//and retrieves the id 80253. myEntityType2 now has 80253 as ID.
getEntitymanager().persist(myEntityType2);
Now this is where I get a unique constraint SQL error. Im trying to insert a ID which already exists and the changes are automatically rolled back (the old entity is no longer deleted). This happens after I see in my logger that toplink has deleted the records of the old entity.
Does anyone know how this happens, and why its not working? For the record Ive tried merging, closing, clearing of the entityManager, but nothing seems to work.
It seems to me that JPA might do some bad caching or something. I hope someone has a good answer for me! =)
Update: Theres no longer an issue with unique ID constraints, but I create a new subclass with the same primary key which has been deleted I get the following exception:
Exception Description: Trying to invoke [setApprovedWhen] on the object [null]. The
number of actual and formal parameters differs, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: object is not an instance of
declaring class
It seems to me that it wont let me change the object into a different subclass?
EDIT:
Try with explicitly start and commit transaction.
Deleting an entity is as simple as
calling EntityManager method
remove(Object entity) as the following
example shows. The entity you delete
must be managed: that is, it must have
been previously read in the current
persistence context.
entityManager.getTransaction().begin();
myEntityType1 = getEntityManager().find(MyEntityType1.class, 80253);
getEntityManager().remove(myEntityType1);
entityManager.getTransaction().commit();
When the transaction is completed, or
you call EntityManager method flush(),
the entity will be deleted.
In a container managed persistence
context the transaction boundaries
will be controlled by the container.