When using OrmLite to add an entry into the database there seems to be two ways of doing it:
dbConn.Insert(customer);
and
dbConn.Save(customer);
When using Insert() the AutoIncrement ID field does not get updated, but when using Save() it does.
If you use:
dbConn.LastInsertId();
It will return the correct ID if Save() is used but the wrong ID if Insert() is used.
Why do these two methods exist? Using Insert() will add an entry to the database with the correct ID, it is just not reflected in the POCO model. It seems strange that there are no exceptions thrown and no indication of a problem, when you can end up using the wrong ID and get hard to track bugs if you are not aware of this.
It's already mentioned in the documentations:
Save and SaveAll will Insert if no record with Id exists, otherwise it Updates. Both take multiple items, optimized to perform a single read to check for existing records and are executed within a sinlge transaction.
For Insert operation, it's pretty straighforward, there's nothing special in that functions.
So, Use Insert when you know there's no duplicated key in your records, otherwise use save method.
If you want to retrieve the LastInsertId, with Insert method, you need to specify the [AutoIncrement] on your POCO.
UPDATE: When you set your IDbConnectionFactory using Singelton With Lazy< T > implementation, it seems the LastInsertId will not working. so just use lock or static implementation.
Related
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. ;)
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
Currently, I'm using the following code to perform an 'upsert' in CRM
try
{
crm.Create(c);
}
catch (SoapException)
{
crm.Update(c);
}
Am I right in assuming that this type of updating will wipe all existing information from my business entity? And if so, how can I get the entity to update from the repository? Do I need fetch, retrieve, or something like that?
Thanks.
Using exception handling for flow control is bad 99.99% of the time; in this case among other things because you never know what the actual reason for your SoapException is.
A much cleaner way would be to check whether your record's ID field has a value; if so, do an Update, if not, do a Create (and maybe add the resulting ID to your object if you use it further). (We've seen a Create on a record with an ID actually do an update years ago, but we've never been able to reproduce it.)
Other than that, #ckeller is perfectly right; an attribute that is null in your object (because it wasn't in the ColumnSet when retrieving from the database or hasn't been set otherwise) will not be touched in an Update.
Hi all I am doing something like this -
void update(ClasstoUpdate obj)//obj is already having values to update...
{
var data= (from i in Entityobject.ClasstoUpdate
where obj.Id==i.Id
select i).FirstorDefault();
data.Name="SomeCoolName";
EntityObject.SaveChanges();
}
I want to perform an update without again querying using the Id,is there any way I just pass the updated object to ADO.NET Entity framework and it updates it.I am sorry if I am missing something here but this is the way i have been doing it wondering if there is a simple way to update. Thanks.
The simple way to update an object is fetch it, change it, and submit changes which is what you're already doing.
Another way is to attach the object, and tell the entity framework that the object is in a modified state.
A third way is to update the object by constructing an SQL string that updates the object directly in the database without fetching it. However I wouldn't recommend doing this.
A side note: remember to check for null in your function. If you know the return value of FirstOrDefault will never be null then you should use First instead. You might also want to consider using Single instead of First.
Here's another one of these LinqToSQL questions where I'm sure I must have missed the boat somewhere, because the behavior of the O/R Designer is very puzzling to me...
I have a base class for my LinqToSQL tables, which I called LinqedTable. I've successfully used reflection to get hold of all the properties of the descendant classes and do other standard stuff.
Now I want to have some automatic auditing of my tables, so that whenever a LinqedTable record is inserted or deleted, or a field value changes, I will insert a record into an audit table, detailing the change type, the field name, and its value pre- and post-save.
I thought I would be able to do it using the PropertyChanging event, keeping track of all the changed properties before a save, then clearing the collection of changes after each SubmitChanges() call. But - the generated code from the O/R designer, for some bizarre reason, doesn't give you the property name in the PropertyChanging event - it sends an empty string! (WHY?!) It does send the property name in the PropertyChanged event, but that's already too late for me to get the original value.
I thought to grab all the original values of all properties using the OnLoaded() partial method - but that is private by definition, and I need access to that method in the base class. Even if I used reflection to get hold of that method, that would mean I would have to implement the other half of the partial method for every one of my tables, which kinda defeats the purpose of having inheritance!
I also can't find any suitable method in the DataContext to use or override.
So what would you recommend to get this audit functionality working?
You can use GetChangeSet on the DataContext to retrieve a list of updates, inserts and deletes that have occurred on all tables within a context. You can use ITable.GetOriginalEntityState to retrieve the original values of a changed entity. However, when you retrieve the original values of a deleted or updated record, the associations will not be available so you will have to rely on foreign key values only in that area if you need to process related entities. You can Use ITable.GetModifiedMembers to help retrieve only values that have changed.
Forgive me for perhaps a stupid answer, but how about doing the audit directly in the SQL Server using triggers (if you are in SQL Server 2005 or 2008 standard) or using the change tracking facilities in SQL server 2008 Enterprise?