LINQ query - Multiple where with multiple sub queries based on int array - c#

I have a sql query that performs the type of select I'm after:
select * from Products p where p.ProductId in (
select distinct ProductId from ProductFacets
where ProductId in (select ProductId from ProductFacets where FacetTypeId = 1)
and ProductId in (select ProductId from ProductFacets where FacetTypeId = 4)
)
There can be multiple FacetTypeIds passed into this query.
This query is constructed in a method based on a parameter argument of type int[].
public IEnumerable<Product> GetProductsByFacetTypes(string productTypeSysName, int[] facetTypeIds)
I'm trying to work out how to achieve this in LINQ. So far I've come up with something like this:
var products = from p in sc.Products
where p.ProductType.SysName == productTypeSysName
where p.FacetTypes.Any(x => x.FacetTypeId == 1)
where p.FacetTypes.Any(x => x.FacetTypeId == 4)
select p;
This returns the correct result set.
However I'm not sure how I can build this query using the int[] facetTypeIds parameter.
EDIT:
ProductFacets contains the following data:
ProductId, FacetTypeId
1, 1
1, 2
2, 1
2, 3
3, 4
3, 5
4, 1
4, 2
As an example, I'd like to be able to select only Products which have a FacetTypeId of 1 AND 2.
The result set should contain ProductIds 1 and 4

A local collection may be transmitted to the database by calling Contains:
from ft in facetTypes
where facetTypeIds.Contains(ft.FacetTypeId)
select ft;
The local collection is translated into sql parameters. Sql Server has a limit of ~2100 parameters, so beware.
Products that have any of the facets
from p in sc.Products
where p.ProductType.SysName == productTypeSysName
where
(
from ft in p.FacetTypes
where facetTypeIds.Contains(ft.FacetTypeId)
select ft
).Any()
select p;
Products that have all facets.
int facetCount = facetTypeIds.Count();
from p in sc.Products
where p.ProductType.SysName == productTypeSysName
where
(
from ft in p.FacetTypes
where facetTypeIds.Contains(ft.FacetTypeId)
select ft.FacetTypeId
).Distinct().Count() == facetCount
select p;

