views:

92

answers:

4

I am trying to complete a seemingly simple task that has turned into a several hour adventure: Getting @@Identity from TableAdapter.Insert()

Here's my code:

protected void submitBtn_Click(object sender, EventArgs e)
{
    AssetsDataSetTableAdapters.SitesTableAdapter sta = new AssetsDataSetTableAdapters.SitesTableAdapter();
    int insertedID = sta.Insert(siteTxt.Text,descTxt.Text);

    AssetsDataSetTableAdapters.NotesTableAdapter nta = new AssetsDataSetTableAdapters.NotesTableAdapter();
    nta.Insert(notesTxt.Text, insertedID, null,null,null,null,null,null);
    Response.Redirect("~/Default.aspx");
}

So you might say "Oh yea, all you have to do is ExecuteNonQuery by changing the ExecuteMode property for the TableAdapter" like this StackOverflow Answer. XXX Incorrect. I already tried this. Not only does it make GetData() quit working (cause I'm returning a scalar now instead of rowdata) (I need to KEEP GetData()), but it also doesn't fix the problem. The insertedID variable is still set to the value 1.

I tried creating a second TableAdapter in the TypedDataSet.XSD and setting the property for that adapter to "scalar", but it still fails with the variable getting a value of 1.

Worse yet, the generated insert command is

INSERT INTO [dbo].[Sites] ([Name], [Description]) VALUES (@Name, @Description);
SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY())

And the "Refresh the Data Table" (Adds a select statement after Insert and Update statements to retrieve Idenity" is also set.

Environment SQL 2008 R2, Visual Studio 2010, .NET 4, Windows XP, all local same machine

So what's wrong?

EDIT/UPDATE I want to clarify that I am using auto-generated code within Visual Studio. I don't know what the "tool" that generated the code is, but if you double click the *.XSD file it displays a UI of the SQL Table Schema's and associated TableAdapter's. I want to keep using the auto-generated code and somehow enable getting the Identity. I don't want to write this all by hand with Stored Procedures.

A: 

You'll need to setup the insert to return the identity as an output value and then grab it as a parameter in your adapter.

These two links should get you going:

http://www.akadia.com/services/dotnet_autoincrement.html

http://msdn.microsoft.com/en-us/library/ks9f57t0.aspx

klabranche
A: 

One way is to run a select query after the insert command. A good way is to wrap the original command like this:

public int WrapInsert(Parameters)
        {
            .....
            int RowsAffected = this.Insert(..Parameters..);
            if ( RowsAffected > 0)
            {
                try
                {
                    SqlCommand cm = this.Connection.CreateCommand();
                    cm.CommandText = "SELECT @@IDENTITY";
                    identity = Convert.ToInt32(cm.ExecuteScalar());
                }
                finally
                {
                    ....
                }
            }

            return RowsAffected;
        }
Aseem Gautam
This is an ASP.NET application. I am concerned that this kind of code will lead to concurrency issues. This is why I want the Insert command to contain the SELECT SCOPE_IDENTITY().
P.Brian.Mackey
-1 for @@IDENTITY
gbn
+1  A: 

Hi - see if this helps.

Here's my SQL Code that works.

CREATE PROCEDURE [dbo].[Branch_Insert]
(
    @UserId uniqueidentifier,
    @OrganisationId int,
    @InsertedID int OUTPUT
)
AS
    SET NOCOUNT OFF;
INSERT INTO [Branch] ([UserId], [OrganisationId]) 
VALUES (@UserId, @OrganisationId);

SELECT Id, UserId, OrganisationId FROM Branch WHERE (Id = SCOPE_IDENTITY())
SELECT @InsertedID = SCOPE_IDENTITY()

Then when I create the Table Adapter - I can instantly see the @InsertedID parameter.

Then from code, all I do is:

int? insertedId = 0;
branchTA.Insert(userId, orgId, ref insertedId);

I'm not 100% whether using ref is the best option but this works for me.

Good luck.

Marko
I am using code generated within Visual Studio. I want to avoid writing all the Stored Proc's myself especially when the SELECT, INSERT (CRUD) methods are working so well. I thought "Refresh the data table" option would resolve this one issue. Do I really have to throw all the generated code out the window just so I can get an autoincremented identity?
P.Brian.Mackey
Nope - but you can go and modify the existing stored procedure, add @InsertedID in the parameters section, and then add SELECT @InsertedID = SCOPE_IDENTITY() on the last line. When you next click "Configure" on your table adapter, you should see an extra column. You don't have to delete your existing sprocs - you're just adding 2 new lines.
Marko
I like your idea. I can add the parameter, but for some reason when I edit the CommandText from "INSERT INTO [Sites] ([Name], [Description]) VALUES (@Name, @Description);SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY())" ...to... "INSERT INTO [Sites] ([Name], [Description]) VALUES (@Name, @Description);SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY());SELECT @InsertedID = SCOPE_IDENTITY() " My insert is corrupted and shows no parameters at all in the C#. I'll have to pick this up again tomorrow.
P.Brian.Mackey
No worries - when you get a chance can you paste your new SQL code as an EDIT to your question ?
Marko
This does work, but be sure to change the CommandText before you create the parameter when using the properties menu in visual studio. For some reason when you change the CommandText the Parameters reset. The final SQL I used: "INSERT INTO [Sites] ([Name], [Description]) VALUES (@Name, @Description); SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY()); SELECT @InsertedId = SCOPE_IDENTITY()". I just appeneded "; SELECT @InsertedId = SCOPE_IDENTITY()" to the generated sql.
P.Brian.Mackey
On a side note I am absolutely baffled that this common task is so non-trivial to perform.
P.Brian.Mackey
A: 

You have exactly 2 choices

  • change your SQL code manually
  • use whatever VS generates

I'd use this SQL and ExecuteScalar

INSERT INTO [dbo].[Sites] ([Name], [Description])
OUTPUT INSERTED.ID
VALUES (@Name, @Description);
gbn