【问题标题】:How to insert child category by specific parent category into table using ado.net?如何使用 ado.net 将特定父类别的子类别插入表中?
【发布时间】:2016-10-25 11:14:08
【问题描述】:

我正在尝试将 Excel 中的类别和子类别插入数据库表中。

我有 1 个 Excel 文件,其中包含一些数据,我正在从这个 Excel 文件创建 dataset,其中包含大量 datatables

在这个dataset我有2个datatables,形式如下:

包含记录的数据表 0:类别

ParentCategory Description
  Electronics   jhdkhsd
  Sports        kjshfhs

包含记录的数据表 1:SubCategory

Subcategory ParentCategory  Description
  Mobile       Electronics   weprwp
  Tv           Electronics   sdflskd
  Balls        Sports        kjshdfkjh
  Shoes        Sports        uytuyt

现在我的数据库表是这样的:

类别Id,Name,Description,parentid

到目前为止,我已成功插入父类别,但现在尝试插入子类别,但这是我目前正在努力的地方。

到目前为止这是我的代码:

var dsFinal = new DataSet();

    //Some code to read excel sheets and data from excel and create datatables and records with it.


 dsControlSheet.Tables[0].Columns.Add("Id");
 DataColumn parentId = new DataColumn("ParentId", typeof(int));
 parentId.DefaultValue = 0;
 dsFinal.Tables[0].Columns.Add(parentId);
 dsFinal.Relations.Add("Abc",dsFinal.Tables[0].Columns["ParentCategory"],
 dsFinal.Tables[1].Columns["ParentCategory"],false); //creating relation ship between Category datatable
// and SubCategory datatable on field ParentCategory



using (SqlConnection connection = new SqlConnection(""))
     {
       SqlDataAdapter adapter = new SqlDataAdapter();
       var insertCommand = new SqlCommand("insert into Category (Name,Description) values (@ParentCategory,@Description) SET @Id = SCOPE_IDENTITY()", connection);
       var parameter = insertCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "Id");
       insertCommand.Parameters.Add("@ParentCategory", SqlDbType.NVarChar, 50, "ParentCategory");
       insertCommand.Parameters.Add("@Description", SqlDbType.NVarChar, 50, "Description");
       parameter.Direction = ParameterDirection.Output;
       insertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
       adapter.InsertCommand = insertCommand;
       adapter.Update(dsFinal.Tables[0]); //successfully inserted parent category and got autoincremented value in Id column of my 0th datatable

       //trying to insert child category using above insert command
       foreach (DataRow parentCategory in dsFinal.Tables[0].Rows)
                  {
                      var child = parentCategory.GetChildRows("Abc").CopyToDataTable();//get child category of particular parent 
                      adapter.Update(child);
                  }
     }

这里在最后一个循环中插入子类;我对如何使用感到困惑 插入子类别的相同insertCommand 变量?

更新:我使用datatable Expression 计算parentid 是这样的:

using (SqlConnection connection = new SqlConnection(""))
         {
           SqlDataAdapter adapter = new SqlDataAdapter();
           var insertCommand = new SqlCommand("insert into Category (Name,Description) values (@ParentCategory,@Description) SET @Id = SCOPE_IDENTITY()", connection);
           var parameter = insertCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "Id");
           insertCommand.Parameters.Add("@ParentCategory", SqlDbType.NVarChar, 50, "ParentCategory");
           insertCommand.Parameters.Add("@Description", SqlDbType.NVarChar, 50, "Description");
           parameter.Direction = ParameterDirection.Output;
           insertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
           adapter.InsertCommand = insertCommand;
           adapter.Update(dsFinal.Tables[0]); //successfully inserted parent category and got autoincremented value in Id column of my 0th datatable

          //For inserting child category..
           //added column parentid to store child category
           SqlDataAdapter da = new SqlDataAdapter();
           dsFinal.Tables[1].Columns.Add("ParentId", typeof(int), "IIF(Parent.ParentCategory=ParentCategory,parent.Id,0)");
           var insertChildCategoryCommand = new SqlCommand("insert into Category (Name,Description,ParentId) values (@Subcategory,@Description,@ParentId) SET @Id = SCOPE_IDENTITY()", connection);
           var parameter1 = insertChildCategoryCommand.Parameters.Add("@Id", SqlDbType.Int, 0, "Id");
           insertChildCategoryCommand.Parameters.Add("@Subcategory", SqlDbType.NVarChar, 50, "Subcategory");
           insertChildCategoryCommand.Parameters.Add("@Description", SqlDbType.NVarChar, 50, "Description");
           insertChildCategoryCommand.Parameters.Add("@ParentId", SqlDbType.int, 0, "ParentId");
           parameter1.Direction = ParameterDirection.Output;
           insertChildCategoryCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
           da.InsertCommand = insertChildCategoryCommand;
           //Error here that computed column cannot be inserted.Here computed column is parentid
           da.Update(dsFinal.Tables[1]);            
         }

错误Computed column(parentid) cannot be inserted.

