Combining Tables without JOIN using Lambda Expression - c#

Combining Tables without JOIN keyword
var res2 = from u in dtEmp.AsEnumerable()
from v in dtDept.AsEnumerable()
where u.Field<int>("DepartmentID") == v.Field<int>("DepartmentID") &&
u.Field<double>("Salary") > 10000
select new
{
Name = u.Field<string>("Name"),
Department = v.Field<string>("DepartmentName")
};
How to do the same using Lambda Expression without using Join Keyword?

Do you mean you want to switch from SQL syntax to Method Chain syntax? i.e:
var res2 = dtEmp.AsEnumerable()
.SelectMany(u => dtDept.AsEnumerable(), (u, v) => new {u, v})
.Where(#t => u.Field<int>("DepartmentID") == v.Field<int>("DepartmentID") &&
u.Field<double>("Salary") > 10000).Select(#t => new
{
Name = u.Field<string>("Name"),
Department = v.Field<string>("DepartmentName")
});

Related

Lambda syntax. How join by 1 to many keys

i'm trying rewrite query from native sql to linq lambda syntax (not linq query syntax)
LINQ (not work)
var result = _uow.Repository<TableA>().Get().AsNoTracking()
.GroupJoin(
_uow.Repository<TableB>().Get().AsNoTracking(),
a => new { a.TabNotesCodeId, a.TabLabelCodeId },
b => b.ElementNameId
(b, a) => new SubSection
{
SubSectionName = b.CustomValue ?? a.TabLabelCodeId,
SubSectionNote = b.CustomValue ?? a.TabLabelCodeId,
})
.Where(a => a.ResourceId == 1);
SQL
SELECT [SubSectionName] = ISNULL(B.CUSTOMVALUE,A.TABLABELCODEID),
[SubSectionNote] = ISNULL(B.CUSTOMVALUE,A.TABNOTESCODEID)
FROM TableA as A LEFT JOIN
(SELECT CUSTOMVALUE, ELEMENTNAMEID FROM TableB WHERE DISPLAYSETTINGID = 1) as B
ON B.ELEMENTNAMEID IN ( A.TABNOTESCODEID, A.TABLABELCODEID)
WHERE A.RESOURCEID = 1
Q How to rewrite sql ON B.ELEMENTNAMEID IN ( A.TABNOTESCODEID, A.TABLABELCODEID) to lambda syntax
...
a => new { a.TabNotesCodeId, a.TabLabelCodeId },
b => b.ElementNameId
....
(doesn't work)
Normally I would suggest following my SQL conversion rules, but this is sufficiently complex I don't think it would help.
To use query comprehension syntax on a non-equijoin that is a left join, it seems easiest to use lambda syntax to express the join conditions, so I just combined the sub-query with the join conditions:
var ans = from A in TableA
where A.ResourceID == 1
from B in TableB.Where(b => b.DisplaySettingID == 1).Where(b => b.ElementNameID == A.TabNotesCodeID || b.ElementNameID == A.TabLabelCodeID).DefaultIfEmpty()
select new {
SubSectionName = (B.CustomValue ?? A.TabLabelCodeID),
SubSectionNote = (B.CustomValue ?? A.TabNotesCodeID)
};
The lambda equivalent of multiple from clauses to generate a cross join is SelectMany, so converting into lambda syntax:
var ans2 = TableA.Where(a => a.ResourceID == 1)
.SelectMany(a => TableB.Where(b => b.DisplaySettingID == 1).Where(b => b.ElementNameID == a.TabNotesCodeID || b.ElementNameID == a.TabLabelCodeID)
.DefaultIfEmpty(),
(a, b) => new {
SubSectionName = (b.CustomValue ?? a.TabLabelCodeID),
SubSectionNote = (b.CustomValue ?? a.TabNotesCodeID)
}
);
After countless experiments i've found out solution:
_uow.Repository<TableA>().Get().AsNoTracking()
.GroupJoin(
_uow.Repository<TableB>().Get().AsNoTracking().Where(b => b.DisplaySettingId == 1),
a => new { note = a.TabNotesCodeId, label = a.TabLabelCodeId },
b => new { note = b.ElementNameId, label = b.ElementNameId },
(a, b) => new { a,b })
.Where(joinTables => joinTables.a.ResourceId == 1)
.SelectMany(
joinTables => joinTables.b.DefaultIfEmpty(),
(joinTables, b) => new SubSection()
{
LayoutTab = joinTables.a.LayoutTab,
SubSectionName = b.CustomValue ?? joinTables.a.TabLabelCodeId,
SubSectionNote = b.CustomValue ?? joinTables.a.TabNotesCodeId
});

joining two from with an expression<func<>>

Is it possible to join two from based on a local expression variable?
ex;
var query = from t in context.table1
from a in context.anothertable1.Where(x => t.id == a.id)
select new {a,t};
on line 2, the Where clause .Where(x => t.id == a.id) how would you move it into an expression?
I know i can do this;
Expression<Func<anothertable1, bool>> test = x => x.field1 == 1;
and It would work here;
var query = from t in context.table1
from a in context.anothertable1
.Where(x => t.id == a.id)
.Where(test)
select new {a,t};
and everything work and the sql query generated is as expected.
I can't figure out how to do the same with the other where.
EDIT
a more complex example, i anonymized it so it might not compile
var listOfMinMaxtable1 = (from n in context.table1.Where(table1Filter)
group n by n.table1_Number into grp
select new MinMaxtable1()
{
table1_Id_Max = grp.Max(x => x.table1_Id),
table1_Id_Min = grp.Min(x => x.table1_Id),
table1_Number = grp.Key
});
var listtable2 = (from t in context.table2
group t by t.table2_Id into grp
select new table2()
{
table2 = grp,
table2_Id = grp.Key
});
var query = from MinMax in listOfMinMaxtable1
//inner join **reference 1**
from table3 in context.table3
.Where(x => x.table_Number == MinMax.table_Number)
.Where(noticeMasterFilter) //a working expression<func<>>
//inner join **reference 2**
from Lasttable1 in context.table1
.Where(x => x.table_Id == MinMax.table_Id_Max)
//left join **reference 3**
from Firsttable1 in context.table1
.Where(x => x.table_Id == MinMax.table_Id_Min)
.Where(firstNoticeFilter) //a working expression<func<>>
.DefaultIfEmpty()
//left join **reference 4**
from Lasttable2 in listtable2
.Where(x => x.table_Id == MinMax.table_Id_Max)
.SelectMany(x => x.table2)
.Where(x => x.table2_Id == 123)
.OrderByDescending(x => x.table_Id)
.Take(1)
.DefaultIfEmpty()
if you find //left join reference 3 in the code above
that where clause; .Where(x => x.table_Id == MinMax.table_Id_Min)
might be sometime; .Where(x => x.table_Id == MinMax.table_Id_Max)
I could just copy/paste the whole from and change the where clause while adding noop pattern (an expression that return false and this make entity framework remove the whole thing so it doesn't affect the generated sql/result) with an expression on both from
for reference(this is noise to the question), the noop expression that i'm talking about is;
Expression<Func<table1, bool>> includeFrom= x => false;
and would be used like
//left join **reference 3**
from Firsttable1 in context.table1
.Where(x => x.table_Id == MinMax.table_Id_Min)
.Where(firstNoticeFilter) //a working expression<func<>>
.Where(includeFrom) //<--- this line make it a noop if the expression stay false
.DefaultIfEmpty()
but I don't want to do this if it's possible to make a custom expression that would go into the .Where()
Instead of creating an expression based on one type, you can create a combined type and use that for your where expression.
Two Table Combined Type
public class TwoTableDto
{
public Table1 t { get; set; }
public Table2 a { get; set; }
}
Query without expression
var query = (from t in context.table1
from a in context.anothertable1
select new TwoTableDto { t = t, a = a })
.Where(x => x.t.id == x.a.id);
Expression
Expression<Func<TwoTableDto, bool>> expr = x => x.t.id == x.a.id;
Query with expression
var query = (from t in context.table1
from a in context.anothertable1
select new TwoTableDto { t = t, a = a })
.Where(expr);

Converting query to lambda expression with joins and where clause

I have this query and I'm having trouble converting into a lambda expression
SELECT [dbo].[Prospects].[Id]
,[UserId]
,[NewId]
,[dbo].[Prospects].[Status]
FROM [dbo].[Prospects] join [dbo].[User] on [dbo].[User].Id = [dbo]. [Prospects].UserId
where [dbo].[Prospects].NewId = 3 and [dbo].[User].IsActive = 1
This is what i got, but it is not working
var result = Workspace.Prospects.Join
(Workspace.Users, pros => pros.UserId,
use => use.Id, (pros, use)
=> new { Prospect = pros, User = use}).Where
(both => both.User.IsActive == true && both.Prospect.NewId == idVacante)
.OrderBy(both => both.Prospect.Id).AsEnumerable().ToList();
List<Prospect> prospects = result.Cast<Prospect>().ToList();
It's not that hard. you can filter the tables before joining them
var result = Workspace.Prospects.Where(x=> x.NewId == 3)
.Join(Workspace.Users.Where(x => x.IsActive == 1),
p => p.UserId,
u => u.Id,
(p, u) => new { p.Id, p.UserId, p.NewId, p.Status })

SQL query to LINQ conversion with nested select statements

I want to convert the following query to LINQ:
SELECT LV.* FROM LowerVehicles LV
INNER JOIN (Select VSerial,MAX(updatedOn) MaxUpdatedOn from LowerVehicles group by vserial) LVG
ON LV.VSerial = LVG.VSerial AND LV.updatedOn = LVG.MaxUpdatedOn
Not knowing your entities classes, here is an approximation. You can use query syntax or fluent syntax. Sometimes one is preferable over the other, and in the case of joins and grouping I prefer to use query syntax.
QUERY SYNTAX
var query = from LV in LowerVehicles
join LVG in (
from r in LowerVehicles
group r by r.vserial into g
select new {VSerial = g.Key, MaxUpdatedOn = g.Max(t => t.updatedOn)})
on LV.VSerial equals LVG.Vserial
and LV.updatedOn equals LVG.MaxUpdatedOn
select LV;
FLUENT SYNTAX
var lvg = LowerVehicles.GroupBy(t => t.vserial)
.Select(g => new {
VSerial = g.Key,
MaxUpdatedOn = g.Max(t => t.updatedOn)
});
var query = LowerVehicles.Join(
lvg,
a => new { a.VSerial, a.updatedOn },
b => new { b.VSerial, b.MaxUpdatedOn },
(a, b) => new { LV = a, LVG = b}
)
.Select(t=> t.LV);
Something like this?
Something.LowerVehicles
.Join(something.LowerVehicles.Select(y => new { y.VSerial, updatedOn = y.updatedOn.Max() }).GroupBy(z => z.VSerial),
x => new { x.VSerial, x.updatedOn },
lvg => new { lvg.VSerial, lvg.updatedOn },
(x, y) => x)

LINq query throws exception

I am running the following query
var allroles = from l in metaData.Role select l.RoleId;
var personroles = from k in metaData.PersonRole
where k.PersonId == new Guid(Session["user_id"].ToString())
select k.RoleId;
Dictionary<Guid, string> allroleswithnames =
(from l in metaData.Role
select new { l.RoleId, l.Description })
.ToDictionary(u => u.RoleId, u => u.Description);
var avl_roles = from j in allroles.Except(personroles)
select new
{
RoleId = j,
Description = allroleswithnames[new Guid(j.ToString())]
};
clist_avl_roles.DataSource = avl_roles;
clist_avl_roles.DataBind();
The code at code for avl_roles throwing error
Subquery returned more than 1 value. This is not permitted when the subquery
follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Actually there are multiple rows for roleid with same person id. How do I rewrite the query to handle this situation?
var personId = new Guid(Session["user_id"].ToString());
var personRoles = metaData.PersonRole
.Where(pr => pr.PersonId == personId)
.Select(pr => pr.RoleId);
var avl_roles = from r in metaData.Role
where !personRoles.Contains(r.RoleId)
select new { r.RoleId, r.Description };
Or in single query
var avl_roles = from r in metaData.Role
join pr in metaData.PersonRole.Where(x => x.PersonId == personId)
on r.RoleId equals pr.RoleId into g
where !g.Any()
select new { r.RoleId, r.Description };

Categories