Generate non-identity primary key - linq-to-sql

My workplace doesn't use identity columns or GUIDs for primary keys. Instead, we retrieve "next IDs" from a table as needed, and increment the value for each insert.
Unfortunatly for me, LINQ-TO-SQL appears to be optimized around using identity columns. So I need to query and update the "NextId" table whenever I perform an insert. For simplicity, I do this during the creation of the new object:
var db = new DataContext( "...connection string..." );
var car = Car
{
Id = GetNextId<Car>( db ),
TopSpeed = 88.0
};
db.InsertOnSubmit( car );
db.SubmitChanges();
The GetNextId method is something like this:
public int GetNextId<T>( DataContext db )
{
using ( var transaction = new TransactionScope ( TransactionScopeOption.RequiresNew ) )
{
var nextId = (from n in db.GetTable<NextId> ()
where n.TableName == typeof(T).Name
select n).Single ();
nextId.Value += 1;
db.SubmitChanges ();
transaction.Complete ();
return nextId.Value - 1;
}
}
Since all operations between creation of the data context and the call to SubmitChanges are part of one transaction, do I need to create a separate data context for retrieving next IDs? Each time I need an ID, I need to query and update a table inside a transaction to prevent multiple apps from grabbing the same value. The call to SubmitChanges() in the GetNextId() method would submit all previous operations, which I don't want to do.
Is a separate data context the only way, or is there something better I could try?

Related

Getting identity column value using entity framework 4.1

I'm using entity framework 4.1 (VS 2010, SQL Server 2012) for inserting data into a database.
First I create an instance of an object, fill the properties with values and call AddObject(), like this:
VideoData videodata = new VideoData();
videodata.StartCaptureTime = startCaptureTime;
videodata.EndCaptureTime = endCaptureTime;
videodata.CameraID = CameraID;
using (var context = new PercEntities())
{
if (context.VideoDatas.Where(c => c.VideoID == videoID).Count() == 0)
{
var videoData = new VideoData
{
StartCaptureTime = startCaptureTime,
EndCaptureTime = endCaptureTime,
CameraID = CameraID,
};
context.VideoDatas.AddObject(videoData);
context.SaveChanges();
}
}
The thing is, that the table in the database has an identity column:
VideoID int IDENTITY(1,1)
and I need to get the value inserted by the identity function in order to fill additional objects, that have the VideoID as a foreign key. for example:
FrameData frameData = new FrameData();
frameData.VideoID = videodata.VideoID;
frameData.Path = path;
The only thing I could think of was to query for the max identity right after AddObject(videoData), but I'm afraid of race conditions.
I'm new to Entity Framework, so I'd be happy for any guidance on this.
If you have other objects which require VideoID as FK you just need to correctly configure your navigation properties between VideoData and those other types and EF will handle it for you.
Call to AddObject does not insert your data to database and because of that you cannot get the identity value after this call. Only call to SaveChanges will push all your changes to database and during this call EF will handle referential integrity internally (but only if you have your model correctly configured with relations).
After calling SaveChanges your VideoID should be populated automatically if you have everything correctly configured.

LINQ to SQL - Insert New Record

I have a database table with an auto-incrementing ID field. this table is exposed to by code via a Entity Data Model. I am trying to write a new record to this table. I have a method that needs to be responsible for creating these records. This method takes in the property values of the record. It needs to create a record, and write a reference record in another table. Currently, here is what I am trying
public int CreateRecord(string name, string description, List<int> ids)
{
using (DatabaseContext database = new DatabaseContext())
{
Record record = new Record();
record.Name = name;
record.Description = description;
database.Records.InsertOnSubmit(record);
database.SubmitChanges();
List<RecordTrack> tracks = new List<RecordTrack>();
foreach (int id in ids)
{
RecordTrack track = new RecordTrack();
track.RecordID = record.ID;
track.ID = id;
tracks.Add(track);
}
database.Tracks.InsertAllOnSubmit(tracks);
database.SubmitChanges();
}
}
I can't seem to get the record to save in this manner. I was able to do it when I passed a Record in that I wanted to insert. But due to other factors, I need a way to purely create the record here from scratch. What am I doing wrong?
Thank you!
there should be a AddToRecord() function in your database context. Use that function to add your record variable and then call SaveChanges() from your database context.

handle transaction in Linq to sql

I am implementing the asp.net MVC web application, where i am using the Linq to Sql to manipulate the data in database. but in my one of action, i want to insert multiple table entries which are depends upon each other by referring previous insertion Id's. So i just wnat to know how to handle the transaction, like begin transaction, commit,rollback and all like in ADO.net. how to manage this. what if one of insertion get crashed in the middle of manipulation?
Note:- I am not using the Stored procedures here. I am using Lambda expressions and methods. Also these are use in different manager classes.
Example:
For Create Subject - used method in SubjectManager class to insert subject infor, that returns subject Id. within this subjectid i am inserting the let say its chapters with another method in manager class as ChapterManager. which again returns the ChapterId, on base of this chapeter Id , inserting the Topics of chapter. that again uses Topic manager same like above.in each manger class i am creating dataContext object for the same. and I am controlling all this within a single action in my controller. but worrying about the transaction management. how I can use here ?
The DataContext already includes an embedded transaction object. For example, let's say you are placing a new order for a customer. You can set up your model so that the following code updates both the Customer AND Order table with a single SubmitChanges. As long as a foreign key relationship exists between the two tables, the embedded transaction object handles both the Customer update and the Order insert in the same transaction. Using a TransactionScope object to encase a single DataContext is redundant:
using (DataContext dc = new DataContext())
{
Order order = new Order();
order.ProductID = 283564;
order.Quantity = 7;
order.OrderDate = DateTime.Now;
Customer customer = dc.Customers.Single(c => c.CustomerID == 6);
customer.LastUpdate = order.OrderDate;
customer.Orders.Add(order);
dc.SubmitChanges();
}
using(TransactionScope scope = new TransactionScope())
{
using(DataContext ctx = new MyDataContext())
{
ctx.Subject.Add(subject);
Chapter chapter = new Chapter();
chapter.SubjectId = subject.Id;
ctx.SubmitChanges();
ctx.Chapter.Add(chapter);
ctx.SubmitChanges();
scope.Complete() // if it all worked out
}
}
From the System.Transactions namespace I believe.

