【问题标题】:Getting @@IDENTITY from TableAdapter从 TableAdapter 获取 @@IDENTITY
【发布时间】:2011-04-11 21:20:17
【问题描述】:

我正在尝试完成一项看似简单的任务,却变成了几个小时的冒险:从TableAdapter.Insert() 获取@@Identity

这是我的代码:

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");
}

一个answer 建议我可能需要做的就是更改ExecuteMode。我试过了。这使得GetData() 退出工作(因为我现在返回一个标量而不是行数据)(我需要保留 GetData())。它也没有解决insertedID变量仍然设置为1的问题。

我尝试在 TypedDataSet.XSD 中创建第二个 TableAdapter 并将该适配器的属性设置为“标量”,但它仍然失败,变量的值为 1。

生成的插入命令是

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

并且还设置了“刷新数据表”(在Insert和Update语句后添加select语句来检索身份)。

环境

SQL Server 2008 R2、Visual Studio 2010、.NET 4、Windows XP,都是本地同一台机器。

这是什么原因造成的?

编辑/更新

我想澄清一下,我在 Visual Studio 中使用了自动生成的代码。我不知道生成代码的“工具”是什么,但是如果您双击 *.XSD 文件,它会显示 SQL 表架构和关联的 TableAdapter 的 UI。我想继续使用自动生成的代码并以某种方式启用获取身份。我不想用存储过程手动编写这些。

【问题讨论】:

  • 如果可能,请使用SCOPE_IDENTITY()IDENT_CURRENT(tablename) 而不是@@IDENTITY@@IDENTITY 值可能不是您所期望的(它是在最后一个事务中涉及的任何表中生成的最后一个 IDENTITY,包括那些可能从触发器或类似的东西接收 INSERT 的表)。
  • 只是对您的编辑的注释-自动生成的代码会根据您的存储过程中的变化进行检测-因此根据我下面的回答,您只需修改 2 行代码即可获得所需的效果。我认为没有其他办法。

标签: c# asp.net sql tableadapter


【解决方案1】:

真正的答案:

  • 阅读以下注意事项!

从 tableadapter 插入函数中获取标识

我经常收到有关此问题的问题,但从未找到 是时候把它写下来了。

好吧,问题如下:你有一个主键和 int 的表 类型定义为 Identity,插入后您需要知道 PK 值 新插入的行。完成此操作的步骤如下:

使用向导在 查询主体只需在底部添加 SELECT SCOPE_IDENTITY() 保存此查询,更改此查询的 ExecuteMode 属性 NonQuery to Scalar 在您的代码中编写以下内容(ta 是 TableAdapter 实例):

int id;

try
{
 id = Convert.toInt32(ta.InsertQuery(firstName, lastName, description));
}
catch (SQLException ex)
{
//...
}
finally
{
//...
}

用这个赚钱! :) 2009 年 3 月 12 日由 Draško Sarić 发表

来自: http://quickdeveloperstips.blogspot.nl/2009/03/get-identity-from-tableadapter-insert.html

注意事项:

  • 可以通过生成的插入查询的属性将 ExecutMode 设置为 Scalar。 (按 F4)。

  • 在我的版本 (Visual Studio 2010 SP1) 中,选择语句是自动生成的。