【问题讨论】:

    标签: c# .net datatable ado.net dataadapter


    【解决方案1】:

    您几乎可以使用最新的代码。

    唯一的问题是计算的列不允许用于插入/更新数据库表。顺便说一句,错误信息不是“Computed column(parentid) cannot be insert.”,而是:

    SourceColumn 'ParentId' 的列映射失败,因为 DataColumn 'ParentId' 是计算列。

    我同意该消息可能会更好,而且我没有找到任何描述该消息的文档。最可能的原因是计算的列没有正常存储。

    无论原因是什么,这就是现实。除了创建常规列并手动填充数据之外,您别无选择。

    有很多方法可以做到这一点(高效和低效),但是一旦您已经创建了关系,就可以使用DataRow.GetParentRow 方法来定位相关的类别记录。

    说了这么多,换行

    dsFinal.Tables[1].Columns.Add("ParentId", typeof(int), 
        "IIF(Parent.ParentCategory=ParentCategory,parent.Id,0)");
    

    使用以下 sn-p:

    var dtSubCategory = dsFinal.Tables[1];
    dtSubCategory.Columns.Add("ParentId", typeof(int));
    foreach (var drSubCategory in dtSubCategory.AsEnumerable())
    {
        var drCategory = drSubCategory.GetParentRow("Abc");
        drSubCategory["ParentId"] = drCategory != null ? drCategory["Id"] : 0;
    }
    

    你就完成了。

    编辑:让我说清楚。这里唯一的时间关键操作是按名称定位类别 ID。使用关系和GetParentRow 等效于评估访问parent 的表达式,就像您尝试的那样。数据关系在内部维护了一个查找结构以支持此类操作。

    如果您想获得最佳性能,请不要创建关系,而是创建字典。你需要的是一个名字(字符串),找到对应的 id(int),所以Dictionary<string, int> 是一个完美的候选:

    var categoryIds = dsFinal.Tables[0].AsEnumerable().ToDictionary(
        dr => dr.Field<string>("ParentCategory"), // key
        dr => dr.Field<int>("Id") // value
    );
    var dtSubCategory = dsFinal.Tables[1];
    dtSubCategory.Columns.Add("ParentId", typeof(int));
    foreach (var drSubCategory in dtSubCategory.AsEnumerable())
    {
        int categoryId;
        categoryIds.TryGetValue(drSubCategory.Field<string>("ParentCategory"), out categoryId);
        drSubCategory["ParentId"] = categoryId;
    }
    

    【讨论】:

    • 我们可以在没有 forloop 的情况下做到这一点吗,因为我有数千个类别,每个类别都有很多子类别,这就是为什么我想出表达式以避免 forloop.with 在循环中执行此操作如果有数千个类别和子类别,速度会非常慢。你觉得呢??
    • 正如我在答案中所写,“你别无选择”。循环也没有问题,唯一的时间关键操作是找到相应的父级。无论如何,您的计算列在内部执行了哪个操作(当您在表达式中加入 Parent 时)。
    • 我也对此进行了很多搜索,但没有找到任何解决方案。所以这个表达式和循环在性能方面会相同吗??
    • 我相信是的。同样,外部循环不是问题(当您调用Update 时,数据适配器也会循环遍历表记录)。内循环将是一个问题,例如,如果您尝试使用线性搜索来定位类别记录。幸运的是,当您使用数据关系时,情况并非如此,因为它创建了一个快速查找内存结构。另一种(可能更快)的方法是使用字典而不是数据关系。
    • 很棒的解释。例如,在这种情况下,我将如何使用这个字典而不是数据关系??
    【解决方案2】:

    更改您的关系,使其使用 Id 和 ParentId,您的第一个解决方案将正常工作。

    当您更新第一个表并获取自动增量值时,它将通过关系自动更新第二个表。这是一个例子。

    DataSet ds = new DataSet();
    DataTable dt = new DataTable();
    var colParent = dt.Columns.Add("id", typeof(int));
    
    DataTable dtChild = new DataTable();
    var colChild = dtChild.Columns.Add("parentid", typeof(int));
    
    ds.Tables.Add(dt);
    ds.Tables.Add(dtChild); 
    ds.Relations.Add("relation1", colParent, colChild);
    
    DataRow rowParent = dt.NewRow();
    rowParent["id"] = 1;
    dt.Rows.Add(rowParent);
    DataRow rowChild = dtChild.NewRow();
    rowChild["parentid"] = rowParent["id"];
    dtChild.Rows.Add(rowChild);
    
    // at this point, parentid from rowChild is "1".    
    rowParent["id"] = 10; // DataAdapter.Update simulation.
    // and now parentid from rowChild has been update via the relation to "10". 
    

    使用此方法时,我通常会将列设置为具有种子和步长设置为 -1 的标识列,从而将列设置为获取临时“id”值。这将在第三行之后。

    colParent.AutoIncrement = true;
    colParent.AutoIncrementSeed = -1;
    colParent.AutoIncrementStep = -1;
    

    除非您真的需要在这些字符串字段上建立关系,否则无需执行任何循环、计算列或任何过于复杂的操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多