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
Related
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;
I have a 'complex' linq query I would like to improve and to understand.
(from x in tblOrder
orderby x.OrderNo
// where x.Filename is most recent filename for this order
group x by new { x.OrderNo, x.Color } into groupedByColorCode
select new
{
OrderNo = groupedByColorCode.Key.OrderNo,
ProductRef = groupedByColorCode.FirstOrDefault().ProductRef,
Color = groupedByColorCode.Key.Color,
Packing = groupedByColorCode.FirstOrDefault().Packing,
TotalQuantity = groupedByColorCode.Sum(bcc => bcc.OriQty).ToString()
}
x is an Order. I also would like to filter by Filename. Filename is a variable from tblOrder. Actually I would like to keep and keep only the orders from the most recent file.
What 'where' clause should I add to my linq query to be able to filter these last file name.
Thank you
First it's better to use orderby in the end of the query, because sorting will work quicker on the smaller set of data.
Second you should use where in the top of query, it will make smaller your set before grouping and sorting (set it after from line)
At last grouping creates dictionary with Key = new { x.OrderNo, x.Color } (in this keys) and Value = IEnumerable, and then groupedByColorCode becomes IEnumerabler of {Key, Value}. So it should stand in the end before orederby
there is MaxBy() or MinBy() if you need max or min by some criteria
I know that we can use the Contains method in generating a where clause in a linq query like this:
List<long> objectIDs = new List<long>() { 1, 1, 2 };
var objects = dbcontext.Where(o => objectIDs.Contains(o.ID))
.Select(o => o).ToList();
My question is, how will I be able to select the matching object twice if the ID occurs twice in the where condition?
It sounds like you want to select a separate copy of the object for each match.
Select() can only return exactly one object; you need SelectMany():
list.SelectMany(p => Enumerable.Repeat(p, objectIDs.Count(id => id == p.ID)))
You could also do this faster using a join.
I'm having trouble coming up with an efficient LINQ-to-SQL query. I am attempting to do something like this:
from x in Items
select new
{
Name = x.Name
TypeARelated = from r in x.Related
where r.Type == "A"
select r
}
As you might expect, it produces a single query from the "Items" table, with a left join on the "Related" table. Now if I add another few similar lines...
from x in Items
select new
{
Name = x.Name
TypeARelated = from r in x.Related
where r.Type == "A"
select r,
TypeBRelated = from r in x.Related
where r.Type == "B"
select r
}
The result is that a similar query to the first attempt is run, followed by an individual query to the "Related" table for each record in "Items". Is there a way to wrap this all up in a single query? What would be the cause of this? Thanks in advance for any help you can provide.
The above query if written directly in SQL would be written like so (pseudo-code):
SELECT
X.NAME AS NAME,
(CASE R.TYPE WHEN A THEN R ELSE NULL) AS TypeARelated,
(CASE R.TYPE WHEN B THEN R ELSE NULL) AS TypeBRelated
FROM Items AS X
JOIN Related AS R ON <some field>
However, linq-to-sql is not as efficient, from your explanation, it does one join, then goes to individually compare each record. A better way would be to use two linq queries similar to your first example, which would generate two SQL queries. Then use the result of the two linq queries and join them, which would not generate any SQL statement. This method would limit the number of queries executed in SQL to 2.
If the number of conditions i.e. r.Type == "A" etc., are going to increase over time, or different conditions are going to be added, you're better off using a stored procedure, which would be one SQL query at all times.
Hasanain
You can use eager loading to do a single join on the server to see if that helps. Give this a try.
using (MyDataContext context = new MyDataContext())
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Item>(i => i.Related);
context.LoadOptions = options;
// Do your query now.
}
How to write LINQ statement that returns ROWNUMBER of element with id == something?
There is no direct way to do this that I'm aware of. You'd have to pull the whole query down to the client, and the from there you could project in the row numbers. As an alternative, you could write a stored procedure that uses ROW_NUMBER, and then hit that proc from Linq to SQL.
In your case, the only way you're going to be able to do this would be client side. Keep in mind that the following statement is NOT going to do this at the server, but will pull down your whole table and get the index at the client...
using (var dc = new DataClasses1DataContext())
{
var result = dc.Users
.AsEnumerable() // select all users from the database and bring them back to the client
.Select((user, index) => new // project in the index
{
user.Username,
index
})
.Where(user => user.Username == "sivey"); // filter for your specific record
foreach (var item in result)
{
Console.WriteLine(string.Format("{0}:{1}", item.index, item.Username));
}
}
You should be able to use the Skip and Take extension methods to accomplish this.
For example, if you want row 10:
from c in customers
where c.Region == "somewhere"
orderby c.CustomerName
select new {c.CustomerID, c.CustomerName}
.Skip(9).Take(1);
How To Project a Line Number Into Linq Query Results
How To Project a Line Number Into Linq Query Results