子对象集合作为变量列表中的列

时间:2018-09-27 08:40:00

标签: sql-server linq linq-to-sql

我有一个代表清单信息的数据库。 公司A收到发票并将其拆分为公司A,B,C。

我们需要为每个收到的发票创建一个清单文件。

我创建的数据库具有以下规格:

  • 文档具有ID的其他信息(描述,发票日期等)
  • 它具有可变的行数,每个原始发票行各占一个。
  • 它具有可变的列数,对于公司,发票将被拆分到。
  • 同一文档中的每一行具有相同的列数。

这是db的剥离表示:

+-----------------+   +------------------+   +--------------------+
| DOCUMENTS       +   | ROWS             |   | COLS               |
+-----------------+   +------------------+   +--------------------+
| DocID        PK |1-*| DocID         PK |1-*| DocID           PK |   +-----------------+
| ...             |   | Row           PK |1-*| Row             PK |   | COMPANIES       |
+-----------------+   | Description      |   | Col             PK |   +-----------------+ 
                      | RebillAmount     |   | CompanyID          |*-1| CompanyID    PK |
                      +------------------+   | RebilledAmount     |   | Description     |
                                             +--------------------+   +-----------------+

我想要实现的是网格表示,如下所示:

+-----------------+--------------+-------------+-------------+-------------+
| Description     | RebillAmount | "Company A" | "Company B" | "Company C" | ...
+-----------------+--------------+-------------+-------------+-------------+
| ISP invoice     |   € 1.000,00 |    € 333,00 |    € 333,00 |    € 334,00 |
| Insurance       |     € 600,00 |      € 0,00 |    € 400,00 |    € 200,00 | 
| ...             |              |             |             |             |
+-----------------+--------------+-------------+-------------+-------------+

使用Linq to SQL检索数据。

我通过手动遍历行和列并使用字典来跟踪列来成功创建了DataGridView:

Dim CompanyCols as new Dictionary(Of Integer, DataGridViewColumn)

Dim DocID = 1
Dim Doc = (From d In dc.Documents Where f.DocID = DocID Select d).Single

For Each r In Doc.Rows
    Dim rowid = Grid.Rows.Add()
    Dim row = Grid.Rows(rowid)
    row.Cells(ColEnum.Description).Value = r.Description
    riga.Cells(ColEnum.RebillAmount).Value = r.RebillAmount
    Dim col As DataGridViewColumn
    For Each c In r.Cols
        If CompanyCols.ContainsKey(c.CompanyID) Then
            col = CompanyCols(c.CompanyID)
        Else
            Dim newcol = New DataGridViewTextBoxColumn With {.HeaderText = c.Companies.Description}
            col = Grid.Columns(Grid.Columns.Add(newcol))
            CompanyCols.Add(c.CompanyID, col)
        End If
        row.Cells(col.Index).Value = c.RebillAmount
    Next
Next

不幸的是,这种方法断开了与数据库对象的链接,因此,如果我需要编辑数据并保存它们,则必须扫描整个网格并手动更新“ Doc”对象及其子对象。

是否可以将行“绑定”到Doc.rows和将列“绑定”到Doc.cols?

谢谢!

1 个答案:

答案 0 :(得分:0)

因此,您有一系列文档。您要在表中显示一个文档。表中的列代表文档中提到的Companies。不同的文件可能具有不同的公司数量。

幸运的是,您只希望在表格中仅显示一个文档:获得文档后,您知道应该将哪些公司作为列添加到表格中。

尽管您定义了一个文档中的每一行具有相同数量的列,因此,每个文档都使用相同数量的公司,但您并未指定每行使用相同的公司

所以您最终可能会这样:

Rows[0] has two Cols:
    Cols[0] has Company[0]
    Cols[1] has Company[1]
Rows[1] has two Cols:
    Cols[0] has Company[0]
    Cols[1] has Company[2]
Rows[3] has two Cols:
    Cols[0] has Company[3]
    Cols[1] has Company[4]

如果我查看您的代码,在这种情况下,您希望显示5列。

Row        Company[0]   Company[1]  Company[2]   Company[3]  Company[4]
----------------------------------------------------------------------------
Row[0]       aaa           bbb
Row[1]       ccc                       ddd
Row[2]                                               eee         fff

表中的每个公司列都有一个列标题,用于显示公司名称。如果您有一行,则如果该行未使用公司,则该行的单元格的值将为null,或者使用使用其名称显示在列标题中的公司的单元格的RebilledAmount

这意味着一旦您获取了文档,就需要了解所有使用过的公司。每个公司成为一个专栏。在每列中,您都需要记住公司名称和公司ID

对于要添加的每一行,您必须找出与该行的列所使用的公司的ID相匹配的列。

