C# / Linq-to-sql - Lock datacontext.Table on insertonsubmit - c#

I'm currently creating a method on my data access class that is going to insert an entity object to the database and I was expecting to get the latest inserted ID afterwards... and I've already done that but then I was wondering what would happend if the method somehow gets invoked twice at the same time, would it return the wrong ID?
So as a work around to that I decided to Lock the table on my datacontext:
lock(dataContext.Persons)
{
InsertOnSubmit(person);
dataContext.SubmitChanges();
}
but I do feel like this is inappropriate... I mean, the table isnt big and it wont take long to the datacontext to submit changes... So my question is, what kind of trouble would I run into by locking it like that?
PS: Let me know if my question was not clear enough and I'll edit it!

There is no need to lock your table.
LINQ-to-SQL will automatically populate your ID field with the identity from the database upon your call to .SubmitChanges() when you insert your person.
The caveat is that both your database and L2S entity must define your ID field as an identity. On your Person entity, you should have that field defined as the primary key, IsDbGenerated=true, UpdateCheck=never, and have the correct database type.
Once you submit, you should be able to simply retrieve the ID from your Person entity:
dataContext.Persons.InsertOnSubmit(person);
dataContext.SubmitChanges();
var id = person.ID; // now has the database generated identity.

Im not sure if this is part of your concerns, but if you use TransactionScope you can pass in a TransactionOption that will deal with multiple transactions.
Check out TransactionOption.Isolationlevel

Related

Entity framework. Insert values from one entity class to another different one

Here is my problem...
I've got one entity class, and I want to keep historical values from this table. Every time each record is changed, I want to insert a snapshot from this record into another table (with the same fields). I could do this field by field, but I'm sure there should be a simple way of doing this.
I've tried something like this, but it doesn't work:
var t1 = context.TABLE1.Find(id);
var t2 = new TABLE2();
context.Entry(t2).CurrentValues.SetValues(t1);
context.SaveChanges();
I've found this How to "transfer" the data from one table to another with EF? but it doesn't work for me, because my tables can't do what is said on this post
t2.CurrentValues.SetValues(t1);
Any ideas?
You could use a package like AutoMapper to create a mapping between the Table1 and Table2 classes. They have good documentation on how to get that set up. You could also override the SaveChange() method to handle the creation/saving of the historical record.

Entity Framework concurrency issue using stored procedures

