Successive SelectMany in Linq Request - c#

I have three tables built with EF code first.
I try to retrieve some information with SelectMany so that I can flatten the query and get only the fields that I need among those three tables.
My tables are presented as follow:
Tables: ProductOptions *-* ProductOptionValues 1-* LanguageProductOptionValue
|ProductOptionID | OVPriceOffset | LanguagesListID
|PriceOffset | OptionValueCategory | ProductOptionValueName
| | ... |
var queryCabColor = _db.ProductOptions
.Where(c => c.ProductOptionTypeID == 18 && c.ProductId == 1)
.SelectMany(z => z.ProductOptionValues, (productOptions, productOptionValues)
=> new
{
productOptions.ProductOptionID,
productOptions.PriceOffset,
productOptionValues.OVPriceOffset,
productOptionValues.OptionValueCategory,
productOptionValues.ProductOptionValuesID,
productOptionValues.Value,
productOptionValues.LanguageProductOptionValue
})
.SelectMany(d => d.LanguageProductOptionValue, (productOptionValues, productOptionValuesTranslation)
=> new
{
productOptionValuesTranslation.LanguagesListID,
productOptionValuesTranslation.ProductOptionValueName
})
.Where(y => y.LanguagesListID == currentCulture);
So far, when I loop in the query I can just retrieve the LanguagesListID and ProductOptionValueName and I can't find a way to get all of the above mentionned fields. Any suggestion?

I think in your case the Linq syntax is more appropriate than explicit SelectMany. Something like this should work:
var queryCabColor =
from productOptions in db.ProductOptions
where productOptions.ProductOptionTypeID == 18 && productOptions.ProductId == 1
from productOptionValues in productOptions.ProductOptionValues
from productOptionValuesTranslation in productOptionValues.LanguageProductOptionValue
where productOptionValuesTranslation.LanguagesListID == currentCulture
select new
{
productOptions.ProductOptionID,
productOptions.PriceOffset,
productOptionValues.OVPriceOffset,
productOptionValues.OptionValueCategory,
productOptionValues.ProductOptionValuesID,
productOptionValues.Value,
productOptionValuesTranslation.LanguagesListID,
productOptionValuesTranslation.ProductOptionValueName
};

Related

Linq to SQL | Get all records where a foreign key doesn't exist

I have 2 tables:
Schools
-------
pk_school_id,
title
and
Business_Hours
--------------
pk_id,
fk_school_id
I want pk_school_id and title from School Table for all pk_school_id that does not exist as fk_school_id in Business_Hours table.
var Schools = (from b in Db.Language_School_Business_Hours
join s in Db.Language_Schools on b.fk_school_id equals s.pk_school_id into lrs
from lr in lrs.DefaultIfEmpty()
select new
{
LeftID = b.fk_school_id,
RightId = ((b.fk_school_id == lr.pk_school_id) ? lr.pk_school_id : 0)
});
try this to achieve your goal, without join, just take elements that are not contained in Business_Hours table
var Schools = Db.Language_Schools
.Where(s => !Db.Language_School_Business_Hours
.Select(b => b.fk_school_id).ToList().Contains(s.pk_school_id))
.Select(x => new
{
x.pk_school_id,
x.school_title
});
I think we can a bit simplify the linq by removing .ToList() after the first .Select(...), removing the last .Select(...). Pls take a look at a below code.
var schools = Db.Language_Schools
.Where(w => !Db.Language_School_Business_Hours
.Select(s => s.fk_school_id)
.Contains(w.pk_school_id))
.ToList();

Optimize or ditch LINQ query?