Do MERGE using Linq to SQL

SQL Server 2008 Ent
ASP.NET MVC 2.0
Linq-to-SQL
I am building a gaming site, that tracks when a particular player (toon) had downed a particular monster (boss). Table looks something like:
int ToonId
int BossId
datetime LastKillTime
I use a 3d party service that gives me back latest information (toon,boss,time).
Now I want to update my database with that new information.
Brute force approach is to do line-by-line upsert. But It looks ugly (code-wise), and probably slow too.
I think better solution would be to insert new data (using temp table?) and then run MERGE statement.
Is it good idea? I know temp tables are "better-to-avoid". Should I create a permanent "temp" table just for this operation?
Or should I just read entire current set (100 rows at most), do merge and put it back from within application?
Any pointers/suggestions are always appreciated.
An ORM is the wrong tool for performing batch operations, and Linq-to-SQL is no exception. In this case I think you have picked the right solution: Store all entries in a temporary table quickly, then do the UPSERT using merge.
The fastest way to store the data to the temporary table is to use SqlBulkCopy to store all data to a table of your choice.
If you're using Linq-to-SQL, upserts aren't that ugly..
foreach (var line in linesFromService) {
var kill = db.Kills.FirstOrDefault(t=>t.ToonId==line.ToonId && t.BossId==line.BossId);
if (kill == null) {
kill = new Kills() { ToonId = line.ToonId, BossId = line.BossId };
db.Kills.InsertOnSubmit(kill);
}
kill.LastKillTime = line.LastKillTime;
}
db.SubmitChanges();
Not a work of art, but nicer than in SQL. Also, with only 100 rows, I wouldn't be too concerned about performance.
Looks like a straight-forward insert.
private ToonModel _db = new ToonModel();
Toon t = new Toon();
t.ToonId = 1;
t.BossId = 2;
t.LastKillTime = DateTime.Now();
_db.Toons.InsertOnSubmit(t);
_db.SubmitChanges();
To update without querying the records first, you can do the following. It will still hit the db once to check if record exists but will not pull the record:
var blob = new Blob { Id = "some id", Value = "some value" }; // Id is primary key (PK)
if (dbContext.Blobs.Contains(blob)) // if blob exists by PK then update
{
// This will update all columns that are not set in 'original' object. For
// this to work, Blob has to have UpdateCheck=Never for all properties except
// for primary keys. This will update the record without querying it first.
dbContext.Blobs.Attach(blob, original: new Blob { Id = blob.Id });
}
else // insert
{
dbContext.Blobs.InsertOnSubmit(blob);
}
dbContext.Blobs.SubmitChanges();
See here for an extension method for this.

How can i get primary key value when i insert a new record?

I am using LINQ-to-SQL class. It has a method object.InsertOnSubmit() .But it returns void so can i get primary key values after inserting a record.
I want the primary key value of recently inserted record.
yes (Assuming it is an identity field).
Calling InsertOnSubmit alone doesn't send the insert to the db. You need to call SubmitChanges in order for any changes in the current datacontext instance to be sent to the db.
After you have called SubmitChanges, you can access the values from the instance you used when calling InsertOnSubmit. Linq to sql will populate those values for you when doing the insert (which occurs during SubmitChanges)
Check this example: http://msdn.microsoft.com/en-us/vbasic/bb737920.aspx#dynid
In short, you don't need to. The object itself is updated.
public void Add(Person person)
{
using (MyEntities context = new MyEntities())
{
Context.Persons.InsertOnSaveChanges(person);
Context.SaveChanges();
}
}
public void Foo()
{
Person p = new Person { name = "John", age = 20 }
Add(p);
Int32 id = p.id; // id contains the newly inserted object's id
}
Here's an example of how that works:
var dc = new MyDataContext();
var cust = new Customer{FirstName="John", LastName="Smith"};
dc.Customer.InsertOnSubmit(cust);
dc.SubmitChanges();
//after SubmitChanges(), the Id is filled from the database
var customerPrimaryKey = cust.Id;
If the model is set up properly, the PK should be set on the affected object (the one you just inserted) automagically.
HTH.
Seth
Once you have called the InserOnSubmit(),primary key value is set to the corrosponding fiels in youe table object. You can simply access that property to get Primary Key.
Are you calling SubmitChanges() on your data context after inserting the record? The changeset won't be evaluated until you do.