linq to entities, a where in where clause? (inner where) - c#

I have a table with a one to many mapping to a table that has a many to many mapping to another table. I'd like to do the following:
var results = context.main_link_table
.Where(l => l.some_table.RandomProperty == "myValue" &&
l.some_table.many_to_many_table
.Where(m => m.RandomProperty == "myValue"));
How can I achieve this? The first part will work but when trying it without the 'inner WHERE', I can't access the many_to_many_table's properties, but the "inner where" obviously won't compile. I basically want to achieve something like the following SQL query:
SELECT * from main_link_table
INNER JOIN some_table AS t1 ON t1.association = main_link_table.association
INNER JOIN many_to_many_table AS t2 ON t2.association = some_table.association
WHERE t1.RandomProperty = 'MyValue' AND t2.RandomProperty = 'MyValue'
It's seemingly simple but I can't find a way to achieve it in one single line of linq - using multiple lines to achieve the desired effect returns too much results and I end up having to loop through them. I also tried stuff like:
var results = main_link_tbl.Include("some_table.many_to_many_table")
.Where(l => l.some_table.many_to_many_table.<property>
== "MyValue")
But at this point I can't select a property of many_to_many_table unless I add a FirstOrDefault(), which nullifies the effect since it won't search through all the records.
What did work, but requires multiple lines of code and in the background returns too many results in the SQL query built by the linq-to-entities framework:
var results = db.main_link_table.Include("some_table")
.Include("some_table.many_to_many_table")
.Where(s => s.some_table.RandomProperty
== "myValue")
.Select(s => s.some_table);
foreach(var result in results) {
var match_data = result.Where(s => s.many_to_many_table.RandomProperty
== "myValue");
}
This piece of code will return all rows inside some_table that match the first Where condition and then applies the next Where condition, while I obviously only need a single row where the many_to_many_table.RandomProperty equals myValue.

It should work if you change the inner Where to Any:
var results = context.main_link_table
.Where(l => l.some_table.RandomProperty == "myValue" &&
l.some_table.many_to_many_table
.Any(m => m.RandomProperty == "myValue"));

If you want to do a join, why don't you just do a join?
var query = from main in context.MainLinks
join t1 in context.Some on main.Association equals t1.Association
where t1.RandomProperty == "MyValue"
join t2 in context.ManyToMany on t1.Association equals t2.Association
where t2.RandomProperty == "MyValue"
select new { main, t1, t2 };
That should achieve exactly what your SQL does...

from link in db.main_link_table
join s in db.some_table on link.association1 = s.association
join m in db.many_to_many_table on link.association2 = m.association
where s.X = 'MyValue' AND m.Y = 'MyValue'
select m; // or s or link or both 3 as you want

Related

How to improve performance when joining List and Linq object

I have one list, read from file:
var lsData = ReadExcelFile<CustomerEntity>(path);
And one Object (loaded into memory):
lsCustomer = await CustomerService.GetAll()
.Where(c => c.isDeleted == null || !c.isDeleted.Value)
.OrderBy(c=> c.Code)
.ToListAsync();
And the join command:
var lsDuplicateEmail =
(from imp in lsData
join cust in lsCustomer
on ImportHelpers.GetPerfectStringWithoutSpace(imp.Email) equals ImportHelpers.GetPerfectStringWithoutSpace(cust.Email)
into gjoin
from g in gjoin.DefaultIfEmpty()
select new
{
ImportItem = imp,
CustomerItem = g,
}
into result
where !string.IsNullOrEmpty(result.ImportItem.Email) && result.CustomerItem != null
&& !ImportHelpers.CompareString(result.ImportItem.Code, result.CustomerItem.Code)
select result);
var lsDuplicateEmailInSystem = lsDuplicateEmail.Select(c => c.ImportItem.Code).Distinct().ToList();
I perform test with lsData list about 2000 records, lsCustomer about 200k records.
The Customer Email field is not indexed in the DB.
The join command executes with about 10s (even though the result is 0 records), too slow.
I've looked around and can't seem to index the email field in lsCustomer. I know the reason for the slowness is because the complexity is O(n*m).
Is there any way to improve performance?
Try the following code. Instead of GroupJoin, which is not needed here I have used Join. Also moved filters up in query.
var lsDuplicateEmail =
from imp in lsData
where !string.IsNullOrEmpty(imp.Email)
join cust in lsCustomer
on ImportHelpers.GetPerfectStringWithoutSpace(imp.Email) equals ImportHelpers.GetPerfectStringWithoutSpace(cust.Email)
where !ImportHelpers.CompareString(imp.Code, cust.Code)
select new
{
ImportItem = imp,
CustomerItem = cust,
};
Also show GetPerfectStringWithoutSpace implementation, maybe it is slow.
Another possible solution is to swap lsData and lsCustomer in query, maybe lookup search is not so fast.