【讨论】:

    【解决方案2】:

    这是我的有效 SQL 代码。

    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()
    

    然后,当我创建表适配器时 - 我可以立即看到 @InsertedID 参数。

    然后从代码中,我所做的就是:

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

    我不是 100% 使用 ref 是否是最佳选择,但这对我有用。

    祝你好运。

    【讨论】:

    • 我正在使用 Visual Studio 中生成的代码。我想避免自己编写所有存储过程,尤其是当 SELECT、INSERT (CRUD) 方法运行良好时。我认为“刷新数据表”选项可以解决这一问题。我真的必须将所有生成的代码扔出窗口才能获得自动递增的身份吗?
    • 不——但是你可以去修改现有的存储过程,在参数部分添加@InsertedID,然后在最后一行添加SELECT @InsertedID = SCOPE_IDENTITY()。当您下次单击表格适配器上的“配置”时,您应该会看到一个额外的列。您不必删除现有的存储过程 - 您只需添加 2 个新行。
    • 我喜欢你的想法。我可以添加参数,但由于某种原因,当我从“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() " 我的插入已损坏,在 C# 中根本没有显示任何参数。明天我得再把它捡起来。
    • 不用担心 - 当您有机会时,您可以将新的 SQL 代码作为 EDIT 粘贴到您的问题中吗?
    • 这确实有效,但在 Visual Studio 中使用属性菜单时,请务必在创建参数之前更改 CommandText。由于某种原因,当您更改 CommandText 时,参数会重置。我使用的最后一条 SQL:“INSERT INTO [Sites] ([Name], [Description]) VALUES (@Name, @Description); SELECT Id, Name, Description FROM Sites WHERE (Id = SCOPE_IDENTITY()); SELECT @InsertedId = SCOPE_IDENTITY()"。我只是将“; SELECT @InsertedId = SCOPE_IDENTITY()”附加到生成的sql。
    【解决方案3】:

    所有信息都在这里,但我没有找到任何完整的答案,所以这是我使用的完整步骤。

    添加一个插入查询,并将SELECT SCOPE_IDENTITY() 附加到它上面,如下所示:

    INSERT INTO foo(bar) VALUES(@bar);
    SELECT SCOPE_IDENTITY()
    

    确保添加一个 ;到 VS 为您创建的 INSERT 语句的末尾。

    完成添加查询向导后,确保在设计视图中选择了查询,然后从属性窗格中将执行模式更改为 Scalar

    确保在从代码中调用查询时使用 Convert.ToInt32(),如下所示:

    id = Convert.ToInt32( dataTableAdapter.myInsertQuery("bar") )
    

    没有 Convert.ToInt32 不会出现编译器错误,但会得到错误的返回值。

    另外,每次修改查询时,都必须将执行模式重置回Scalar,因为每次VS都会将其改回Non Query

    【讨论】:

    • 这与 Remco 的答案相同,最后一次编辑是在此之前一年半。
    • 你是对的@AgapwIesu,对不起,不知道我怎么没注意到。那是2年前(现在),所以不知道。我想我会把它留在这里,但如果有人想删除它也不会感到难过。
    【解决方案4】:

    这是你的做法(在视觉设计器中)

    1. 右键单击表适配器并“添加查询”
    2. SQL 语句 - 选择更新(最佳自动生成参数)
    3. 复制并粘贴您的 SQL,它可以是多行的,只要确保“查询设计器”没有打开,因为它将无法解释多个命令 -我的示例显示了一组示例“合并”语句(请注意,新的 SERVERS 具有合并命令)。

      UPDATE YOURTABLE
      SET  YourTable_Column1 = @YourTable_Column1, YourTable_Column2 = @YourTableColumn2
      WHERE (YourTable_ID = @YourTable_ID)
      IF @@ROWCOUNT=0
        INSERT INTO YOURTABLE ([YourTable_Column1], [YourTable_Column2])
        VALUES (@YourTable_Column1, @YourTable_Column2)
      @YourTable_ID = SCOPE_IDENTITY()
      
    4. 从查询属性窗口/边栏中更改/添加 @YourTable_ID 参数。在 Parameter Collection Editor 中,ID 参数需要有一个 InputOutput 方向,以便在调用 Table Adapter 函数时更新该值。 (特别注意:确保您制作 InputOutput 的任何列,设计器都没有将此列设置为“只读”并且数据类型也匹配,否则更改数据表中的列或参数相应的信息)

    这应该可以省去为这样一个简单的活动编写存储过程的需要。

    太棒了。您会注意到,这种方法是一种执行数据层功能的快速方法,无需中断 SQL 过程并编写大量过程。唯一的问题是,你必须跳很多舞......

    【讨论】:

      【解决方案5】:

      您需要设置插入以将标识作为输出值返回,然后在适配器中将其作为参数获取。

      这两个链接应该可以帮助您:

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

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

      【讨论】:

        【解决方案6】:

        你有两个选择:

        • 手动更改 SQL 代码
        • 使用 Visual Studio 生成的任何内容

        我会使用以下 SQL 和 ExecuteScalar。

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

        【讨论】:

          【解决方案7】:

          一种方法是在插入命令之后运行选择查询。一个好方法是像这样包装原始命令:

              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;
              }
          

          【讨论】:

          • 这是一个 ASP.NET 应用程序。我担心这种代码会导致并发问题。这就是为什么我希望插入命令包含 SELECT SCOPE_IDENTITY()。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多