I have a linq to sql statement that inserts records in the database. If there is a duplicate, it throws Primary key violation .
after this happens, whenever i try to execute any other statement, it repeatedly shows this error. Is this a problem of transaction not getting closed. Also how to handle transactions in a more reliable way in LINQ to SQL
Each DataContext instance tracks the instances of the mapping classes that it has seen. When you say: myDataContext.InsertOnSubmit(myCustomer); you are registering a customer instance with this DataContext. Later, when you say myDataContext.SubmitChanges();, the DataContext attempts to carry out all changes that it is tracking.
If these changes fail - the DataContext does not stop tracking them and will attempt to make the change each time SubmitChanges is called.
In order to have a DataContext that is not tracking a record that it can't insert, you should abandon this instance and new-up another one.
If your primary key field is an identity field, DO NOT populate it when you insert it into the database. For example, given a Customer table with the following structure:
Customer
========
Customer_ID (PK, identity)
Last_Name (varchar)
First_Name (varchar)
Middle_Initial (char)
this is possible:
public int CreateCustomer(string lastName, string firstName, string middleInitial)
{
using (DataContext dc = new DataContext())
{
Customer customer = new Customer();
customer.Last_Name = lastName;
customer.First_Name = firstName;
customer.Middle_Initial = middleInitial;
dc.Customers.InsertOnSubmit(customer);
dc.SubmitChanges();
return customer.Customer_ID;
}
}
The most likely reason for your error is that you are trying to access the entity object after the DataContext in which it was created has been destroyed. Using the above example, this will produce an error similar to the one you are probably receiving:
public int CreateCustomer(string lastName, string firstName, string middleInitial)
{
using (DataContext dc = new DataContext())
{
Customer customer = new Customer();
customer.Last_Name = lastName;
customer.First_Name = firstName;
customer.Middle_Initial = middleInitial;
dc.Customers.InsertOnSubmit(customer);
dc.SubmitChanges();
}
return customer.Customer_ID; // <<-- Error occurs here
}
The reason the error occurs is due to the built-in change tracking of the LINQ engine. When the DataContext is created, any entity objects created within that DataContext are tied to that DataContext via a reference within the entity object. Once the DataContext falls out of scope, the reference is no longer valid and the contents of the entity object are no longer considered reliable to the LINQ engine.
As far as transaction handling is concerned, a transaction is created when the DataContext is created. When SubmitChanges is called, all changes are executed within the context of the DataContext's transaction, no matter how many entities/tables are involved.
Are you re-using the same DataContext? If so, don't do that - create a new DataContext for each logical operation and use TransactionScope to manage transactions
Why not check for a duplicate record before trying to save the record?
Throwing exceptions are very costly.
Related
I'm using LINQ to SQL to access my database but I'm only reading, I never insert, update or delete anything. Are there ways to optimize LINQ2SQL for this?
Yes there is. Linq 2 SQL will by default cache all data that you read from the DB. It needs to do this to track any changes you apply to your objects, so it can generate the necessary insert/update/delete statements when you call SubmitChanges()
If you're only reading data, this is unnessecary. You can turn off object tracking by setting the ObjectTrackingEnabled property to false on your DataContext.
One thing I've been told, is to avoid using the generated record class.
That is, if you have a Users table, L2S will create for you a User class, which is what it returns from the database. Instead of using that directly, you should create a "shadow" class --- all the same Properties, but nothing else, and immedaitely copy the data into those records for your use. In fact, if it's going to be exclusively read-only, you can assign them in the ctor, and only have public getters:
class myUser
{
public string FName {get; private set}
public string LName {get; private set}
public myUser(User user)
{
this.FName = user.FName;
this.LName = user.LName;
}
}
var users = from u in db.Users
where .....
select new myUsers(u);
This avoids a lot of overhead needed to deal with the possibility of writing the object out again.
every example I seen shows how to do a update query in linq to sql by doing this.
// grab entity you want to update
entity.UserId = "123"; // update the fields you want to update.
entity.Name = "bob";
Dbcontext.SubmitChanges();
I am wondering can you juse pass in a new object and have it figure it out?
Like could I do this?
Enity myEntity = new Entity();
myEntity.UserId = "123";
myEntity.Name = bob:
// grab entity record
// shove record ito the found record
// it figured out what to update and what no to update
Depending on what exactly you want to do you either need the InsertOnSubmit method, or the Attach method of the respective table (i.e. dbContext.Entities). InsertOnSubmit is used to add a record, while Attach can be used if you want to affect an UPDATE without having to first SELECT the record (you already know the primary key value)
In the case you have the dbContext available and ready, just add InsertOnSubmit:
Entity myEntity = new Entity();
myEntity.UserId = "123";
myEntity.Name = bob:
Dbcontext.InsertOnSubmit(myEntity);
Dbcontext.SubmitChanges();
As the name of the method implies, this will insert your new entity into the database on calling SubmitChanges.
Marc
If you want to do this for performance reasons then you shouldn't worry about it. Linq to Sql will cache objects locally so that just grabbing an entity by ID to modify some fields is very cheap.
It's possible to attach and persist it to the database, however you may want to set a field to check for concurrency (ie LastModified).
If you are going to use the Attach method on the data context, you need to set the primary/composite keys before you attach the entity (so you don't trigger INotifyPropertyChanging, INotifyPropertyChanged events).
I'm learing LINQ-to-SQL right now and i have wrote a simple application that define SQL data:
[Table( Name = "items" )]
public class Item
{
[ Column( IsPrimaryKey = true, IsDbGenerated = true ) ]
public int Id;
[ Column ]
public string Name;
}
I have launched 2 copy of application connected to the same .sdf file and tested if all database modifications in one application affects another application. But strange thing arise. If i use InsertOnSubmit() and DeleteOnSubmit() in one application, added/removed items are instantly visible in other application via 'select' LINQ queue. But if i try to modify 'Name' field in one application, it is NOT visible in other applicaton until it reconnects the database :(. The test code i use:
var Items = from c in db.Items
where Id == c.Id
select c;
foreach( var Item in Items )
{
Item.Name = "new name";
break;
}
db.SubmitChanges();
Can anyone suggest what i'm doing wrong and why InsertOnSubmit()/DeleteOnSubmit works and SubmitChanges() don't?
This is related to how Linq-to-Sql manages Object Identity. If you query for the same entity more than once, you will always receive the same object representing the row in the database.
When you insert or delete entities, you are able to see the changes from another client because Linq-to-Sql will need to create/delete the objects associated with those entities and, if there are no conflicts, this presents no problem.
Updating has a different behavior that's explained in the Object Identity article.
LINQ to SQL uses this approach to
manage the integrity of local objects
in order to support optimistic
updates. Because the only changes that
occur after the object is at first
created are those made by the
application, the intent of the
application is clear.
If you need the most updated data from the database, use the DataContext.Refresh with RefreshMode.OverwriteCurrentValues.
While trying to use LINQ to SQL I encountered several problems.
I have table persons:
int ID
string firstName
string lastName
And table notes that has:
int ID
string noteText
string createdBy
datetime creationDate
int PersonID
PersonID is a foreign key and the relationship is 1:n
I tried to use LINQ to SQL to create a person and some notes for each person.
Person person = new person();
Person.firstName = "me";
Person.note = new note();
Person.note.noteText = "some text…";
_DataContext.Persons.InsertOnSubmit(person);
_DataContext.SubmitChanges();
The problem is that the person object doesn't yet exist in the DB so it doesn't have an ID yet. So the note.personID filed has a 0 value… (the ID field is an identity field in the sql server)
The only solution for this that I found is to create a person , submitchanges and then create a note and submitchanges again.
Am I missing something here or maybe that’s the way one should work with LINQ to SQL?
How can I add multiple notes per person with LTS? I have a 1:n relationship and I don't see it with LTS.
If a person has 10000 notes, I don't want the person object constructor to load all the notes he has. I want to load them only when I refer to them. How can I config LTS to load the notes on demand?
If you aren't using the LinqToSql class designer, you should think about using it. The classes it generates will support the insertion scenario you outlined.
I can tell you aren't using the designer because it would give you a Notes (plural) property on the Person... as Person is 1 to Many with Notes.
How can I config LTS to load the notes on demand?
The designer will generate a property of type EntitySet(Note), which will load Notes on demand.
What I would like to do is the following. I have a single table, Products, containing a private key ProductID. Instead of having SQL Server auto-increment ProductID on inserts, I want to increment it in the DataContext partial method "InsertProduct":
Partial Public Class MyDataContext
Private Sub InsertProduct(ByVal instance As Product)
Dim id As Integer = Me.Products.Max(Function(p As Product) p.ProductID) + 1
instance.ProductID = id
Me.ExecuteDynamicInsert(instance)
End Sub
End Class
However, this will only work when inserting the first Product instance. When attempting to insert a second instance, the id retrieved is the same as for the first,
Using context As New MyDataContext
Dim product1 As New Product
context.Products.InsertOnSubmit(product1)
context.SubmitChanges() 'This works
Dim product2 As New Product
context.Products.InsertOnSubmit(product2)
context.SubmitChanges() 'DuplicateKeyException
End Using
Am I missing something obvious here?
I would really recommend letting SQL Server do the incremental numbering. The above approach even if you do get it working would fail under load in a multi-user scenario where they both get the same ID and try to insert the same one.
InsertProduct is declared as a partial method in the designer generated file (MyDataClasses.designer.vb).
It is executed, in fact I can insert a breakpoint in InsertProduct and observe everything running correctly for product1. For product2 an exception is thrown by context.SubmitChanges() but the breakpoint is not hit.
InsertProduct isn't declared as a partial method in the code above. Is this a typo in your post or have you declared it with a different signature than required and thus it is not being executed?
[EDIT] I'm not a VB programmer (I use C#), but I think your code needs to declare the code as partial as well as in the designer. This is true in C#, anyway.