动态创建GridView

mic*_*ael 8 c# linq asp.net database-design

我想创建一个显示PDF文件记录的GridView.这些记录可以附加可由用户自定义的元数据,因此他们可以创建自己的列并在其中输入自己的信息.然后我希望它显示在GridView中,因此如果列顺序为-1,它们可以对每列和列顺序进行排序,它不会显示在GridView中.

例如,有一个静态表

DocumentsTable:
ID int
PDF_Folder varchar
UserID int
Run Code Online (Sandbox Code Playgroud)

然后还有另一个表,用户可以为其创建自己的列

MetaDataColumns:
ID int
userid int foreign key
KeyName varchar
Order int
Run Code Online (Sandbox Code Playgroud)

以及保存值的表

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
MetaDataID int foreign key
value varchar(100)
Run Code Online (Sandbox Code Playgroud)

现在的问题是我需要从MetaDataColumn中获取列来创建GridView,然后使用MetaDataValue表中的值填充它.我最初的计划是有一个动态创建GridView并向其添加列的函数,但是我仍然坚持如何使用MetaDataValue中的值作为列.或者我可以使用GridView AutoGenerate列,但我需要自定义SQL以显示自定义数据.我有点不知道如何处理这个问题.

我提出的一种方法是伪代码:

private DataTable CreateColumns()
{
   var columns =  select * from MetaDataColumns 
                  where userid = UserId;

   DataTable dt = new DataTable();

   foreach (column in columns)
   {
       dt.Columns.Add(new DataColumn(column[keyName], typeof(string));  //assumes all string
   }

 return dt
}

private void PopulateDG(DataGrid dg)
{
    var documents = select * from DocumentsTable
                     where userid=UserId;

    foreach (document in documents)
    {            
        var columnValues = select * from MetaDatavalues 
                           documentID == document.id;

        DataRow dr = dg.NewRow();
        dr[columnValues.KeyName] = columnValues.value;

    }

 }

 private void LoadGV()
 {  
   DataGrid dg = CreateColumns();
   PopulateDG(dg);
   GridView.datasource = dg;
   GridView.DataBind();
  }
Run Code Online (Sandbox Code Playgroud)

我不喜欢这个设计的一个原因是文档表中的每一行都创建了另一个查询.我不确定这是否是SQL的问题?

Ben*_*Ben 2

你的问题主要是数据库设计的问题。您必须动态添加列,因为您已经将列(在 3NF 中)转换为表中的行。显然,这是因为您允许用户添加自己的列 - 我的心不寒而栗,但这就是应用程序的工作方式:-)。

由于结构的原因,MetaDataColumns我将假设用户能够定义一组列名称,然后他们可以根据需要选择将其应用于单个文档。

我认为问题在于,在完全非规范化的数据库中尝试正确规范化所有内容时,您已经给自己带来了很多麻烦。我的解决方案是对您的表进行非规范化MetaDataValues。您没有提及您正在使用什么 RDBMS,但 MySQL 有4096 列或 65k 字节的硬限制。Oracle 中的限制为1000, SQL Server 中的限制为1024

如果将结构更改MetaDataValues为以下内容,您应该能够在其中容纳至少332 组信息。这在 上是唯一的UserIDDocumentID因此理论上您可以删除代理键ID

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
KeyName1 varchar
Order1 int
Value1 varchar(100) 
...
KeyNameN varchar
OrderN int
ValueN varchar(100)
Run Code Online (Sandbox Code Playgroud)

当然,这确实将允许单个用户创建的列数上限设置为 332;但是,限制用户的能力是很正常的,任何能够想到在单个 PDF 上存储 332 个独立元数据的人都应该受到某种限制。

如果您确实有特别痴迷于信息的用户,您始终可以声明具有相同结构的第二个表并继续填充它。

这样做意味着MetaDataColumns除了向用户显示选项之外不会用于任何其他用途。每次进行更改时您都必须进行更新MetaDataValues,并且确保您没有覆盖已经存在的信息可能会有点痛苦。我怀疑您必须执行一些操作,例如在更新记录之前选择记录、迭代KeyName1..KeyNameN并填写第一个没有任何数据的记录。或者,您也可以编写一个绝对糟糕的 SQL 查询。不管怎样,这都会成为“瓶颈”。

另一种选择是向 中添加一个附加列MetaDataColumns,该列指示与哪个 N 列相关,但这将用户绝对限制为 332 列,而不是每个文档 332 列。

然而,您从数据库中进行选择现在非常简单:

MetaDataValues:
ID int
UserID int foreign key
DocumentID int foreign key
KeyName1 varchar
Order1 int
Value1 varchar(100) 
...
KeyNameN varchar
OrderN int
ValueN varchar(100)
Run Code Online (Sandbox Code Playgroud)

无需尝试迭代表动态生成 1,000 个列选择语句。所有信息都在那里,您可以轻松访问。

归根结底,您问题的“正确”答案取决于您想把时间花在哪里。您是否希望多花半秒来创建或更新文档,或者多花半秒(可能更多)来选择该文档上的信息。

就我个人而言,我认为用户明白创建某些东西需要时间,但没有什么比等待很长时间才能看到某些东西出现更烦人的了。

还有另一种社交解决方案,而不是数据库解决方案。不允许您的用户创建自己的列。选择用户想要的最常见的元数据片段,并在数据库中以标准化形式正确创建它们。您将能够创建具有正确数据类型的列(从长远来看,这将为您省去很多麻烦)并且更轻松。我怀疑你是否足够幸运,能够做到这一点;但这是值得牢记的。