【问题标题】:Is there a way to create an SQL Table from a dynamically generated fields (Field name, Field type)有没有办法从动态生成的字段(字段名称、字段类型)创建 SQL 表
【发布时间】:2021-07-08 09:26:39
【问题描述】:

我正在开发一个用户不知道内容是什么样的应用。所以我正在使用动态内容。我为我的控件创建了一个模型,名为 control details

public class ControlDetails
{
    public string ControlName{ get; set; }
    public string Type { get; set; }
    public string Label { get; set; }
    public bool IsRequired { get; set; }

}

用户填充数据库,我将它们作为表单呈现给我的 Blazor/Razor UI。但是,我想将表单中填写的数据提交到表格中。

渲染模型中的一个示例记录是

   ControlName => Lastname;
   Type => Textbox;
   Label => Enter Firstname ;
   IsRequired => Yes;

这是由用户动态创建的

如何动态创建一个对应的SQL表,当UI表单被填写时,数据将被提交到该表中?比如

  create table [Table Name]
  (
      ControlName Type
      ControlName Type
      ControlName Type
  )

【问题讨论】:

  • 这听起来像是一个糟糕的计划:让用户从前端创建表......对我来说听起来有点像 X-Y 问题:xyproblem.info
  • @BartKiers 用户不要创建表。我正在考虑如何在创建控件时生成它们添加的控件的相应表
  • 这个问题并不清楚。标题说:“有没有办法从动态生成的字段创建 SQL 表”。也许改写问题和/或添加更多细节?
  • 我给出的答案我假设你有一个这个 Blazor 项目可以访问的数据库?但是您还没有明确它是 Microsoft SQL 数据库还是其他某种数据库类型,或者您的 Blazor 项目是运行服务器端还是客户端。如果它运行在服务器端,它会改变相当多的安全考虑,以及是否需要为用户响应设置 API。

标签: c# asp.net-core blazor blazor-server-side


【解决方案1】:

我一直在开发一个包含动态内容的 Blazor 项目,使管理员用户可以控制要存储的内容。我的设置是在 PHP API 和 MySQL 上以节省成本,但我认为这个想法也应该适合你。

您希望避免让用户直接在数据库中创建表,而我所做的是让数据库存储动态内容的元数据。

所以在数据库中我制作了这样的表格: TableMetaData - 用于存储动态表元数据的表 ColumnsMetaData - 用于存储不同列的元数据、它们的数据类型等的表 TableColumnsMap - 一个映射表,控制每个表使用哪些列,使不同的动态表可以共享列类型 DataRow - 在动态表中存储所有数据行的表。