So I have a LINQ (to SQL) query that pulls information from a database into a grid. There is a function to aggregate the grid data based on the current filter parameters which will sum the amount of recurring "X"'s in the grid data.
For instance, lets assume the grid displays customer vists to a grocery store. The original data may show the follow:
Date | Name | No. Prod | Total $
--------------------------------------------
01/02/13 | Customer A | 4 products | $23.00
01/02/13 | Customer B | 2 products | $3.26
01/02/13 | Customer C | 7 products | $47.42
01/16/13 | Customer A | 3 products | $26.22
Clicking the summation function for the clients column will display the following grid data:
Cnt| Name | Tot. Prod | Total $
--------------------------------------
2 | Customer A | 7 products | $49.22
1 | Customer B | 2 products | $3.26
1 | Customer C | 7 products | $47.42
My problem is that I am doing the summation logic in a LINQ query. I assumed this would be fast...but it is just the opposite. Here is a sample.
Expression<Func<OrdersView, bool>> filter;
filter = m => m.RecordCreated >= fromDate && m.RecordCreated <= toDate && m.DepartmentID == _depID;
var ClientAggOrders = dataContext.OrdersView
.Where(filter)
.GroupBy(m => m.Name)
.Select(gr => new
{
Name = gr.Key,
Count = gr.Where(s => s.ID != null).Count(),
id = gr.Select(s => s.ID),
S1 = gr.Sum(s => s.Tare < s.Gross ? s.Tare : s.Gross),
S2 = gr.Sum(s => s.Tare < s.Gross ? s.Gross : s.Tare),
NetWeight = gr.Sum(s => s.NetWeight),
Price = gr.Sum(s => s.NetPrice)
}
).ToList();
My question is, why is this such bad practice? LINQ allows for these expressions in the SELECT clause, but the time it takes to execute is beyond absurd to the point where I don't see it being beneficial in any real world scenario.
Am I using LINQ wrong and should I just move my logic outside of the query or can this be optimized and done within LINQ properly? Thanks for any advice!
You can use LINQPad to see the SQL that is generated.
Because of the way LINQ to SQL works, id = gr.Select(s => s.ID) causes a subquery to be executed for every group. Remove this, and instead get the ID+Name in your GroupBy: .GroupBy(m => new{m.ID, m.Name})
You should find that the generated SQL will now be a single statement, instead of the main statement plus a statement for each group.
Perform grouping only in memory? Solve your problem?
var ordersView =
dataContext.OrdersView
.Where(m => m.RecordCreated >= fromDate && m.RecordCreated <= toDate && m.DepartmentID == _depID)
.ToList();
var ClientAggOrders = ordersView.GroupBy(m => m.Name).Select(gr => new
{
Name = gr.Key,
Count = gr.Where(s => s.ID != null).Count(),
id = gr.Select(s => s.ID),
S1 = gr.Sum(s => s.Tare < s.Gross ? s.Tare : s.Gross),
S2 = gr.Sum(s => s.Tare < s.Gross ? s.Gross : s.Tare),
NetWeight = gr.Sum(s => s.NetWeight),
Price = gr.Sum(s => s.NetPrice)
}).ToList();

Is there a nicer way to write this linq query?

I have a list of Asset entities, and each Asset has a list of Field entities with two properties each that looks like this
| Index | Value |
| 0 | "hello" |
| 1 | "blah" |
| 2 | null |
and in a loop I get variables
i = 2 and i = 3
and I have a linq query to try and get the following: Assets that have a Field where the Value corresponding to i is null, or there is no Field with an Index i.
For example, if i is 2, it will return the asset that has the table above, because it has a Field where 2 corresponds to null.
And, if i is 3, it should also return the above because there is no Field with Index 3.
This code works:
var assets = (from a in assets where
a.Fields.Any(x => x.Index == i && x.Value == null) select a)
.Union(from a in assets where
a.Fields.All(x => x.Index != i) select a)
.ToList();
This isn't very nice, and I'm wondering is there a way to do it in one statement?
The other answers work, but if you simplify the question it gets even more straightforward:
assets.Where(a => !a.Fields().Any(f => f.Index == i &&
f.Value != null))
.ToList()
You want all the Assets where there isn't a Field with an Index of i and a non-null Value. You don't need to split that into two conditions.
You don't need two queries, just use one with an ||:
assets = assets
.Where(a => a.Fields.Any(f => f.Index == i && f.Value == null)
|| a.Fields.All(f => f.Index != i))
.ToList();
You have a few options, as I see it:
use a more traditional JOIN syntax of LINQ and LEFT JOIN only on the Asset ID and Index. So if the LEFT JOIN returns null (i.e. DefaultIfEmpty()) then that index wasn't found for that Asset. That's case #2. However, if it is NOT null, then you can filter (i.e. where clause) further by checking that Value is null. That's case #1.
You can combine where statement. where a.Fields.Any(...) || a.Fields.All.
You would want to try them both, in my opinion, to see which performs better for your needs. I'd guess the first option would perform much better, but if the data size is small, the second option is definitely much easier.
var assets = assets.where(a => a.Fields.Any(x => x.Index == i && x.Value == null) || a.Fields.All(x => x.Index != i)).ToList();
You can actually shortcut the query a bit in this case
var assets = (
from a in assets
where a.Fields.All(x => x.Index != i || x.Value == null)
select a
).ToList();