Linq entity look ahead to value in next row

When querying my entity framework DB, I am trying to match a condition based on the values from 2 rows.
My current query looks like this:
var query = context.table.where(x => x.row > 2);
This all works fine.
What I now want to achieve is to query the table based on the value in current and next row eg:
var query = context.table.where(x => x.row > 2 && x.row[next row up in DB] < 2);
Can this be done.
I know I can achieve this in code, but can it be done in a single query using LINQ and entity?
Here is an example of how I would do this with SQL:
SELECT *
FROM t_Table p
INNER JOIN t_Table f
ON (p.id + 1) = f.id
WHERE p.column = whatever
AND f.column = whatever2
Translating your sample SQL into LINQ to SQL:
var ans = from p in t_table
from f in t_table
where (p.id+1) == f.id && p.column == whatever && f.column == whatever2
select new { p, f };
This does not appear to generate an inner join in SQL but rather a cross-join, but I assume the SQL engine will handle it appropriately. Note that LINQ can only do equi-joins.
I didn't realize you can do (some) expressions in LINQ joins as long as equal is the primary operator, and this generates a sub-select and an inner join, which seems quite a bit faster:
var ans = from p in t_table
where p.column == whatever
let pidplus1 = p.id+1
join f in t_table on pidplus1 equals f.id
where f.column == whatever2
select new { p, f };

join not returning all elements in left table C# lambda

I have 2 tables, the left table has data like this:
I do a left join with another table with the following expression:
var result = posicion.Join(fact,
p => p.Cod_articulo,
f => f.Cod_articulo,
(p, f) => new { p.Posicion, p.Cant_historico,
p.Cod_articulo, f.Cantidad_facturada });
The problem is that the results don't include some items from the left table as seen below:
As you can see in the result there is no data for position 3, 6 etc. what would be missing from my join?
You need to do group join (that is left join in Linq). It's better to do with query syntax:
from p in posicion
join f in fact
on p.Cod_articulo equals f.Cod_articulo into g // GroupJoin
from pf in g.DefaultIfEmpty()
select new {
p.Posicion,
p.Cant_historico,
p.Cod_articulo,
Cantidad_facturada = (pf == null) ? null : pf.Cantidad_facturada
}
This query selects all facts corresponding to posicion p into group g. Then from each group we select results, even if there is no corresponding facts for current posicion (that is DefaultIfEmpty case).
Lambda syntax will be much less readable:
posicion.GroupJoin(fact,
p => p.Cod_articulo,
f => f.Cod_articulo,
(p, g) => new { p, g })
.SelectMany(x => x.g.DefaultIfEmpty(), (x, pf) => new {
x.p.Posicion,
x.p.Cant_historico,
x.p.Cod_articulo,
Cantidad_facturada = (pf == null) ? null : pf.Cantidad_facturada
});
Consider also reading this MSDN article: How to: Perform Left Outer Joins
what would be missing from my join?
Presumably there are no entries in fact which have the corresponding Cod_articulo values (e.g. 60155 for posicion 3). Join in LINQ represents an inner join, where there has to be an entry in both sources in order to create an appropriate result.
If you want a left join, you'd typically use GroupJoin, so that each element on the "left" side ends up matching a group of entries from the "right" side, where that group may be empty.

How do I create multiple joins using LINQ extension methods?