我的视觉基础有点生锈,所以我将向您展示C#中的代码。我敢肯定,您将能够掌握该想法并将其转换为视觉基础

Document fetchedDocument = ...

// find out which companies are used by the Document.
// from every company remember the name and the Id:
var companyInfo = fetchedDocument.Rows      // take all rows
    .SelectMany(row => row.Cols)            // from these rows take all Cols
    .Select(col => new
    {
         CompanyId = col.CompanyId,         // from every Col take the CompanyId
         Name = col.Company.Name,           // and the company name
    })  
    .Distinct();                            // remove duplicates

如果在获取文档时未获取公司名称,则必须在单独的查询中获取它们。如果您在本地某处有公司名称,则可以在“词典”中或之后通过以下方式获取它们:

var companyIds = fetchedDocument.Rows        // take all rows
    .SelectMany(row => row.Cols)             // from all rows take the Cols
    .Select(col => col.CompanyId)            // from every col take the companyId
    .Distinct();                             // remove duplicates

// add the CompanyNames
var companyInfo = companyIds.Select(companyId => new
{
    Id = companyId,
    Name = CompanyCollection[companyId].CompanyName,
});

一旦您知道所有公司,便可以添加所有列。我们将首先添加DescriptionRebillAmount的列:

myDataGridView.Columns.Clear();

// Column Description:
int columnDescriptionIndex = myDataGridView.Columns.Add(new DataGridViewColumn()
{
    Name = "ColDescription",             // name of the column
    HeaderText = "Description",          // header text
    ValueType = typeof(string),          // this column shows strings
    DataPropertyName = "Description",    // this column shows property Description
});

// Column RebillAmount:
int columnRebillAmountIndex = myDataGridView.Columns.Add(new DataGridViewColumn()
{
    Name = "ColRebillAmount",             // name of the column
    HeaderText = "RebillAmount",          // header text
    DataPropertyName = "RebillAmount",    // this column shows property RebillAmount
    ValueType = typeof(decimal),          // this column shows decimals
    // if desired: add a DefaultCellStyle to define the display format of the amount
}

现在添加公司列。在每个公司列中,您都需要记住所显示公司的ID。为此,我们创建一个DataGridViewColumn的子类,其中包含所显示公司的ID:

class CompanyColumn : DataGridViewColumn
{
    public int CompanyId {get; private set;}

    public CompanyColumn(int companyId, string companyName)
    {
        this.CompanyId = companyId;
        Name = companyName;               // name of the column
        HeaderText = companyName;         // header text
        ValueType = typeof(decimal);      // this column shows decimals
        // if desired: add a DefaultCellStyle to define the display format
    }
}

每个二手公司增加一栏;为了快速查找:将列索引放在Dictionary中, 键为companyId,值为columnIndex

var companyColumns = new Dictionary<int, int>()
foreach(var usedCompany in usedCompanies)
{
     var columnIndex = myDataGridView.Columns
         .Add(new CompanyColumn(usedCompany.Id, usedCompany.Name);
     companyColumns.Add(usedCompany.Id, columnIndex);
}

添加所有列之后,可以添加行:

myDataGridView.Rows.Clear();

foreach(var row in document.Rows)
{
    var addedRow = myDataGridView.AddRow();

    // Description and RebillAmount
    addedRow.Cells[colDescriptionIndex].Value = row.Description;
    addedRow.Cells[colRebillAmountIndex].Value = row.RebillAmount;

    // add the value for every company in the row, in the correct column
    foreach (var col in row.Cols
    {
        // every col has a companyId
        // find in the dictionary the index of the column that represents this company
        int columnIndex = companyColumns[col.CompanyId];

        // put the RebilledAmount in the column with the found index
        addedRow.Cells[columnIndex].Value = cell.RebilledAmount;
    }
}

现在,如果更改了单元格,则要更改“文档”中的相应值:

void UpdateChangedCell(DataGridViewCell cell)
{
     if (cell.ColumnIndex == colDescriptionIndex)
     {    // the changed cell is a description cell
          string description = (string)cell.Value;
          document.Rows[cell.RowIndex].Description = description;
     }
     else if (cell.ColumnIndex == colRebillAmountIndex)
     {    // the changed cell is a rebillAmount cell
          decimal rebillAmount = (decimal)cell.Value;
          document.Rows[cell.RowIndex].RebillAmount = rebillAmount;
     }
     else
     {   // the changed cell is in one of the company columns:
         CompanyColumn column = (CompanyColumn)myDataGridView.Columns[cell.ColumnIndex];
          int companyId = column.CompanyId;
          decimal rebilledAmount = (decimal)column.Value;

          // update the one and only Col in this Row that has CompanyId
          var colToUpdate = row.Cols
              .Where(col => col.CompanyId == companyId)
              .Single();
          colToUpdate.RebilledAmount = rebilledAmount;
     }
}