LINQ: Fill objects from left join - c#

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:
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()
}
);
}
}

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();

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.

Try GroupJoin sytnax
There is also Join syntax for regular inner join
This link has an example of group join

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

Related

LINQ Compare 2 Tables (Sort of like SQL Except?)

I currently have 2 tables:
Table1, with the following columns:
Id, TypeId, VersionId, AnotherColumn1, AnotherColumn2, AnotherColumn3
Table2, with the following columns:
Id, TypeId, VersionId, DifferentColumn1, DifferentColumn2
The only thing these 2 tables have in common are TypeId and VersionId.
I am trying to get only the TypeId and VersionId from Table1 AS LONG as that specific **Type Id + VersionId combination **is not in Table 2.
I have tried the following:
var result1 = this.Table2
.Select(k => new { TypeId = k.TypeId, VersionId = k.VersionId })
.ToArray() // Trying to first select all possible TypeId + VersionId combinations from Table2
var finalResult = this.Table1
. // This is where I am lost, should I use a `.Except`?, some kind of `.Where`?
This should be possible with grouping a left outer join using DefaultIfEmpty:
var results = context.Table1
.GroupJoin(context.Table2,
t1 => new {t1.TypeId, t1.VersionId},
t2 => new {t2.TypeId, t2.VersionId},
(t1, t2) => new { Values = new {t1.TypeId, t1.VersionId}, Table2s = t2.DefaultIfEmpty().Where(x => x != null) })
.Where(g => !g.Table2s.Any())
.Select(g => g.Values)
.ToList();
This may look a bit complicated but we essentially do an outer join between the tables on the TypeId and VersionId. When fetching the grouped result of that join we have to tell EF to exclude any cases where Table2 would be #null, otherwise we would get a collection with 1 #null element. (There may be a more optimal way to get this to work, but the above does work) This is grouped by the combination of values requested (TypeId and VersionId from Table 1). From there it is just filtering out the results where there are no Table2 records, and selecting the "Key" from the grouping, which is our desired values.
With the help of **LINQ Except** also you can do this
Please find the following details with an example
public class Table1
{
public int Id { get; set; }
public int TypeId { get; set; }
public int VersionId { get; set; }
public string AnotherColumn1 { get; set; }
}
public class Table2
{
public int Id { get; set; }
public int TypeId { get; set; }
public int VersionId { get; set; }
public string DifferentColumn1 { get; set; }
}
private void GetValuesUsingExcept()
{
List<Table1> lstTable1 = new List<Table1>()
{
new Table1{Id=1, TypeId=101, VersionId=201, AnotherColumn1="Test1"},
new Table1{Id=2, TypeId=102, VersionId=202, AnotherColumn1="Test2"},
new Table1{Id=3, TypeId=103, VersionId=203, AnotherColumn1="Test3"}
};
List<Table2> lstTable2 = new List<Table2>()
{
new Table2{Id=1, TypeId=101, VersionId=201, DifferentColumn1="DiffVal1"},
new Table2{Id=2, TypeId=102, VersionId=202, DifferentColumn1="DiffVal2"},
new Table2{Id=4, TypeId=104, VersionId=204, DifferentColumn1="DiffVal3"}
};
var output = lstTable1.Select(s1 => new { s1.TypeId, s1.VersionId }).Except(lstTable2.Select(s2 => new { s2.TypeId, s2.VersionId })).ToList();
}

LINQ Query (Group BY) one column

Consider the following classes
public class DashboardTile
{
public int ID { get; set; }
public int? CategoryID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class DashboardTileBO : DashboardTile
{
public bool IsChecked { get; set; }
public List<DashboardTileBO> DashboardTiles { get; set; }
}
I have list of tiles in which some tiles are child of other.Now I want to show my list of tiles in such a way that if it has childs it gets added to the list.
query I am trying
var allDashBoardTiles = (from a in context.DashboardTiles
group a by a.CategoryID into b
select new BusinessObjects.DashboardTileBO
{
ID = a.ID,
Name = a.Name,
Description = b.Description,
DashboardTiles = b.ToList(),
}).ToList();
var list = context.DashboardUserTiles.Where(a => a.UserID == userId).Select(a => a.DashboardTileID).ToList();
allDashBoardTiles.ForEach(a => a.IsChecked = list.Contains(a.ID));
Now in above query when I use group clause and in select if I use a.ID,a.Name etc it says that it doesnot contain definitionor extension method for it.
Table
You can't access the properties of a directly because GroupBy returns IGrouping<TKey,T>. You can include other columns also in your group by and access them like this:-
(from a in context.DashboardTiles
group a by new { a.CategoryID, a.ID, a.Name } into b
select new BusinessObjects.DashboardTileBO
{
ID = b.Key.ID,
Name = b.Key.Name,
DashboardTiles = b.ToList(),
}).ToList();
Edit:
Also, I guess the property DashboardTiles in DashboardTileBO class should be List<DashboardTile> instead of List<DashboardTileBO>, otherwise we cannot fetch it from DashboardTiles data.

Linq query to return list within a list

Hi I have following two model classes
public class c1
{
public int id { get; set; }
public int ptId { get; set; }
public int bId { get; set; }
public int rId { get; set; }
public IEnumerable<styles> newStruct { get; set; }
}
public class styles
{
public int id { get; set; }
public int bId { get; set; }
public string desc { get; set; }
}
I am trying to write a linq query
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
select new c1
{
pId= c.pId,
id = c.id,
newStruct = new List<styles>
{
new styles
{
id=y.room_id,
desc=y.desc,
}
}
});
return records.ToList();
Problem I am having is that in newStruct is suppose to be List of all the styles but it just returns one style at a time rather than one list.
Please let me know how can it return record where inside it contains list of styles
Thanks
If you want to get a sublist by main list, you should use group by,
You can try this, but I'm not sure it worked. Becasue I couldn't compile it.
var records = (from y in db.main
join c in db.secondary on y.bId equals c.bId
where c.id == id
group c by new
{
c.pId,
c.id
} into gcs
select new c1
{
pId = c.Key.pId,
id = c.Key.id,
newStruct = from g in gcs select new styles { id=g.room_id, desc=g.desc}
});
Is this LINQ to Entities? If so, and the mappings are correct in the edmx, you can try:
var records =
from c in db.secondary
where c.id == id
select new c1
{
pId = c.pId,
id = c.id,
newStruct = c.main.Select(m => new styles { id = m.room_id, desc = m.desc })
};
return records.ToList();