DataRow表的表定义:

  • 表:数据行
  • DatarowId - int(主键部分)
  • TableId - int(主键部分)
  • ReadOnly - bool(它是否应该是 可在 UI 中编辑)
  • UpdatedBy - 用户 ID(如果出现问题,它 可能对
  • 有用
  • LastUpdate - 日期(主键部分)每次更新都会在此表中创建一个具有新“lastupdate”日期时间的新行。
  • 活动 - bool (这是为了改善数据流我只让最新的更新 处于活动状态,这是为了避免必须检查“最大 最后更新”)
  • IsDeleted - bool(允许删除和恢复列。如果 最新的活动行版本被删除,它被删除。)

最后一张桌子是:

  • DataCell - 用于在行的到达列中存储数据的表。 DataCell 表的列
  • DatarowId - int - 数据行的 ID,它是主键部分
  • TableId - int - 此数据行所属的表,它的第二个主键部分。它指的是 tablemetadata 表。
  • ColumnId - int - 这个属于的列,这个Id指的是columnmetadata表
  • CellValue - 字符串 - 存储的数据。根据列元数据及其定义的数据类型,我将根据具体情况进行不同的转换。
  • UpdatedBy - userid - 对用户数据表的引用
  • LastUpdate - 日期 - 最后一个主键部分,用于存储此单元格发生的完整历史记录
  • Active - bool - 与数据行表相同,仅将最新版本设置为活动
  • IsDeleted - bool - 与数据行表相同,如果单元格已被删除,则为 true。

这是我在 Blazor 中用于显示此数据的代码示例:

<table class="table" style="table-layout: auto; height: auto; padding:0px; border-collapse:collapse; border-spacing: 0px;">
        <thead style="padding: 0px;">
            <tr style="padding: 3px;">
                @foreach (DataColumn col in tableView.Table.Columns)
                {
                    <th @onclick="@(
                        () =>
                                     {
                                         Console.WriteLine(col.ColumnName + " ASC");
                                         if(tableView.Sort == col.ColumnName + " ASC")
                                            tableView.Sort = col.ColumnName + " DESC";
                                         else
                                            tableView.Sort = col.ColumnName + " ASC";
                                         EventManager.AddRenderAction(() => StateHasChanged());
                                     }
                        )" style="padding: 3px; cursor: s-resize;">@col.ColumnName</th>
                    }
            </tr>
        </thead>

        <tbody>
            @{
                isReadOnly = false; // ApiService.IsAdmin ? false : true;
                int rowindex = 0;
            }

            @foreach (DataRowView rowView in tableView) // for (int r = 0; r < tableView.Table.Rows.Count; r++)
            {
                if (OnlyShowChanges && !rowView.Row.GetChangedColumns().Where(c => c.ColumnName != "Vælg").Any())
                    continue;

                DataRow row = rowView.Row;
                int gid = groupId;

                if (pageSize * pageIndex > rowindex || (pageSize * (pageIndex + 1) - 1) < rowindex)
                {
                    rowindex++;
                    continue;
                }
                rowindex++;

                if (groupId != 1 && ApiService.mUserGroups.Where(g => groupName != ((TableDropDownColumn)row.ItemArray[2]).SelectedItem).Any())
                    continue;

                int r = tableData.Rows.IndexOf(row);

                <tr bgcolor="white">
                    @for (int c = 0; c < tableView.Table.Rows[r].ItemArray.Length; c++)
                    {
                        var cell = tableView.Table.Rows[r].ItemArray[c];
                        var col = tableView.Table.Columns[c];
                        string cellvalue = Convert.ToString(tableView.Table.Rows[r].ItemArray[c]);
                        Type cellType = tableView.Table.Columns[c].DataType;

                        isReadOnly = col.ReadOnly;

                        //cellType = BusDataProps.GetValueOrDefault(tableView.Table.Columns[c].ColumnName);

                        bool nullable = false;
                        if (cellType.IsGenericType && cellType.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            nullable = true;
                            cellType = Nullable.GetUnderlyingType(cellType);
                        }

                        if (cellType == typeof(TableDropDownColumn))
                        {
                            string selectedDropDownValue = "Tom";
                            int colno = c;
                            if (tableView.Table.Rows[r].ItemArray.Any())
                                selectedDropDownValue = ((TableDropDownColumn)tableView.Table.Rows[r].ItemArray[c]).SelectedItem;
                            <td style="padding: 0px; border-collapse:collapse;">
                                <select NAME='@col.ColumnName' readonly="@isReadOnly" @onchange="@((ChangeEventArgs __e) => UpdateDropDown(__e,r,colno))" style="background-color:transparent;" value=@selectedDropDownValue>

                                    @foreach (var item in ((TableDropDownColumn)tableView.Table.Rows[r].ItemArray[c]).DropDownItems)
                                    {
                                        if (@isReadOnly && item != selectedDropDownValue)
                                            continue;
                                        <option value=@item>@item</option>
                                        @*<option value="lightblue" style="background-color:lightblue;">Blå</option>
                                            <option value="yellow" style="background-color:yellow;">Gul</option>
                                            <option value="white" style="background-color:whitesmoke;">Hvid</option>*@
                                    }
                                    @if (!tableView.Table.Rows[r].ItemArray.Any())
                                    {
                                        <option value="Tom">Tom</option>
                                    }
                                </select>
                            </td>
                        }
                        else if (cellType == typeof(string))
                        {
                            p_rows = @cellvalue.Trim().Length > 30 ? @cellvalue.Trim().Length / 30 : p_rows = 1;
                            s_rows = @cellvalue.Trim().Length > 30 ? @cellvalue.Trim().Length / 30 : s_rows = 1;
                            <td style="padding: 0px; border-collapse:collapse;">
                                <textarea cols="10" rows="@p_rows" @onchange="@((ChangeEventArgs __e) => row.SetField<string>(col, __e.Value.ToString()))" value="@cellvalue"
                                          readonly="@isReadOnly" style="background-color:transparent; resize:both; min-height:22px; overflow: hidden; padding: 3px; border:none;" wrap="hard" />
                            </td>
                        }
                        else if (cellType == typeof(int))
                        {
                            <td style="background-color:transparent; padding:0px">
                                <input type="number" pattern="/^-?\d+\.?\d*$/" readonly="@isReadOnly" onKeyPress="if(this.value.length==4) return false;" style="background-color:transparent; border:none; padding: 3px; width: 4em" value="@cellvalue"
                                       @onchange="@((ChangeEventArgs __e) => row.SetField<int>(col, int.Parse(__e.Value.ToString())) )" size="4" max="9999" id="number" />
                            </td>
                        }
                        else if (cellType == typeof(DateTime))
                        {
                            <td style="background-color:transparent; padding:0px">
                                @if ((nullable && ((DateTime?)cell).HasValue && ((DateTime?)cell).Value.ToString("yyyy") != "0001"))
                                {
                                    <input type="date" readonly="@isReadOnly" style="background-color:transparent; border:none; padding: 3px; width:10em" value="@(((DateTime?)cell).Value.ToString("yyyy-MM-dd"))"
                                           @onchange="@((ChangeEventArgs __e) =>row.SetField<DateTime?>(col,(DateTime?)__e.Value))" />
                                }
                                else if (!nullable && !string.IsNullOrWhiteSpace(cellvalue))
                                {
                                    <input type="date" readonly="@isReadOnly" style="background-color:transparent; border:none; padding: 3px; width:10em" value="@(((DateTime)cell).ToString("yyyy-MM-dd"))"
                                           @onchange="@((ChangeEventArgs __e) =>row.SetField<DateTime>(col,(DateTime)__e.Value))" />
                                }
                                else
                                {
                                    <input type="date" readonly="@isReadOnly" style="background-color:lightyellow; border:none; padding: 3px; width:10em" value="@DateTime.Today.ToString("yyyy-MM-dd")"
                                           @onchange="@((ChangeEventArgs __e) =>ChangeDate(row, col, __e))" />//
                                }
                            </td>
                        }
                        else if (cellType == typeof(bool))
                        {
                            <td style="padding: 0px;">
                                @if (!string.IsNullOrWhiteSpace(cellvalue) && Convert.ToBoolean(tableView.Table.Rows[r].ItemArray[c]))
                                {
                                    <input type="checkbox" style="background-color:transparent; border:none; width:6em; padding: 3px" checked
                                           @onchange="@((ChangeEventArgs __e) =>row.SetField<bool>(col,(bool)__e.Value))" />
                                }
                                else
                                {
                                    <input type="checkbox" style="background-color:transparent; border:none; width:6em; padding: 3px"
                                           @onchange="@((ChangeEventArgs __e) =>row.SetField<bool>(col,(bool)__e.Value))" />
                                }
                            </td>
                        }
                        else
                        {
                            <td style="padding: 0px;">
                                <textarea cols="40" rows="@p_rows" value="No value found type is @cellType.ToString()"
                                          readonly="@isReadOnly" style="background-color:transparent; resize:both; min-height:22px; overflow: hidden; padding: 3px; border:none;" wrap="hard" />
                            </td>
                        }
                    }
                </tr>
            }
        </tbody>
    </table>

