2

I'm new to linq. How do I load my objects using LINQ from a left join database query (PostGIS).

This is my database query:

SELECT          
                dt.id, 
                dt.type, 
                dta.id as "AttId",
                dta.label,
                dtav.id as "AttValueId",
                dtav.value

FROM            public."dataTypes" dt, 
                public."dataTypeAttributes" dta
LEFT JOIN       public."dataTypeAttributeValues" dtav
ON              dta.id = "dataTypeAttributeId"        
WHERE           dt.id = dta."dataTypeId"
ORDER BY        dt.type, dta.label, dtav.value

And here is example output:

enter image description here

I have 3 entities:

public class TypeData
{
    public int ID { get; set; }
    public string Type { get; set; }
    public TypeDataAttribute[] Attributes { get; set; }

}
public class TypeDataAttribute
{
    public int ID { get; set; }
    public string Label { get; set; }
    public TypeDataAttributeValue[] Values { get; set; }
}

public class TypeDataAttributeValue
{
    public int ID { get; set; }
    public string Value { get; set; }
}

Update

Here is where I get stuck:

...
using (NpgsqlDataReader reader = command.ExecuteReader())
{

    if (reader.HasRows)
    {
        IEnumerable<TypeData> typeData = reader.Cast<System.Data.Common.DbDataRecord>()
            .GroupJoin( //<--stuck here.
    }
...

SOLUTION:

using AmyB's answer, this is what filled my objects:

using (NpgsqlDataReader reader = command.ExecuteReader())
{

    if (reader.HasRows)
    {

        var groups = reader.Cast<System.Data.Common.DbDataRecord>()
            .GroupBy(dr => new { ID = (int)dr["id"], AttID = (int)dr["AttId"] })
            .GroupBy(g => g.Key.ID);

        typeDataList = (
            from typeGroup in groups
            let typeRow = typeGroup.First().First()
            select new TypeData()
            {
                ID = (int) typeRow["id"],
                Type = (string) typeRow["type"],
                Attributes = 
                (
                    from attGroup in typeGroup
                    let attRow = attGroup.First()
                    select new TypeDataAttribute()
                    {
                        ID = (int)attRow["AttId"],
                        Label = (string)attRow["label"],
                        PossibleValues =
                        (
                            from row in attGroup
                            where !DBNull.Value.Equals(attRow["AttValueId"])
                            select new TypeDataAttributeValue() { ID = (int)row["AttValueId"], Value = (string)row["value"] }
                        ).ToArray()
                    }
                ).ToArray()
            }
        );
    }
}
Amy B
  • 108,202
  • 21
  • 135
  • 185
capdragon
  • 14,565
  • 24
  • 107
  • 153

4 Answers4

2

So - if I understand right - you have a database query that you are happy with, but you want to take the row-column shaped result and project it into a hierarchically shaped result.


Suppose the results are in a List<Row>

public class Row
{
  public int id {get;set;}
  public string type {get;set;}
  public int attid {get;set;}
  public string label {get;set;}
  public int? attvalueid {get;set;}
  public string value {get;set;}
}

Then you would group twice, and turn each top-level group into a Type, each child-level group into an Attribute and each row into a Value (if the row is not an empty value).

List<Row> queryResult = GetQueryResult();

//make our hierarchies.
var groups = queryResult
  .GroupBy(row => new {row.id, row.attid})
  .GroupBy(g => g.Key.id);

//now shape each level
List<Type> answer =
(
  from typeGroup in groups
  let typeRow = typeGroup.First().First()
  select new Type()
  {
    id = typeRow.id,
    type = typeRow.type,
    Attributes =
    (
      from attGroup in typeGroup
      let attRow = attGroup.First()
      select new Attribute()
      {
        id = attRow.attid,
        label = attRow.label
        Values =
        (
          from row in attRow
          where row.attvalueid.HasValue  //if no values, then we get an empty array
          select new Value() {id = row.attvalueid, value = row.value }
        ).ToArray()
      }
    ).ToArray()
  }
).ToList();
Amy B
  • 108,202
  • 21
  • 135
  • 185
  • I believe this IS what I want.. might take me a bit to digest and try out. Thank you so much!. – capdragon Sep 19 '12 at 19:49
  • Works awesome except one thing... in the inner most nested `Values = ( from row...` The number of values I get is correct, but they are both the same value. For instance I get two "Ascending" Values instead of one "Ascending" and one "Descending". What can I do to fix this? – capdragon Sep 20 '12 at 15:13
  • Nevermind... I was wrong.. it was my mistake. As a token of my appreciation, I rewarded you with over 160 extra points by upvoting every answer you answered correctly on first page of [your profile answers page.](http://stackoverflow.com/users/8155/david-b?tab=answers) – capdragon Sep 20 '12 at 16:52
  • @capdragon - please vote answers based on their individual merits and not as a grand reward for other actions - all those upvotes were detected as "gaming" the system and rep was reversed. If you want to reward a particular answer more - the way to go about that is by adding and awarding a bounty. – Amy B Sep 21 '12 at 03:48
  • (+1...again) Thanks for letting me know. The thing with the bounty it is awarded "out of my control", someone else can take it with a wrong answer but popular support, and you have to wait many days. Or there can be ONE wrong answer and they get the bounty. It's a huge waste IMO. There should be a better way to reward reputation to people for going above and beyond. I suppose I can make it, less obvious ;) – capdragon Sep 21 '12 at 13:13
1

Try GroupJoin sytnax

There is also Join syntax for regular inner join

This link has an example of group join

AD.Net
  • 13,352
  • 2
  • 28
  • 47
1
 var q = (from dt in public."dataTypes"
         // Join the second table to the first, using the IDs of the two tables <-- You may join on different columns from the two tables
         join dta in public."dataTypeAttributes" on
         new { ID = dt.id } equals
         new { ID = dta.id }
         // Join the third table to the first, using the IDs of the two tables
         join dtav in public."dataTypeAttributeId" on
         new { ID = dt.id } equals
         new { ID = dtav.id }
         // Conditional statement
         where dt.id == dta."dataTypeId"
         // Order the results
         orderby dt.type, dta.label, dtav.value
         // Select the values into a new object (datObj is a created class with the below variables)
         select new datObj() {
            ID = dt.id,
            Type = dt.type,
            AttID = dta.id,
            Label = dta.label,
            AttValueID = dtav.id,
            AttValue = dtav.value
         }).ToList()

This will return a List that match your where statement, in the order specified by the orderby statement.

Not exactly what you need, but should give you an example of what you should be doing.

Bob.
  • 3,894
  • 4
  • 44
  • 76
  • @capdragon Its probably a bit different that what you're looking for, but it should give you an idea what to look for. – Bob. Sep 19 '12 at 18:58
0

Yopu should check this: http://codingsense.wordpress.com/2009/03/08/left-join-right-join-using-linq/

ígor
  • 1,144
  • 7
  • 16
  • I've looked at many tutorials and links such as the one you provided but I don't get it. Can you show how this would apply to my code by filling my entities instead of `console.write`? – capdragon Sep 19 '12 at 14:39