Displaying Custom Fields in LINQ to Entity Dynamic Select

c# dynamic-linq entity-framework linq vb.net

Question

Let's say I have an entity Container with four properties: ContainerID (Integer), ContainerNumber (String), Title (String), and Date (Date).

I have a second entity called ContainerCustomFields that has three properties: CustomFieldID (Integer), ContainerID (Integer and Foreign Key), FieldName (String), FieldContent (String). The ContainerCustomFields entity stores user-defined custom fields for the Container entity. The data looks like this:

For Container:

Container
---------
ID  |     Number     |     Title     |    Date      |
=====================================================
1   |      10000     |   1st Title   |   1/12/2017  |
2   |      10543     |   2nd Title   |   1/31/2017  |
3   |      10667     |   3rd Title   |   4/12/2016  |
4   |      12889     |   4th Title   |   5/23/2012  |

For Custom Fields:

ID  |   ContainerID   |   FieldName   |    Content  |
=====================================================
1   |        1        |    Colour     |    Blue     |
2   |        1        |    Height     |    5000     |
3   |        1        |    Length     |    9100     |
4   |        4        |    Colour     |    Gray     |

Is it possible to return the data so it looks like this:

Container
---------
ID  |     Number     |     Title     |    Date      |   Colour   |  Height  |  Length  |  
====================================================================================
1   |      10000     |   1st Title   |   1/12/2017  |    Blue    |   5000   |   9100   |
2   |      10543     |   2nd Title   |   1/31/2017  |            |          |          |
3   |      10667     |   3rd Title   |   4/12/2016  |            |          |          |
4   |      12889     |   4th Title   |   5/23/2012  |    Gray    |          |          |

I'm able to get a list of the custom fields and their values for each container as well as select the columns that I want from the entity using a select statement. How do I combine these two statements?

For Each customField In _db.ContainerCustomFields
  .Where(Function(cf) cf.ContainerID = newBox.ContainerID)

returnContainers.Select(Function(c) New With 
  {.ContainerID = c.ContainerID, 
   .Number = c.ContainerNumber,
   .Title = c.Title,
   .Date = c.Date })

I'm using Web API 2 to return this as an anonymous type for my web application.

1
1
3/14/2017 12:02:22 AM

Popular Answer

First group into each Container, turning Fields & values into arrays within the grps:

var grps = from c in _db.Containers
join ccf in _db.ContainerCustomFields
    on c.ContainerID equals ccf.ContainerID
select  new 
        {ContainerId = c.ContainerID, 
         ContainerNumber= c.ContainerNumber, 
         Title = c.Title, 
         Date = c.Date, 
         FieldName = ccf.FieldName, 
         FieldContent = ccf.FieldContent}
into f
group f by f.ContainerId
into myGroup
select new
{
    ContainerId = myGroup.Key,
    ContainerNumber = myGroup.Max(d => d.ContainerNumber),
    Title = myGroup.Max(d => d.Title),
    Date = myGroup.Max(d => d.Date),
    Fields = myGroup.Select(d => d.FieldName).ToArray(),
    Values = myGroup.Select(d => d.FieldContent).ToArray()
};

Now find all of the Field Names that can appear:

var fields = from ccf in _db.ContainerCustomFields 
             group ccf by ccf.FieldName
             into grp
             select grp.Key;

Now create a datatable that contains columns for our containers and all possible fields:

DataTable dt = new DataTable();
dt.Columns.Add("ContainerId", typeof(Int32));
dt.Columns.Add("ContainerNumber", typeof(String));
dt.Columns.Add("Title", typeof(String));
dt.Columns.Add("Date", typeof(DateTime));
foreach(var fld in fields)
{
    dt.Columns.Add(fld, typeof(String));
}

Now for each group of data add a row for to our datatable and add values for each fields that are relevant:

foreach (var row in grps)
{
    DataRow dr = dt.NewRow();
    dr["ContainerId"] = row.ContainerId;
    dr["ContainerNumber"] = row.ContainerNumber;
    dr["Title"] = row.Title;
    dr["Date"] = row.Date;
    for (int i = 0; i < row.Fields.Count(); i++)
    {
        dr[row.Fields[i]] = row.Values[i];
    }
    dt.Rows.Add(dr);    
}

Now you can use the data table to fill out a grid or whatever. Missing Field Values in the data table will be set to null.

Setup code I used for the database tables:

CREATE TABLE Container
(
    ContainerID Int, 
    ContainerNumber Varchar(50), 
    Title Varchar(50), 
    [Date] Date
)

INSERT INTO Container
VALUES
(1, '10000', '1st Title', '2017-01-12'),
(2, '10543', '2nd Title', '2017-01-31'),
(3, '10667', '3rd Title', '2017-04-12'),
(4, '12889', '4th Title', '2017-05-23')


CREATE TABLE ContainerCustomFields 
(
    CustomFieldID INT, 
    ContainerID Int, 
    FieldName Varchar(50), 
    FieldContent Varchar(50)
)

INSERT INTO ContainerCustomFields
VALUES
(1,1, 'Colour', 'Blue'),
(2,1, 'Height', '5000'),
(3,1, 'length', '9100'),
(4,4, 'Colour', 'Gray')
0
3/14/2017 5:01:12 PM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow