OrderBy with value from another table - c#

I would like to order a Listview based on the products'name it displays. My website is made of several languages and thus I built a linked table with a product name for each languages.
When I try to sort it I always get this error
DbSortClause expressions must have a type that is order comparable.
Parameter name: key
My code is the following:
IQueryable<Product> query = from p in _dbCtx.Products
where p.LanguageProduct.Any(lg => lg.Language == _currentCulture)
select p;
...
if (keys.Contains("OrderBy"))
{
if (Request.QueryString["OrderBy"] == "NameAsc")
query = query.OrderBy(t => t.LanguageProduct.Select(v => v.ProductName));
}
Any suggestions? Many thanks in advance.
EDIT: Maybe I haven't been clear enough. Therefore, I'll add some more code:
IQueryable<Product> query = from p in _dbCtx.Products
where p.IsVisible == true
where p.LanguageProduct.Any(lg => lg.Language == _currentCulture)
select p;
if (keys.Contains("Indiv"))
{
if (Request.QueryString["Indiv"] == "IndivYes")
query = query.Where(c => c.IsCustomizable == true);
if (Request.QueryString["Indiv"] == "IndivNo")
query = query.Where(c => c.IsCustomizable == false);
}
if (keys.Contains("OrderBy"))
{
if (Request.QueryString["OrderBy"] == "NameAsc")
query = query.OrderBy(t => t.LanguageProduct.Select(v => v.ProductName));
else if (Request.QueryString["OrderBy"] == "NameDes")
query = query.OrderByDescending(t => t.LanguageProduct.Select(v => v.ProductName));
else if (Request.QueryString["OrderBy"] == "PriceAsc")
query = query.OrderBy(t => t.ListPrice);
else if (Request.QueryString["OrderBy"] == "PriceDes")
query = query.OrderByDescending(t => t.ListPrice);
}
Everything works fine by adding successive where clauses to my query until it has to order by name. Hereunder is the structure of my database:
table: Product ProductTranslation
columns: id ReferenceName FKId Language ProductName
Example: 1 FirstProduct 1 fr-FR Produit 1
1 de-DE Produkt 1
1 en-US Product 1

You can do this using this:
var queryable = query.SelectMany(p => p.LanguageProduct, (p, l) => new{p,l})
.OrderBy(t => t.l.ProductName)
.Select(t => t.p);

Related

C# db query where conditions are met, orderby date and then get the first result