I'm having trouble using LINQ method calls with multiple joins. I'm trying to do something like this:
if (!isDepSelect)
{
query = (from Items in db.DEPARTMENTs
select Items);
}
else
{
query = (from Items in db.DEPARTMENTs
from gDept in db.DEPT_PROFILE
from wAccess in db.WEB_ACCESS
where Items.DEPT_CODE == gDept.DEPT_CODE && gDept.USER_ID == wAccess.USER_ID && wAccess.EMP_ID == id
select Items);
}
I had done this:
IQueryable<DEPARTMENT> query = db.DEPARTMENTs;
if (isDepSelect)
{
query = query.Join(db.DEPT_PROFILE,depts => depts.DEPT_CODE,prof => prof.DEPT_CODE,(depts, prof) => depts);
}
But now I don't know how to add the JOIN of DEPT_PROFILE table with the WEB_ACCESS table and the condition of the EMP_ID = id.
The reason I'm doing this is that the isDepSelect boolean is not the only condition that this query will change its relations and I need someway to add this relations without repeating my LINQ for each of my conditions.
Thank you for your time.
Try with,
List<DEPARTMENTs> list = db.DEPARTMENTs.Join(db.DEPT_PROFILE, dept => dept.DEPT_CODE, prof => prof.DEPT_CODE, (dept,prof) => new {dept, prof})
.Join(Wdb.WEB_ACCESS, depts => depts.prof.USER_ID,web => web.USER_ID,(depts,web) => new { depts, web})
.Where(result => result.web.EMP_ID== id).Select(s => s.depts.dept).ToList<DEPARTMENTs>();
If you have your associations setup, you can do this without any joins in your code at all:
query = db.DEPARTMENTs
.Any(item => item.DEPT_PROFILEs
.Any(gDept => gDept.WEB_ACCESSs
.Any(wAccess => wAccess.EMP_ID == id)));
Of course this is assuming a 1-m relationship between each of the objects in the graph. You can eliminate some of the Any methods if there are 1-0..1 relationships in the graph as necessary.
you should use the equals operator...
query = from Items in db.DEPARTMENTs
from gDept in db.DEPT_PROFILE
join wAccess in db.WEB_ACCESS on
gDept.DEPT_CODE equals Items.DEPT_CODE
select Items;
thats just a snippet of your example query, but you can see how i am using the join operator to introduce a 2nd table and the equals operator to declare the joining columns.
This should work:
query = (from Items in db.DEPARTMENTs
join gDept in db.DEPT_PROFILE
on Items.DEPT_CODE equals gDept.DEPT_CODE
join wAccess in db.WEB_ACCESS
on gDept.USER_ID equals wAccess.USER_ID
where wAccess.EMP_ID == id
select Items);

using "greater than or equal" operator in linq join operation [duplicate]

I had tried to join two table conditionally but it is giving me syntax error. I tried to find solution in the net but i cannot find how to do conditional join with condition. The only other alternative is to get the value first from one table and make a query again.
I just want to confirm if there is any other way to do conditional join with linq.
Here is my code, I am trying to find all position that is equal or lower than me. Basically I want to get my peers and subordinates.
from e in entity.M_Employee
join p in entity.M_Position on e.PostionId >= p.PositionId
select p;
You can't do that with a LINQ joins - LINQ only supports equijoins. However, you can do this:
var query = from e in entity.M_Employee
from p in entity.M_Position
where e.PostionId >= p.PositionId
select p;
Or a slightly alternative but equivalent approach:
var query = entity.M_Employee
.SelectMany(e => entity.M_Position
.Where(p => e.PostionId >= p.PositionId));
Following:
from e in entity.M_Employee
from p in entity.M_Position.Where(p => e.PostionId >= p.PositionId)
select p;
will produce exactly the same SQL you are after (INNER JOIN Position P ON E..PostionId >= P.PositionId).
var currentDetails = from c in customers
group c by new { c.Name, c.Authed } into g
where g.Key.Authed == "True"
select g.OrderByDescending(t => t.EffectiveDate).First();
var currentAndUnauthorised = (from c in customers
join cd in currentDetails
on c.Name equals cd.Name
where c.EffectiveDate >= cd.EffectiveDate
select c).OrderBy(o => o.CoverId).ThenBy(o => o.EffectiveDate);
If you have a table of historic detail changes including authorisation status and effective date. The first query finds each customers current details and the second query adds all subsequent unauthorised detail changes in the table.
Hope this is helpful as it took me some time and help to get too.

Categories

Resources