join list with linq-to-sql query

I have list of MyObject that looks like this:
public class MyObject{
public int FruitID {get;set;}
public string FruitName {get;set;}
}
List<MyObject> TheList = new List<MyObject>();
This list is populated with a linq-to-sql query. I'm looking to create a join between this list and a table that contains FruitID as its foreign key.
The table HarvestTimes looks like this:
FruitID | HarvestDatetime | RipeFactor
3 | 3/4/2011 | 2
3 | 4/5/2011 | 4
3 | 5/5/2011 | 3
4 | 3/21/2011 | 2
4 | 4/10/2011 | 2
4 | 5/10/2011 | 2
This is what I have so far:
var TheQuery = (from list in TheList
join fruit in MyDC.HarvestTimes on
list.FruitID equals fruit.FruitID
where ....
select new MyObject{... }).ToList();
I'm have some trouble with the Where clause. How do I get only the Fruit where the RipeFactor was always 2. For instance, Fruit 3 has a RipeFactor of 2 but also has 4 and whereas only Fruit4 has only 2s. I tried with Contains but both fruits come up.
Thanks for your suggestions.
Assuming there is a Relationship between the tables HaverstTime and Fruit:
var TheQuery = MyDC.HarvestTimes
.Where(p => TheList.Select(q => q.FruitID).Contains(p.FruitID))
.GroupBy(p => p.Fruit)
.Where(p => p.All(q => q.RipeFactor == 2))
.Select(p => p.Key);
This will create a IEnumerable<Fruit> which I think can be easily converted to MyObject.
Update:
Oops I forgot to add TheList.Select(q => q.FruitID). That's why it didn't compile.
Sorry =)
Update2:
Do the same, considering Ripefactor = 2 and 3
var TheQuery = MyDC.HarvestTimes
.Where(p => TheList.Select(q => q.FruitID).Contains(p.FruitID))
.GroupBy(p => p.Fruit)
.Where(p => p.All(q => q.RipeFactor == 2 || q.RipeFactor == 3))
.Select(p => p.Key);
I think this would work
var fruit = (from list in TheList
join fruit in
(from fr in MyDc.HarvestTimes
group fr by fr.FruitID into fg
where !fg.Any(f => f.RipeFactor != 2)
select fg)
on list.FruitID equals fruit.Key
select new MyObject{... }).ToList();
Update - If you only want to return the distinct list of FruitIDs you need to select fg.Key instead of fg
var fruit = (from list in TheList
join fruit in
(from fr in MyDc.HarvestTimes
group fr by fr.FruitID into fg
where !fg.Any(f => f.RipeFactor != 2)
select fg.Key)
on list.FruitID equals fruit
select new MyObject{... }).ToList();

Remove Duplicate based on column value-linq

i have many to many relationship between employee and group. following linq statement
int[] GroupIDs = {6,7};
var result = from g in umGroups
join empGroup in umEmployeeGroups on g.GroupID equals empGroup.GroupID
where GroupIDs.Contains(g.GroupID)
select new { GrpId = g.GroupID,EmployeeID = empGroup.EmployeeID };
returns groupid and the employeeid. and result is
GrpId | EmployeeID
6 | 18
6 | 20
7 | 19
7 | 20
I need to remove the rows for which the employeeid is repeating e.g. any one of the row with employeeid= 20
Thanks
Okay, if you don't care which employee is removed, you could try something like:
var result = query.GroupBy(x => x.EmployeeId)
.Select(group => group.First());
You haven't specified whether this is in LINQ to SQL, LINQ to Objects or something else... I don't know what the SQL translation of this would be. If you're dealing with a relatively small amount of data you could always force this last bit to be in-process:
var result = query.AsEnumerable()
.GroupBy(x => x.EmployeeId)
.Select(group => group.First());
At that point you could actually use MoreLINQ which has a handy DistinctBy method:
var result = query.AsEnumerable()
.DistinctBy(x => x.EmployeeId);

Categories

Resources