I would like to write a LINQ query using EF Core that would get results from multiple matching columns in a SQL table based on an array of serverside data that I would provide for matching. SQL query should look/do something like this:
SELECT * FROM (VALUES ('a', 'one'), ('b', 'two'), ('c', 'three')) AS myserverdata (TransactionId, OrderId)
join ReportExecution re ON myserverdata.OrderId = re.OrderId AND myserverdata.TransactionId = re.TransactionId
Is this even possible?
I had few attempts where all end up crashing on generaing a SQL from expression:
using join
var query =
from execution in context.ReportExecutions
join candidate in insertCandidates // serverside array of data I'd like to match in SQL
on new {execution.OrderId, execution.TransactionId} equals new{candidate.OrderId, candidate.TransactionId}
select execution;
return query.ToListAsync();
using Contains and similarly with .Any (this would work for array of strings and then generate WHERE IN (...), but I can't pull it off for matching multiple columns.
var keys = candidates.Select(x => new { x.TransactionId, x.OrderId });
return context.ReportExecutions
.Where(rme => keys.Contains(new { rme.TransactionId, rme.OrderId } ))
.ToListAsync();
Thanks for the feedback.
EF Core supports only Contains with local collection, but there is workaround. You can use my function FilterByItems
and rewire query in the following way:
var query =
from execution in context.ReportExecutions
.FilterByItems(insertCandidates, (e, c) => e.OrderId == c.OrderId && e.TransactionId == c.TransactionId, true)
select execution;
Related
I am trying to implement an SQL query using LINQ (and EF Core 2.2.2), but I am stuck.
The SQL query is:
SELECT DISTINCT([client].[Id]), [client].[Name]
FROM [Clients] AS [client]
LEFT JOIN [Documents] as [doc] ON [client].[Id] = [doc].[ClientId]
WHERE client.Name LIKE N'%test%' OR
doc.Name LIKE N'%test%' OR
ORDER BY client.Id DESC
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY
and I've started writing the query using LINQ:
from client in context.Set<Client>()
from doc in client.Documents.DefaultIfEmpty()
where client.Name.Contains("test")
or doc.Name.Contains("test")
group client by client.Id into c // this should act as 'distinct'
select c.First()
But now I am stuck with how to add ORDER BY, OFFSET and FETCH.
I tried to write something like this:
(
from client in context.Set<Client>()
from doc in client.Documents.DefaultIfEmpty()
where client.Name.Contains("test")
or doc.Name.Contains("test")
group client by client.Id into c // this should act as 'distinct'
select c.First()
)
.OrderByDescending(c => c.Id) // this is still working on IQueryable
.Skip(10)
.Take(10)
.ToListAsync();
And it returned the correct results, but when I looked at the SQL profiler, the query didn't include ordering and skipping at all (it was done in the application layer, on the entire set returned by the query).
Is there a way to do this using LINQ, or I need to use plain SQL in my codebase?
Distinct is not possible with query syntax, so just combine them:
var query =
from client in _clientService.Query()
from doc in client.Documents.DefaultIfEmpty()
where client.Name.Contains("test") || doc.Name.Contains("test")
select new { client.Id, client.Name };
var result = await query
.Distinct()
.OrderBy(x => x.Id)
.Skip(10)
.Take(10)
.ToListAsync();
friends we are converting sql query to linq query, this conversion is involving two tables as follows
select * from Tbl_Vulpith_Registration where Profile_Pic is not null and MemId IN(select MemId from Tbl_List_Services)
here Tbl_Vulpith_Registration and Tbl_List_Services both are the tables
memId is the common column in the both the tables.
following the what we tried to convert the above sql query to linq
var reglist= objentity.Tbl_Vulpith_Registration.Select(a => a).Where(a => a.Profile_Pic != null);
// var res= reglist.Where(a=>a.Tbl_List_Services)
var listmemsmemIds = objentity.Tbl_List_Services.Select(b => b.MemId).ToList();
var finalist = reglist.Select(b => b).Where(c => c.MemId.Contains(listmemsmemIds));
We've tried multiple ways to convert but no luck.
You need to do the exact opposite:
.Where(c => listmemsmemIds.Contains(c.MemId))
Use the below code which join two tables and fetch the output (internally, it will create only one query)
var reglist = objentity.Tbl_Vulpith_Registration.Join(objentity.Tbl_List_Services,
o => o.MemId, i => i.MemId, (o, i) => o).Where(o => o.Profile_Pic != null).ToList()
The above code correlates the tables based on matching MemId and return the object of entity type of Tbl_Vulpith_Registration.
If you want to read about Join method, check this link.
Because of a poor design on our database I have to write a complex query to get my data.
I need to get all valid data from a table_1. In other works I need to get each valid row of my table_1. I don't have a simple valid or invalid column in my table_1. This information is stored in a table_2. Table_2 contains all invalid row with the error message.
Let say the data I need to retrieve are orders. For my example notice that OrderNo is the number of an order. So I can have multiple line, version, of this order in table_1 and I can also have multiple line of error on this order in table_2. So I will also have to use a version number.
I already tried this:
table_1.Where(y => (y.OrderNo == "1234"));
table_2.Where(y => (y.OrderNo == "1234")).Select(y => y.Version).Distinct();
And I think I need to do something like this:
var errorList = table_2.Where(y => (y.OrderNo == "1234")).Select(y => y.Version).Distinct();
table_1.Where(y => (y.OrderNo == "1234" && y.Version.NOT_IN(erriList)));
Could you help me?
I suppose you are searching for Contains function with ! symbol (logical negation operator). Like this:
var errorList = table_2.Where(y => y.OrderNo == "1234")
.Select(y => y.Version);
var res = table_1.Where(y => y.OrderNo == "1234"
//here you get only rows that doesn't represent in errorList
&& !errorList.Contains(y.Version));
to get data from a table but not if in another table
This is called antijoin. While you can use Contains and Any based approaches presented in the other answers, usually you'll get the best performance by using the classic SQL approach - LEFT OUTER JOIN combined with checking the right side for NULL.
Which in LINQ looks like this:
var query =
from t1 in table_1
//where t1.OrderNo == "1234"
join t2 in table_2 on t1.OrderNo equals t2.OrderNo into t2group
from t2 in t2group.DefaultIfEmpty()
where t2 == null
select t1;
Actually when you use OrderNo filter, most probably there will not be a noticeable speed difference between this and other queries. The main benefit of the above would be if you remove that filter, although many nowadays SQL query optimizers are able to derive one and the same execution plan regardless of whether the query uses JOIN / IN / EXISTS constructs.
How about this:
var validRows = table1
.Where(t1 => !table2
.Any(t2 => t1.OrderNo == t2.OrderNo &&
t1.Version == t2.Version));
Note that this is far more efficient in SQL unless you're using something fancy that translates the expression to SQL.
What I have is a string of comma separated IDs that I'm receiving from a query string (e.g. 23,51,6,87,29). Alternately, that string could just say "all".
In my Linq query I need a way to say (in pseudo code):
from l in List<>
where l.Id = all_of_the_ids_in_csv
&& other conditions
select new {...}
I'm just not sure how to go about doing that. I'm not even sure what to google to get me going in the right direction. Any pointing in the right direction would be extremely helpful.
I would suggest to split your query in 2 - first part will select by ID, and the select one will select other conditions.
First of all: check if query string contains numbers, or is just all:
var IEnumerable<ListItemType> query = sourceList;
if(queryStringValue != "All")
{
var ids = queryStringValue.Split(new[] { ',' })
.Select(x => int.Parse(x)) // remove that line id item.Id is a string
.ToArray();
query = query.Where(item => ids.Contains(item.Id));
}
from l in query
// other conditions
select new {...}
Because LINQ queries have deffered execution you can build queries like that without performance drawback. Query won't be executed until you ask for results (by ToList call or enumeration).
If you really want it with just one LINQ query:
var idArray = all_of_the_ids_in_csv.Split(',');
from l in List<>
where (all_of_the_ids_in_csv == "All" || idArray.Contains(l.Id))
&& other conditions
select new {...}
The trick is using string.Split
var ids = string.split(rawIdString, ",").ToList();
var objects = ids.Where(id=> /*filter id here */).Select(id=>new { /* id will be the single id from the csv */ });
// at this point objects will be an IEnumerable<T> where T is whatever type you created in the new statement above
I want to filter my LINQ query based on an included table but am having some trouble.
Here is the original statement, which works:
return
this.ObjectContext.People.
Include("Careers").
Include("Careers.Titles").
Include("Careers.Titles.Salaries");
Now I'm trying to filter on Careers using projected filtering but am having trouble. It compiles but it leaves out the Titles and Salaries tables, which causes runtime errors, and I can't seem to add those tables back in:
var query1 = (
from c in
this.ObjectContext.People.
Include("Careers").
Include("Careers.Titles").
Include("Careers.Titles.Salaries")
select new
{
c,
Careers = from Careers in c.Careers
where Careers.IsActive == true
select Careers
});
var query = query1.AsEnumerable().Select(m => m.c);
return query.AsQueryable();
How can I include the titles and salaries tables in the filtered query?
You can simplify your query considerably, which should resolve your issue. I'm assuming that you want all people with at least 1 active career:
var query =
from c in
this.ObjectContext.People.
Include("Careers").
Include("Careers.Titles").
Include("Careers.Titles.Salaries")
where c.Careers.Any(c => c.IsActive);
return query;
I would try something like,
var query = from p in ObjectContext.People
join c in ObjectContext.Careers on p equals c.Person
where c.IsActive
select p;