所以我将所有表数据存储在 C# DataTables 中,但以我自己的方式使用它。这是显示数据的地方,也是我制作列编辑器的另一个地方,用户可以在其中创建自己的列。一种列类型用于下拉菜单。

【讨论】:

  • 谢谢你。但是我已经能够 tp 渲染到 UI。我需要的是如何存储数据。您提到元数据如何存储用户响应?我可以看看数据库结构是什么样的吗?
  • 这就是我试图用表定义来解释的。我存储表的元数据,放入这些动态表和列的数据存储在 DataRow 和 DataCell 表中。行表存储每一行​​,数据单元将数据存储在特定数据行的每一列中。我在 Blazor 中根据这些信息创建了一个 C# 数据表,一旦用户完成输入数据,我会检查已更新的内容并将其发送到我设置的 API。您可能让 Blazor 在服务器端运行,因此您可以更直接地对其进行更新。
  • 如果您只有一个表单,其中用户的输入没有太大变化,您可以制作一个更简单的版本。到目前为止,我没有创建不同的表,它只是一个具有动态列的表。
  • 我更新了我的回复以更好地解释数据库表。
猜你喜欢
  • 1970-01-01
  • 2013-07-07
  • 2014-03-10
  • 1970-01-01
  • 1970-01-01
  • 2014-11-30
  • 2011-04-12
  • 1970-01-01
  • 2020-06-12
相关资源
最近更新 更多