While evaluating some queries we found some possible optimization. The ideia is shown below but I currently don't know how to solve this.
Current query:
public static List<Object> SampleQuerySales(int store_id)
{
var query = (from clients in db.table1.Where(p => p.store_id == store_id)
from sales in db.table2.Where(q => q.customer_id == clients.customer_id))
select new Object {
...
}).ToList();
return query;
}
This returns all sales made, but its required only the latest sale (OperationDate) from a datetime reference. As obvious this became a bottleneck.
My ideia was to make it similar to query below, which is incorrect (doesn't compile). How can I achieve this dataset?
var query = (from clients in db.table1.Where(p => p.store_id == store_id)
from sales in db.table2.Where(q => q.customer_id == clients.customer_id
&& q.OperationDate <= dateReference)
.OrderByDescending(s => s.OperationDate).FirstOrDefault() //error
select new Object {
...
}).Tolist();
Since you only want one value from table2, use let instead of from:
var query = (from client in db.table1.Where(p => p.store_id == store_id)
let mostRecentSaleAfterDateReference = db.table2
.Where(q => q.customer_id == client.customer_id
&& q.OperationDate <= dateReference)
.OrderByDescending(s => s.OperationDate)
.FirstOrDefault()
select new Object {
...
}).Tolist();

How to groupby with inner join using linq lamba expression

I'm trying to convert a sql stored proc to linq. I'm having issues with the groupby and inner joins.
Here is what I've tried:
var r = _context.Table1
.GroupBy(x => new { x.OptionId, x.Years, x.Strike })
.Join(_context.Table2,
oc => oc.OptionId, o => o.OptionId, (oc, o) => new
{
OptionsCosts = oc,
Options = o
}).Where(x => x.Options.OptionType == 1
&& x.Options.QualifierId != null
&& x.Options.CreditingMethod != "xxx")
.Select(y => new DataModel.Table1()
{
Years = y.Select(a => a.OptionsCosts.Years).FirstOrDefault(),
Strike = y.Select(a => a.OptionsCosts.Strike).FirstOrDefault(),
Value = y.Select(a => a.OptionsCosts.Value).FirstOrDefault(),
ChangeUser = y.Select(a => a.OptionsCosts.ChangeUser).FirstOrDefault(),
ChangeDate = DateTime.Now,
OptionId = y.Select(a => a.OptionsCosts.OptionId).FirstOrDefault()
});
Here is the SQL that I'm trying to convert:
SELECT o2.OptionId, o2.Years, o2.Strike, SUM(d2.Weights) as 'TotalWeight', COUNT(*) as 'Counts'
FROM Table1 o2
INNER JOIN #Dates d2 --this is a temp table that just holds dates. I was thinking just a where statement could do it???
ON d2.EffectiveDate = o2.EffectiveDate
INNER JOIN Table2 od2
ON od2.OptionId = o2.OptionId
AND od2.OptionType = 1
AND od2.qualifierid is null
AND od2.CreditingMethod <> 'xxx' --28095
GROUP BY o2.OptionId,o2.Years, o2.Strike
My data is way off so I'm sure I'm doing something wrong.
var table1=_context.Table1
.groupBy(o2=> new{
o2.OptionId
, o2.Years
, o2.Strike
})
.select(s=> new{
s.key.OptionId
, s.key.Years
, s.key.Strike
,TotalWeight=s.sum(x=>x.Weights)
,Counts=o2.count(c=>c.OptionId)
}).tolist();
var result=table1
.Join(_context.Table2,oc => oc.OptionId, o => o.OptionId, (oc, o) => new{ OptionsCosts = oc, Options = o })
.Where(x => x.Options.OptionType == 1
&& x.Options.QualifierId != null
&& x.Options.CreditingMethod != "xxx")
.select(x=> new {
x.oc.OptionId, x.oc.Years, x.oc.Strike, x.oc.TotalWeight, x.oc.Counts
}).tolist();
Small advise, when you rewriting SQL queries, use LINQ Query syntax which is close to SQL and more effective to avoid errors.
var dates = new List<DateTime>() { DateTime.Now }; // fill list
var query =
from o2 in _context.Table1
where dates.Contains(o2.EffectiveDate)
from od2 in _context.Table1.Where(od2 => // another way to join
od2.OptionId == o2.OptionId
&& od2.OptionType == 1
&& od2.qualifierid == null
&& od2.CreditingMethod != "xxx")
group o2 by new { o2.OptionId, o2.Years, o2.Strike } into g
select new
{
g.Key.OptionId,
g.Key.Years,
g.Key.Strike,
Counts = g.Count()
// SUM(d2.Weights) as 'TotalWeight', -this one is not available because dates in memory
};
If you are on start and trying to rewrite procedures on LINQ - EF Core is bad idea. Too limited IQueryable support and usually you will fight for each complex LINQ query.
Try linq2db which has temporary tables support and your stored proc can be rewritten into identical LINQ queries. Or you can use linq2db.EntityFrameworkCore to extend EF Core functionality.
Disclaimer. I’m creator of this extension and one from linq2db creators.

Loop a query match and remove subset items

I have the following two tables which hold the information on items that have been completed I needed to do it this way for reporting purposes.
qry = db.AssemblyListItems
.AsNoTracking()
.Where(x => x.ProductionPlanID == (long)_currentPlan.ProductionPlan )
.ToList();
var _query = qry.Where(w => w.ItemCode == "EPR15CT.L01" && w.DocumentNo == "0000026590")
.SingleOrDefault();
var hasbeenAssembled = dbCompletedPrinteds
.AsNoTracking()
.Where(x => x.ProductionPlanId == (long)_currentPlan.ProductionPlan)
.ToList();
foreach (var item in hasbeenAssembled) {
qry.RemoveAll(X => X.SOPOrderReturnID == Int32.Parse(item.SopLineItemId) );
}
If it finds any matching items in the second table to remove it from the main query.
You will see the tables have much the same data stored in them. But for some reason the the items is still showing in the I need some way of looping the first query with the second query and removing the matching items from the qry object.
So steps I need to do is :
Loop completed and printed object remove any matching products with the same document number and item code and match the productplan id item and then remove it from the master AssemblyListItems query and then dispaly in a gui at the min its keeping the item in the list.
Edit 2
This would work but I dont think its very effiecent.
List<AssemblyListItems> _query = qry.ToList();
foreach (AssemblyListItems item in _query)
{
var hasbeenAssembled = db.CompletedPrinteds.AsNoTracking().Where(x => x.ProductionPlanId == item.ProductionPlanID).ToList();
foreach(var subitem in hasbeenAssembled )
{
if(item.ProductionPlanID ==subitem.ProductionPlanId && item.DocumentNo == subitem.DocumentNo && item.DocumentNo == subitem.DocumentNo)
{
qry.RemoveAll(x => x.ProductionPlanID == subitem.ProductionPlanId && x.DocumentNo == item.DocumentNo && x.ItemCode == subitem.StockCode);
}
}
}
Edit 3
To Show the items in the edmx
Last week I did query below using Left outer Join to get three group of data
var results = (from srs in srsEmps
join dest in destEmps on srs.EmpCode equals dest.EmpCode into dsNull
from dest in dsNull.DefaultIfEmpty()
select new { srs = srs, dest = dest }).ToList();
var Common = results.Where(x => (x.srs != null) && ( x.dest != null)).ToList();
var Deleted = results.Where(x => x.dest != null).ToList();
var NewlyAdded = results.Where(x => x.srs != null);
Something like this maybe?
//first get list of assembled/completed items with the _currentplan's ID:
var hasbeenAssembled =
dbCompletedPrinteds
.AsNoTracking()
.Where(x => x.ProductionPlanId == (long)_currentPlan.ProductionPlan)
//note: not sure of underlying DB technology here, but this .ToList() will
//typically cause a DB query to execute here.
.ToList();
//next, use that to filter the main query.
qry = db.AssemblyListItems
.AsNoTracking()
.Where(x =>
//Get current plan items
(x.ProductionPlanID == (long)_currentPlan.ProductionPlan)
//filter out items which are in the previous list of 'completed' ones
&& (!hasBeenAssembled.Any(hba => hba.SopLineItemId==x.SOPOrderReturnID))
)
.ToList();
//I don't have any idea what _query is for, it doesn't seem to be used for anything
//in this example...
var _query = qry.Where(w => w.ItemCode == "EPR15CT.L01" && w.DocumentNo == "0000026590")
.SingleOrDefault();

Linq nested or inner query

Suppose I have a list of employees and each employee has several projects. I can get a given employee using:
var employee = employees.SingleOrDefault(x => x.Id == "id");
But how can I filter also project for the employee?
For example:
var employee = list
.SingleOrDefault(x => x.Key == employeeKey &&
x.Projects.SingleOrDefault(p => p.Key == projectKey));
If you want to filter down the Projects after getting the Employee you can use a .Select().
var result = employees.Where(e => e.Id == id).Select(e => new Employee
{
Id = e.Id,
Projects = e.Projects.SingleOrDefault(p => p.Key == projectKey)
}).SingleOrDefault();
So you can get the data you need in one step, but you have to assign the properties by yourself.
Another way is to first get your Employee and then filter down the projects, like BoredomOverload suggested:
var employee = employees.SingleOrDefault(x => x.Id== "id");
employee.Projects = employee.Projects.SingleOrDefault(p => p.Key == projectKey);
Either way you get the employee and the Projects of that Employee filtered.
var employee = employees.SingleOrDefault(
x => x.Id.Equals("id") && x.project.Equals("project")
);
Use Any() LINQ method like
var employee = employees.SingleOrDefault(x => x.Id== "id" && x.Projects.Any(p => p.Id == "Id"));
Moreover, You are filtering based on employee ID x.Id== "id" and mostly that employee ID would a primary key (Unique in nature) and in such case filtering just by Id would be much enough I believe
SingleOrDefault returns the object if found or null. So, in your case, it returns all employees because you are not testing anything. You just said if the project is there then return it.
Use Any instead which will return a boolean value if exist or not:
var employee = list.SingleOrDefault(x => x.Key == customerKey && x.Projects.Any(p => p.Key == projectKey));
If you need to filter if he has only one project with the specific key:
var employee = list.SingleOrDefault(x => x.Key == customerKey && x.Projects.Count(p => p.Key == projectKey) == 1);
You can also achieve it with SingleOrDefault but test the value with null:
var employee = list.SingleOrDefault(x => x.Key == customerKey && x.Projects.SingleOrDefault(p => p.Key == projectKey) != null);
If you want the return type to be more specific then use the select.
If it didn't work, try to add "include" to the list:
list.Include("Projects").... the rest of the query

Entity framework use already selected value saved in new variable later in select sentance

I wrote some entity framework select:
var query = context.MyTable
.Select(a => new
{
count = a.OtherTable.Where(b => b.id == id).Sum(c => c.value),
total = a.OtherTable2.Where(d => d.id == id) * count ...
});
I have always select total:
var query = context.MyTable
.Select(a => new
{
count = a.OtherTable.Where(b => b.id == id).Sum(c => c.value),
total = a.OtherTable2.Where(d => d.id == id) * a.OtherTable.Where(b => b.id == id).Sum(c => c.value)
});
Is it possible to select it like in my first example, because I have already retrieved the value (and how to do that) or should I select it again?
One possible approach is to use two successive selects:
var query = context.MyTable
.Select(a => new
{
count = a.OtherTable.Where(b => b.id == id).Sum(c => c.value),
total = a.OtherTable2.Where(d => d.id == id)
})
.Select(x => new
{
count = x.count,
total = x.total * x.count
};
You would simple do
var listFromDatabase = context.MyTable;
var query1 = listFromDatabase.Select(a => // do something );
var query2 = listFromDatabase.Select(a => // do something );
Although to be fair, Select requires you to return some information, and you aren't, you're somewhere getting count & total and setting their values. If you want to do that, i would advise:
var listFromDatabase = context.MyTable.ToList();
listFromDatabase.ForEach(x =>
{
count = do_some_counting;
total = do_some_totalling;
});
Note, the ToList() function stops it from being IQueryable and transforms it to a solid list, also the List object allows the Linq ForEach.
If you're going to do complex stuff inside the Select I would always do:
context.MyTable.AsEnumerable()
Because that way you're not trying to still Query from the database.
So to recap: for the top part, my point is get all the table contents into variables, use ToList() to get actual results (do a workload). Second if trying to do it from a straight Query use AsEnumerable to allow more complex functions to be used inside the Select

Categories

Resources