Using LINQ to get all results where ID in dictionary - c#

i have something like this now, which only searches for 1 LabID
List<Expense> lstExpenses = (from l in ctx.Expenses
where l.datLab.Lab_ID == labID
select l).ToList<Expense>();
how do i return all results where Lab_ID is in a dictionary i have:
the followin functions returns a dictionary value: report.GetLabChildren(labID, "All"); is in <short>,<string> format. i only want to search on the <short>

If you want to do the filtering in linq to objects:
var query = ctx.Expenses.AsEnumerable()
.Where(expense => dictionary.ContainsKey(espense.Lab_Id));
If you want something that can be translated to the database:
var ids = dictionary.Keys.ToList();
var query = ctx.Expenses
.Where(expense => ids.Contains(expense.Lab_Id));
Note this will only work if you have less than a few thousand keys in your dictionary, as it is translated to a SQL IN clause. If you have more than that either upload them to a temporary table and join the two tables, or pull the entire Expenses table and use the linq to objects version.

List<Expense> lstExpenses = (from l in ctx.Expenses
where report.GetLabChildren(labID, "All").Keys.Contains(i.Lab_ID)
select l).ToList<Expense>();

I'm not 100% sure what you're asking about, but looks like you're looking for ContainsKey method:
var values = report.GetLabChildren(labID, "All");
List<Expense> lstExpenses = (from l in ctx.Expenses
where values.ContainsKey(l.datLab.Lab_ID)
select l).ToList<Expense>();
I'm not sure, but there is a chance that it won't work with LINQ to Entities. Use following if it happens:
List<Expense> lstExpenses = (from l in ctx.Expenses
where values.Keys.Contains(l.datLab.Lab_ID)
select l).ToList<Expense>();

Related

LINQ Projected Filtering C#

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;

When does this LINQ query go to the database?

I'm concerned that this LINQ call actually makes two trips to the database (once for Contains, once for ToList), when all I really want is the SQL-equivalent of a nested select statement:
var query1 = from y in e.cities where y.zip=12345 select y.Id;
var query2 = from x in e.users where query1.Contains(x.cityId) select x;
List<users> result = query2.ToList();
The point: If this is making a trip to the database twice, how do I avoid that? How can I have a nested select statement like this that will just execute as one query one time? Query1 will only ever return 1 or 0 rows. There must be a better way than using "Contains".
Since query1 and query2 are both IQueryable there is only one trip to the database - when you call query2.ToList()
You could combine the queries using a join since you are looking for related information and the relationship is that the user's city id is the same as the city you are restricting to:
var result = (from x in e.users
join y in e.cities
on x.cityId equals y.Id
where y.zip == 12345
select x.Id).ToList();
Above should give you a list of user ids of users that (presumably) live in the zip code 12345.

Use LINQ to convert comma separated strings in a table into a distinct collection of values

I'm working through this MVC3 tutorial and have entered the genre of a film as a comma separated string.
In part 6 we take the genres from the table to populate a drop down list.
I'd like to populate the drop down list with a distinct collection of single genres but I just can't get it to work.
This is what the tutorial suggest as a start point
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
... and this is where I'd got to
var GenreLst = new List<string>();
var GenreQry = (from d in db.Movies
orderby d.Genre
select d.Genre ).Select(s=>s.Split(','))
.Distinct();
GenreLst.AddRange( GenreQry );
Linq2Sql doesn't know s.Split(',') method, so it should throw an exception, you can do this:
var GenreQry = (from d in db.Movies
orderby d.Genre
select d.Genre ).Distinct().ToList();
GenreLst.AddRange( GenreQry.SelectMany(x=>x.Split(',')).Distinct());
about above code:
When calling ToList() in the end of query, your data will be fetched and your query in fact is list,
in second part, SelectMany flats separated strings as a IEnumberable of strings.
Edit: Also in first part you can call .AsEnumerable() instead of .ToList() for fetching data, it seems better way.
In case you find the SelectMany syntax a bit confusing, consider the following (which compiles into a select many method call under the covers but I find easier to read):
var GenreQry = (from d in db.Movies.AsEnumerable()
from s in d.Split(',')
select s)
.Distinct()
.OrderBy(s => s);