How do I do a multi-join on two SQL Views in an Entity Framework Model?

I have two views in my model.
I basically need to do an INNER JOIN on them based on three columns:
dataSource
ShowID
EpisodeID
The first thing I don't know how to do is add the SQL "AND" operator to the LINQ expression.
The second thing is, I don't know how to SELECT the JOINED table.
Can someone give me a hand?
var query = (from s in db.TVData_VW_ShowList
from z in db.TVData_VW_Schedule
where s.dataSource = z.dataSource
&& s.ShowID = z.ShowID
&& s.EpisodeId = z.EpisodeId select ...
You can use anonymous types to your advantage here, both to join across multiple columns, and to project into a new type containing data from both sides of the join. Here's a working example using Linq to objects:
namespace LinqExample
{
class Program
{
static void Main()
{
var Shows = new List<ShowData> { new ShowData { dataSource = "foo", EpisodeID = "foo", ShowID = "foo", SomeShowProperty = "showFoo" }};
var Schedules = new List<ScheduleData> { new ScheduleData { dataSource = "foo", EpisodeID = "foo", ShowID = "foo", SomeScheduleProperty = "scheduleFoo" } };
var results =
from show in Shows
join schedule in Schedules
on new { show.dataSource, show.ShowID, show.EpisodeID }
equals new { schedule.dataSource, schedule.ShowID, schedule.EpisodeID }
select new { show.SomeShowProperty, schedule.SomeScheduleProperty };
foreach (var result in results)
{
Console.WriteLine(result.SomeShowProperty + result.SomeScheduleProperty); //prints "showFoo scheduleFoo"
}
Console.ReadKey();
}
}
public class ShowData
{
public string dataSource { get; set; }
public string ShowID { get; set; }
public string EpisodeID { get; set; }
public string SomeShowProperty { get; set; }
}
public class ScheduleData
{
public string dataSource { get; set; }
public string ShowID { get; set; }
public string EpisodeID { get; set; }
public string SomeScheduleProperty { get; set; }
}
}
So to join you can use the join keyword then use on to specify the conditions. && (the logical and operator in C#) will be translated to the SQL AND keyword.
Also, in EF they have what are known as "implicit joins" meaning if I have TableA with a foreign key to TableB, call it fKey.
Doing where TableA.fKey == TableB.pKey will cause the provider to put a join there. To select you simply need to do;
select new { prop1 = TableA.Prop1, prop2 = TableB.Prop1 }
this will create a new anonymous which selects values from both tables.
Below is a more complete example of the join syntax. I think it uses all of the things you asked about;
var result = from a in TableA
join b in TableB on a.fKey equals b.pKey && b.Status equals 1
select new { a.Prop1, a.Prop2, b.Prop1 };
First you need to create an auxiliar class that contains the columns of both views, something like:
public class viewItem
{
public int ShowID { get; set; }
public int EpisodeID { get; set; }
public int dataSource { get; set; }
...
}
then your linq query would be:
var query = (from s in db.TVData_VW_ShowList
join z in db.TVData_VW_Schedule
on s.dataSource equals z.dataSource
where s.ShowID == z.ShowID
&& s.EpisodeID == z.EpisodeID
select new viewItem {
ShowID = s.ShowID,
EpisodeID = s.EpisodeID,
dataSource = s.dataSource,
...
}

Get Linq Subquery into a User Defined Type

I have a linq query, which is further having a subquery, I want to store the result of that query into a user defined type, my query is
var val = (from emp in Employees
join dept in Departments
on emp.EmployeeID equals dept.EmployeeID
select new Hello
{
EmployeeID = emp.EmployeeID
Spaces = (from order in Orders
join space in SpaceTypes
on order.OrderSpaceTypeID equals space.OrderSpaceTypeID
where order.EmployeeID == emp.EmployeeID group new { order, space } by new { order.OrderSpaceTypeID, space.SpaceTypeCode } into g
select new
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})
}).ToList();
Definition for my Hello class is
public class Hello
{
public IEnumerable<World> Spaces { get; set; }
public int PassengerTripID { get; set; }
}
Definition for my World class is
public class World
{
public int ID { get; set; }
public string Code { get; set; }
public int Count { get; set; }
}
You are creating anonymous object but you need to specify type name World
select new World
{
ID = g.Key.SpaceTypeID,
Code = g.Key.SpaceTypeCode,
Count = g.Count()
})

Categories