Remove item from IQueryable from List - c#

I have 2 tables in my database Products and PremiumProducts for an ASP Net application.
I retrieve products with a simple query
IQueryable<Product> query = from p in myDataContext.Products select p;
I have a List of PremiumProducts (which contains the ID of a Product). I am attempting to remove the IDs in the PremiumProducts list from the query above but not sure if there is a simple way or if i need to convert all the PremiumProducts to a Product first? I attempted this
List<PremiumProducts> premiumProducts;
query = from p in query where (premiumProducts.Contains(p.ID)) select p;
but of course this brings back all sorts of casting errors.
Is there a simple way to remove the PremiumProducts from the query above or do i need to convert the PremiumProducts to a Product, store the ID and then attempt to remove the query with these IDs?

Rather than:
query = from p in query where (premiumProducts.Contains(p.ID)) select p;
I would suggest:
query = from p in query where (!premiumProducts.Select(z => z.ID).Contains(p.ID)) select p;
The key differences:
Using ! (since you want to exclude those that are in
premiumProducts).
Using premiumProducts.Select(z => z.ID) to
ensure that the contains is against the ID not the object (just
avoiding your casting issues).

Related

How can I set values to an object returned by Linq to Entities without using an anonymous?

I need to return an IQueryable that will be fetching data from an Entity Framework 6 database.
So far, I have a query like this:
from i in dbContext.Instruments
where i.EquipID.Equals(equip.ID)
join r in dbContext.Readings on i.ID equals r.InstrumentID into rs
from r in rs.DefaultIfEmpty()
group r by i into g
select ...;
I know I could use the following as the select portion, but then anonymous objects are returned:
select new
{
g.Key.ID,
g.Key.Name,
g.Key.InstrumentType,
g.Key.Units,
g.Key.RollOver,
g.Key.LastReading = g.OrderByDescending(x => x.DateOfReading).FirstOrDefault().Value,
g.Key.LastReadingDate = g.OrderByDescending(x => x.DateOfReading).FirstOrDefault().DateOfReading,
}
What's important are the last two properties, where values are set on-the-fly. I need the objects held by g.Key, with the properties LastReading and LastReadingDate set.
Is it possible to have a navigation property that is the result of a (sub) query, instead of an object derived by a foreign key? If so, how could I work that into my query?
Some additional thoughts...
Anonymous objects won't work because I need the actual Entity class objects later on. I can't use new Instrument() because that threw an exception (complex Entity type cannot be constructed in a LINQ to Entities query). I also cannot use something like this answer, because the query is going through Entity, and the method would not translate to an SQL statement.

Dynamic LINQ query nested object

I have a list of objects (say family), and each object contains a list of other non-value type object (say child). I would like to query this list and specify the where clause dynamically (during run-time).
var fselected = from f in families
from c in f.Children
where (f.FamilyAge > 15 && c.Age > 13)
select f;
The closest thing I found that would do that is Dynamic LINQ on NuGet, but beyond the simple where clause on the top level object, I can't find any examples on how to do above statement.
The only solution I can think of is to split into separate where clause for C and for F, run the c query first, then run F query on resultant data set...
don't use strings to let your users create dynamic queries on your database, that will leave you vulnerable to sql injection. Instead, expose nullable parameters to your users
public Family GetFamily(int? familyAge, int? age)
{
var families = GetAllFamilies();
if(familyAge.HasValue)
families = families.Where(x => x.familyAge = familyAge.value);
if(age.HasValue)
families = families.Where(x => x.age = age.value);
return familes.ToList();
}
Update
Despite the problems of injections when using strings to let your users query your db, you can use the Dynamic Linq Library to pass on a string as a query. But I do advise against using this.

Conversion of SQL group by to Entity Framework Lambda expression

I have 3 tables A which has an ID and other fields, B which has an ID, and C has a many to many relation for the ID's in A and B
I made a query that gets the results that I need
select result.*
from
(SELECT max(A.AID) as AID
FROM A, C
where A.AID = C.AID
group by C.BID) as x, A as result
where result.AID = x.AID
and I want to convert it to a lambda expression in entity framework. But currently the query is not efficient enough. How would I make the lamda expression in EF and also make it more efficient?
If you first order your results descending by the ID you want the max of, and then GroupBy that ID in the related table, then Select the First from each grouping you should get the results you need.
var groupedResults = tempContext.A.OrderByDescending(a => a.AID
).GroupBy(group => group.C.AID).Select(group => group.FirstOrDefault());
This should return you an IQueryable of the A entity that you can then continue to modify with additional clauses if needed.

Join vs Navigation property for sub lists in Entity Framework

I have a sql statement like this:
DECLARE #destinations table(destinationId int)
INSERT INTO #destinations
VALUES (414),(416)
SELECT *
FROM GroupOrder grp (NOLOCK)
JOIN DestinationGroupItem destItem (NOLOCK)
ON destItem.GroupOrderId = grp.GroupOrderId
JOIN #destinations dests
ON destItem.DestinationId = dests.destinationId
WHERE OrderId = 5662
I am using entity framework and I am having a hard time getting this query into Linq. (The only reason I wrote the query above was to help me conceptualize what I was looking for.)
I have an IQueryable of GroupOrder entities and a List of integers that are my destinations.
After looking at this I realize that I can probably just do two joins (like my SQL query) and get to what I want.
But it seems a bit odd to do that because a GroupOrder object already has a list of DestinationGroupItem objects on it.
I am a bit confused how to use the Navigation property on the GroupOrder when I have an IQueryable listing of GroupOrders.
Also, if possible, I would like to do this in one trip to the database. (I think I could do a few foreach loops to get this done, but it would not be as efficient as a single IQueryable run to the database.)
NOTE: I prefer fluent linq syntax over the query linq syntax. But beggars can't be choosers so I will take whatever I can get.
If you already have the DestinationGroupItem as a Navigation-property, then you already have your SQL-JOIN equivalent - example. Load the related entities with Include. Use List's Contains extension method to see if the desired DestinationId(s) is(are) hit:
var destinations = new List<int> { 414, 416 };
var query = from order in GroupOrder.Include(o => o.DestinationGroupItem) // this is the join via the navigation property
where order.OrderId == 5662 && destinations.Contain(order.DestinationGroupItem.DestinationId)
select order;
// OR
var query = dataContext.GroupOrder
.Include(o => o.DestinationGroupItem)
.Where(order => order.OrderId == 5662 && destinations.Contain(order.DestinationGroupItem.DestinationId));

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;

Categories

Resources