C# LINQ query (MYSQL EF) - Distinct and Latest Records

I have a table, lets call it Record. Containing:
ID (int) | CustID (int) | Time (datetime) | Data (varchar)
I need the latest (most recent) record for each customer:
SQL
select * from record as i group by i.custid having max(id);
LINQ version 1
dgvLatestDistinctRec.DataSource = from g in ee.Records
group g by g.CustID into grp
select grp.LastOrDefault();
This throws an error:
System.NotSupportedException was unhandled by user code Message=LINQ
to Entities does not recognize the method 'Faizan_Kazi_Utils.Record
LastOrDefault[Record
](System.Collections.Generic.IEnumerable`1[Faizan_Kazi_Utils.Record
])' method, and this method cannot be translated into a store
expression. Source=System.Data.Entity
LINQ version 2
var list = (from g in ee.Records
group g by g.CustID into grp
select grp).ToList();
Record[] list2 = (from grp in list
select grp.LastOrDefault()).ToArray();
dgvLatestDistinctRec.DataSource = list2;
This works, but is inefficient because it loads ALL records from the database into memory and then extracts just the last (most recent member) of each group.
Is there any LINQ solution that approaches the efficiency and readability of the mentioned SQL solution?
Update:
var results = (from rec in Record group rec by rec.CustID into grp
select new
{
CustID = grp.Key,
ID = grp.OrderByDescending(r => r.ID).Select(x => x.ID).FirstOrDefault(),
Data = grp.OrderByDescending(r => r.ID).Select(x => x.Data).FirstOrDefault()
}
);
So I made a test table and wrote a Linq -> SQL Query that will do exactly what you need. Take a look at this and let me know what you think. Only thing to keep in mind if this query is scaled I believe it will run a query to the DB for each and every CustID record after the grouping in the select new. The only way to be sure would be to run SQL Tracer when you run the query for info on that go here .. http://www.foliotek.com/devblog/tuning-sql-server-for-programmers/
Original:
Could you do something like this? from g in ee.Records where g.CustID == (from x in ee.Records where (g.CustID == x.CustID) && (g.ID == x.Max(ID)).Select(r => r.CustID))
That's all pseudo code but hopefully you get the idea.
I'm probably too late to help with your problem, but I had a similar issue and was able to get the desired results with a query like this:
from g in ee.Records
group g by g.CustID into grp
from last in (from custRec in grp where custRec.Id == grp.Max(cr => cr.Id) select custRec)
select last
What if you replace LastOrDefault() with simple Last()?
(Yes, you will have to check your records table isn't empty)
Because I can't see a way how MySQL can return you "Default" group. This is not the thing that can be simply translated to SQL.
I think grp.LastOrDefault(), a C# function, is something that SQL doesn't know about. LINQ turns your query into an SQL query for your db server to understand. You might want to try and create an stored procedure instead, or another way to filter out what your looking for.
The reason your second query works is because the LINQ to SQL returns a list and then you do a LINQ query (to filter out what you need) on a C# list, which implements the IEnumerable/IQueryable interfaces and understands the grp.LastOrDefault().
I had another idea:
// Get a list of all the id's i need by:
// grouping by CustID, and then selecting Max ID from each group.
var distinctLatest = (from x in ee.Records
group x by x.CustID into grp
select grp.Max(g => g.id)).ToArray();
// List<Record> result = new List<Record>();
//now we can retrieve individual records using the ID's retrieved above
// foreach (int i in distinctLatest)
// {
// var res = from g in ee.Records where g.id == i select g;
// var arr = res.ToArray();
// result.Add(res.First());
// }
// alternate version of foreach
dgvLatestDistinctRec.DataSource = from g in ee.Records
join i in distinctLatest
on g.id equals i
select g;

Why does this additional join increase # of queries?

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.
}

Categories

Resources