I am using ASP.NET to build a application for a retail company. I am using the Entity Framework (model-first) as my data access layer. I am using stored procedures to do my CRUD operations and all columns are mapped and seems to be correct as all CRUD functionality are working as expected.
But I am having concurrency issues with the DELETE operation.
I've added a TimeStamp column to the table I am doing the CRUD operation on. The UPDATE operation works fine as it is updating by primary key and the TimeStamp value. Thus if no rows are affected with the UPDATE operation, because of a change in the TimeStamp value, the Entity Framework throws a OptimisticConcurrencyException.
The DELETE operation works on the same principle as it is deleting by primary key and the TimeStamp value. But no exception is thrown when the TimeStamp value does not match between the entity and the database.
In the C# delete method I do retrieve the latest record first and then update the TimeStamp property to another TimeStamp value (It might be different to the retrieved value). After some investigation by using SQL Profiler I can see that the DELETE stored procedure is executed but the TimeStamp parameter that is passed to the stored procedure is the latest TimeStamp value and not the value that I have set the TimeStamp property to. Thus the record is deleted and the Entity Framework does not throw an exception.
Why would the Entity Framework still pass the retrieved TimeStamp value to the Stored Procedure and not the value that I have assigned the property? Is this be design or am I missing something?
Any help will be appreciated! (where is Julie Lerman when you need her! :-))
Optimistic concurrency in EF works fine. Even with stored procedures.
ObjectContext.DeleteObjects passes original values of entity to delete function. This makes sense. Original values are used to identify the row to delete. When you delete object, you don't (usually) have meaningful edits to your entity. What do you expect EF to do with then? Write? To what records?
One legitimate use for passing modified data to delete function is when you want to track deletes in some other table and you need to throw in some information not accessible at database layer, only at business layer. Examples include application level user name or reason to delete. In this situation you need to construct entity with this values as original values. One way to do it:
var x = db.MyTable.Single(k => k.Id == id_to_delete);
x.UserName = logged_in_user;
x.ReasonForChange = some_reason;
// [...]
db.ObjectStateManager.ChangeObjectState(x, EntityState.Unchanged);
db.MyTable.DeleteObject(x);
db.SaveChanges();
Of course, better strategy might be to do it openly in business layer.
I don't understand your use case with rowversion/timestamp.
To avoid concurrency issues you pass original timestamp to modifying code.
That way it can be compared to current value in database to detect if record changed since you last read it.
Comparing it with new value makes little sense.
You usually use change markers that are automatically updated by database like rowversion/timestamp in SQL Server, rowversion in Oracle or xmin in PostgreSQL.
You don't change its value in your code.
Still, if you maintain row version manually, you need to provide:
a) new version to insert and update to be written, and
b) old version (read from database) to update and delete to check for concurrent changes.
You don't send new value to delete. You don't need to.
Also, when using stored procedures for modification, it's better to compute new version in the procedure and return it to application, not the other way around.
Hard to tell without seeing any code, but maybe when the postback occurs the page is being re-bound before your delete method is firing? On whatever method databinds the form controls (I assume it's OnLoad or OnInit), have you wrapped any databinding calls with if ( !this.IsPostBack ) { ... }?
Also I'm not sure if there's a reason why you're explicitly storing the concurrency flag in viewstate/session variables, but a better way to do it (imo) is to add the timestamp to the DataKeyNames property of the FormView/GridView (ie: <asp:FormView ID='blah' runat='server' DataKeyNames='Id, Timestamp'>.
This way you don't have to worry about manually storing or updating the timestamp. ;)

Return the uniqueidentifier with entity framework

I am using Entity Framework 3.5, while inserting a record i want to return the unique identifier with entity framework. How to do that?
After calling savechages the new id will be stored in the entity. so you can retrieve from the entity.
More generally, "To ensure that objects on the client have been updated by data source-side logic, you can call the Refresh method with the StoreWins value after you call SaveChanges.", per MSDN.
http://msdn.microsoft.com/en-us/library/bb738618.aspx
One way that I have taken to working is, when sending a record to my DataLayer/Business Logic Layer, is to have the DL/BLL do the update/insert, then retrieve the record back from the DB and return it with a boolean indicating success or failure.
That way, I can immediately refresh the record in the interface with the written one so I can be sure the record actually went in how it should have.

Using Attach with Linq To Sql and Stored Procs

I am trying to use the attach method to update an entity that was retrieve via a stored proc.
The stored proc is set up to return a specific instance, which is present in my dbml. The retrieval works as expected and returns a fully populated object. The reason I need to use a stored proc is that I need to update a property on that entity at the same time that it is retrieved.
After I have retrieved this entity, I am mapping it using AutoMapper to another model which is used in another tier of the app. This tier performs a few operations, and makes a change to the entity, and passes it back to the repository for updating.
The repository converts this business model back into a database model, and attempts to attach it to the datacontext in order to take advantage of the automagic updating.
No matter what combination of Attach(entity, true) Attach(entity) etc, and it gives me messages like "Row not found or changed" or "Unable to add an entity with the same primary key".
Does anyone have any experience with the Attach method and how it can be used to update entities that did not necessarily come from the data context using query syntax (ie in this case a stored proc)?
Thanks alot
First, if you are creating a copy of the object, making changes and then trying to attach the copied object to the same DataContext as the one with the original object in it, then this would probably result in the "Unable to add an entity with the same primary key" message. One way to handle this is:
1. Get object from DataContext
2. Make changes and map object (or vice versa - whatever order)
3. Update the original object with the new values made in the other tier
4. SubmitChanges on the DataContext containing the original object
or
Get the object from a DataContext and close the DataContext
Make your changes and do your mapping
Retrieve the object from the DataContext to which you want to save
Update that object with the values from your mapped object
SubmitChanges
Alternately, when you say you are using the proc because you need to update a property at the same time that you retrieve it, I'd need to see the proc, but if you are somehow committing this update after retrieving the information, then indeed the message "row not found or changed" is correct. This would be hard to do, but you could do it if you're loading the data into a temp table, doing the update, and then using a select from the temp table to populate the object. One thing you could try is setting that property, in the L2S designer, to AutoUpdate = Never and see if that makes the problem go away. If so, this is your problem.
1: is it the same data-context, and
2: is it the same entity instance (or one that looks like it)
This would only happen for the same data-context, I suspect. If it is the same entity, then it is already there; just call SumbitChanges. Otherwise, either use a second data-context or detach the original entity.
So if I retrieved the entity via a stored proc, is it being tracked by the datacontext?
The thing is.. I'm going from the data model, to a another model that is used by another component, and then back. Its not.. really the same instance, but it does have all the same properties.
IE
public Models.Tag GetEntity()
{
var dbTag = db.PROJ_GetEntity((int)EntityStatuses.Created, (int)EntityStatuses.CreatingInApi).SingleOrDefault();
return FromDb Entity(dbEntity);
}
var appModel = GetEntity(); // gets an Entity from a stored proc (NOT GetEntity_RESULT)
appModel.MakeSomeChanges();
_Repo.Persist(appModel);
public void Persist(Models.AppModel model)
{
var dbEntity = Mapper.Map(model);
db.Attach(dbEntity);
db.SubmitChanges();
}
This is somewhat pseudo code like.. but it demostrates pretty much exactly what I am doing.
Thanks
I'm upvoting weenet's answer because he's right - you can't use Attach to apply the changes.
Unlike Entity Framework, you can only attach an L2S object to a datacontext if it has never been attached before - i.e. it's a newed entity that you want to Insert into a table.
This does cause numerous problems in multi-layered environments - however I've been able to get around many of the issues by creating a generic entity synchronisation system, which uses reflection and expression trees.
After an object has been modified, I run the dynamic delegate against a new object from the DC and the modified object, so that only the differences are tracked in the DC before generating the Update statement. Does get a bit tricky with related entities, though.

Why is LINQ to SQL entity association creating a new (duplicate) row when inserting a new record?

I am trying to insert a new entity using LINQ-to-SQL, and entity is associated with a User entity. The insert of the new entity is successful, but my existing User entity gets inserted as if it were a new User. The code looks something like the following:
var someEntity = new Entity();
someEntity.User = this.User;
dataContextInstance.SomeEntities.InsertOnSubmit(someEntity);
dataContextInstance.SubmitChanges();
Does anyone know why the user is being inserted as a brand new entity into the Users table? It would seem that the User.UserId would become the foreign key value in the UserId column of the row mapped to the someEntity that is being inserted.
Thanks for any help/suggestions/comments
Since the User entity has been previously loaded by another DataContext (which should hopefully be disposed by now!), you have to attach it to the new (current) DataContext otherwise the DataContext will view it as a new Entity and not an existing one (which already exists in the DB).
Make sure you're using the same instance of DataContext for both tables. See my question here for (I think) a clearer explanation of the problem.
Sorry, I don't get it? Do you want to update your existing user? What is someEntity for?
It makes sense, that LINQ tries to Insert a New user, because you tell it to do so. If you just want to change one of your users you need to select him out of SomeEntities, do the update and then call SubmitChanges (LINQ will recognize, that the original entity has been modified) - without .InsertOnSubmit.

Categories

Resources