EDIT: Sorry I misread the question. I would suggest you use a PredicateBuilder and build up the Where clause dynamically if you need all of the types to be present. This would use extension methods directly.
var facetTypeIds = new [] { 1, 4, ... };
var predicate = PredicateBuilder.True<Product>();
foreach (int id in facetTypeIds)
{
int facetId = id; // avoid capturing loop variable
predicate = predicate.And( p => p.FacetTypes.Any( x => x.FacetTypeId == facetId );
}
var products = sc.Products
.Where( p => p.ProductType.SysName == productTypeSysName )
.Where( predicate );
Original (wrong, but left for context):
You want to use Contains. Note also you can use the logical and to replace multiple Where clauses with a single Where clause.
var facetTypeIds = new [] { 1, 4, ... };
var products = from p in sc.Products
where p.ProductType.SysName == productTypeSysName
&& p.FacetTypes.Any( x => facetTypeIds.Contains( x.FacetTypeId ) )
select p;

This is based on tvanfosson's code. I have doubts about the performance of this approach though.
var facetTypeIds = new [] { 1, 4, ... };
var products = from p in sc.Products
where p.ProductType.SysName == productTypeSysName
&& facetTypeIds.All(ft => p.FacetTypes.Any(x => x.FacetTypeId == ft))
select p;

Related

SQL ROW_NUMBER() in LINQ Query Syntax

I have this query which I wish to replace with Linq as query syntax:
select
ii.Id, ii.Name as Supplier,
qi.Price1, qi.Price2, qi.Price3,
case when qi.price1 is null then NULL else ROW_NUMBER() OVER(ORDER BY isNull(qi.price1,1000000) ASC) end AS Price1Order,
case when qi.price2 is null then NULL else ROW_NUMBER() OVER(ORDER BY isNull(qi.price2,1000000) ASC) end AS Price2Order,
case when qi.price3 is null then NULL else ROW_NUMBER() OVER(ORDER BY isNull(qi.price3,1000000) ASC) end AS Price3Order
From dbo.InquiryItems ii
left join dbo.quoteItems qi on ii.Id = qi.QuoteItem_InquiryItem
SQL-Query Result:
Id Supplier Price1 Price2 Price3 Price1Order Price2Order Price3Order
1655 Supplier 2 80.00 NULL 40.00 3 NULL 1
1656 Supplier 4 65.00 30.00 42.00 2 1 2
1662 Supplier 1 15.00 35.00 43.00 1 2 3
1670 Supplier 3 250.00 NULL NULL 4 NULL NULL
In C# I need this query as a IQueryable Object. I must filter the query for different parts (one or more) and then group it by Supplier (IdAccount) and SUM the prices. This prices I must rank.
return colQuoteItem = from vQuote in this.vQuoteItemOverviews
where vQuote.IdConstructionStageId == ConstructionStageId
group vQuote by new
{
vQuote.IdAccount
} into g
select new vQuoteItemOverviewSum
{
Id = g.Max(x => x.Id),
Price1Order = null, //Here I need the ROW_NUMBER like in the SQL-Syntax
Price2Order = null, //Here I need the ROW_NUMBER like in the SQL-Syntax
Price3Order = null, //Here I need the ROW_NUMBER like in the SQL-Syntax
price1 = g.Sum(x => x.price1),
price2 = g.Sum(x => x.price2),
price3 = g.Sum(x => x.price3),
}
;
Would this work?
var qi1 = (from qi in quoteItems orderby qi.price1 select new {qi.QuoteItem_InquiryItem}).AsEnumerable().Select((i, index) => new {i.QuoteItem_InquiryItem, Rank = index + 1});
var qi2 = (from qi in quoteItems orderby qi.price2 select new {qi.QuoteItem_InquiryItem}).AsEnumerable().Select((i, index) => new {i.QuoteItem_InquiryItem, Rank = index + 1});
var qi3 = (from qi in quoteItems orderby qi.price3 select new {qi.QuoteItem_InquiryItem}).AsEnumerable().Select((i, index) => new {i.QuoteItem_InquiryItem, Rank = index + 1});
return colQuoteItem = from vQuote in this.vQuoteItemOverviews.AsEnumerable()
where vQuote.IdConstructionStageId == ConstructionStageId
group vQuote by new
{
vQuote.IdAccount
} into g
select new vQuoteItemOverviewSum
{
Id = g.Max(x => x.Id),
Price1Order = qi1.FirstOrDefault(_ => _.IdConstructionStageId == x.Id)?.Rank, //Here i need the ROW_NUMBER like in the SQL-Syntax
Price2Order = qi2.FirstOrDefault(_ => _.IdConstructionStageId == x.Id)?.Rank, //Here i need the ROW_NUMBER like in the SQL-Syntax
Price3Order = qi3.FirstOrDefault(_ => _.IdConstructionStageId == x.Id)?.Rank, //Here i need the ROW_NUMBER like in the SQL-Syntax
price1 = g.Sum(x => x.price1),
price2 = g.Sum(x => x.price2),
price3 = g.Sum(x => x.price3),
}
;

Linq to Entity Join table with multiple OR conditions

I need to write a Linq-Entity state that can get the below SQL query
SELECT RR.OrderId
FROM dbo.TableOne RR
JOIN dbo.TableTwo M ON RR.OrderedProductId = M.ProductID OR RR.SoldProductId= M.ProductID
WHERE RR.StatusID IN ( 1, 4, 5, 6, 7 )
I am stuck with the below syntax
int[] statusIds = new int[] { 1, 4, 5, 6, 7 };
using (Entities context = new Entities())
{
var query = (from RR in context.TableOne
join M in context.TableTwo on new { RR.OrderedProductId, RR.SoldProductId} equals new { M.ProductID }
where RR.CustomerID == CustomerID
&& statusIds.Any(x => x.Equals(RR.StatusID.Value))
select RR.OrderId).ToArray();
}
this gives me below error
Error 50 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
How can I do a Multiple condition join for a table.
You don't have to use the join syntax. Adding the predicates in a where clause has the same effect and you can add more conditions:
var query = (from RR in context.TableOne
from M in context.TableTwo
where RR.OrderedProductId == M.ProductID
|| RR.SoldProductId == M.ProductID // Your join
where RR.CustomerID == CustomerID
&& statusIds.Any(x => x.Equals(RR.StatusID.Value))
select RR.OrderId).ToArray();
Change your query syntax from using join to using an additional from clause
var query = (from RR in context.TableOne
from M in context.TableTwo.Where(x => x.ProductID == RR.OrderedProductId || x.ProductID == RR.SoldProductId)
where statusIds.Any(x => x.Equals(RR.StatusID.Value))
select RR.OrderId).ToArray();
Multiple Joins :
var query = (from RR in context.TableOne
join M in context.TableTwo on new { oId = RR.OrderedProductId, sId = RR.SoldProductId} equals new { oId = M.ProductID, sId = M.ProductID }
where RR.CustomerID == CustomerID
&& statusIds.Any(x => x.Equals(RR.StatusID.Value))
select RR.OrderId).ToArray();

LINQ to SQL in and not in

What is in and not in equals in LINQ to SQL?
For example
select * from table in ( ...)
and
select * from table not in (..)
What is equal to the above statement in LINQ to SQL?
You use, where <list>.Contains( <item> )
var myProducts = from p in db.Products
where productList.Contains(p.ProductID)
select p;
Or you can have a list predefined as such:
int[] ids = {1, 2, 3};
var query = from item in context.items
where ids.Contains( item.id )
select item;
For the 'NOT' case, just add the '!' operator before the 'Contains' statement.
I'm confused by your question. in and not in operate on fields in the query, yet you're not specifying a field in your example query. So it should be something like:
select * from table where fieldname in ('val1', 'val2')
or
select * from table where fieldname not in (1, 2)
The equivalent of those queries in LINQ to SQL would be something like this:
List<string> validValues = new List<string>() { "val1", "val2"};
var qry = from item in dataContext.TableName
where validValues.Contains(item.FieldName)
select item;
and this:
List<int> validValues = new List<int>() { 1, 2};
var qry = from item in dataContext.TableName
where !validValues.Contains(item.FieldName)
select item;
Please Try This For SQL Not IN
var v = from cs in context.Sal_Customer
join sag in context.Sal_SalesAgreement
on cs.CustomerCode equals sag.CustomerCode
where
!(
from cus in context.Sal_Customer
join
cfc in context.Sal_CollectionFromCustomers
on cus.CustomerCode equals cfc.CustomerCode
where cus.UnitCode == locationCode &&
cus.Status == Helper.Active &&
cfc.CollectionType == Helper.CollectionTypeDRF
select cus.CustomerCode
).Contains(cs.CustomerCode) &&
cs.UnitCode == locationCode &&
cs.Status == customerStatus &&
SqlFunctions.DateDiff("Month", sag.AgreementDate, drfaDate) < 36
select new CustomerDisasterRecoveryDetails
{
CustomerCode = cs.CustomerCode,
CustomerName = cs.CustomerName,
AgreementDate = sag.AgreementDate,
AgreementDuration = SqlFunctions.DateDiff("Month", sag.AgreementDate, drfaDate)
};
Please Try This For SQL IN
context.Sal_PackageOrItemCapacity.Where(c => c.ProjectCode == projectCode && c.Status == Helper.Active && c.CapacityFor.Contains(isForItemOrPackage)).ToList();

LINQ Filter anonymous type based on IEnumerable values within type

I'm using LINQ to SQL like:
var b =
from s in context.data
select new
{
id = s.id,
name = s.name
myEnumerable = s.OneToMany
};
Where myEnumerable is of type IEnumberable<T> and I want to now get a subset of b based upon properties of the individual items of myEnumerable. For example, say <T> has properties Berry and BerryID, I would want to do something like:
b =
from p in b
where //p.myEnumerable.myType.BerryID== 13
select p;
I'm feel like I'm missing something easy...
Since myEnumerable is an IEnumerable you will have to do a where on that.
var filteredData = from p in listOfData
where p.InnerData.Where(b=>b.ID == 13).Count() > 0
select p;
If I understand what you are saying...this is if there is an ID = 13 in the Enumerable at all.
Are you looking to select p if any of the items in p.myEnumerable have BerryID equal to 13?
b = from p in b
where p.myEnumerable.Any(t => t.BerryID == 13)
select p;
Or are you looking to select p if all of the items in p.myEnumerable have BerryID equal to 13?
b = from p in b
where p.myEnumerable.All(t => t.BerryID == 13)
select p;
What exactly is the condition you want the items in p.myEnumerable to fulfill before you select p?
Keep only items with at least one item having BerryID equal to 13 in the collection.
var b = context.data
.Where(s => s.OneToMany.Any(i => i.BerryID == 13))
.Select(s => new { id = s.id, name = s.name, myEnumerable = s.OneToMany });
Keep only items with all item having BerryID equal to 13 in the collection.
var b = context.data
.Where(s => s.OneToMany.All(i => i.BerryID == 13))
.Select(s => new { id = s.id, name = s.name, myEnumerable = s.OneToMany });

Linq to SQL With Left Outer Join and Group By With Sum - How To

I'm trying to transform the SQL Query below into Linq to SQL
select Categorias.IdCategoria, Categorias.Nome, SUM(lancamentos.valor)
from lancamentos
left outer join Categorias on Lancamentos.IdCategoria = Categorias.IdCategoria
where Month(DataLancamento) = 11
and Credito = 1
and Lancamentos.Ocultar = 0
group by Categorias.IdCategoria, Categorias.Nome
This is what I've done
from lancamento in Lancamentos
where lancamento.Credito == true
&& lancamento.Ocultar == false
&& lancamento.DataLancamento.Month == 10
join categoria in Categorias on lancamento.IdCategoria equals categoria.IdCategoria into temp
from lancamentoJoinCategoria in temp.DefaultIfEmpty()
group lancamentoJoinCategoria by new { lancamentoJoinCategoria.IdCategoria, lancamentoJoinCategoria.Nome } into x
select new {
IdCategoria = (int?)x.Key.IdCategoria
, Nome = x.Key.Nome
}
How do I add the SUM(lancamentos.valor) to the linq to sql above ?
It will be:
(from lancamento in Lancamentos
join categoria in Categorias on lancamento.IdCategoria equals categoria.IdCategoria into temp
from lancamentoJoinCategoria in temp.DefaultIfEmpty()
where lancamento.Credito == true
&& lancamento.Ocultar == false
&& lancamento.DataLancamento.Month == 10
group lancamento by new { lancamentoJoinCategoria.IdCategoria, lancamentoJoinCategoria.Nome } into x
select new
{
IdCategoria = (int?)x.Key.IdCategoria,
Nome = x.Key.Nome,
sumValor = x.Sum(a=>a.valor)
});
You use the .Sum() method.
Eg;
Public Sub LinqToSqlCount03()
Dim q = (From o In db.Orders _
Select o.Freight).Sum()
Console.WriteLine(q)
End Sub
according to MSDN there is no query expression equivalent to the Sum() operation.
I provided a little sample how you could use the Method Syntax of Sum() in a query.
Some query operations, such as Count
or Max, have no equivalent query
expression clause and must therefore
be expressed as a method call. Method
syntax can be combined with query
syntax in various ways. For more
information, see LINQ Query Syntax versus Method Syntax (C#).
var example = new[]
{
new { Count = 1, Name = "a" }, new { Count = 2, Name = "b" },
new { Count = 2, Name = "c" }, new { Count = 2, Name = "c" }
};
var result = from x in example
select new
{
x.Name,
Sum = (from y in example
where y.Count.Equals(2)
&& y.Name==x.Name
select y.Count).Sum()
};
var distinct = result.Distinct().ToList();

Categories

Resources