alternative way to linq GroupBy to improve speed of query - c#

i have some product category which can inherit to more grade (in my project i have max 4 grade). for example (1)Digital_Product-> (2)Mobile-> (3)Android-> (4)Sony
i want to select 2 newest product from each root category (grade 1). i used below query but it processed in 1800ms. the problem is groupBy query. when i delete Groupby, it processed in 100ms.
IEnumerable<Db.Article> _List = uow.ArticleRepository.Get().Where(x =>
x.ArticlePropertyValues.Any(c => (_Status == "All" || c.ArticleProperty.Key == _Status))
&& x.IsActive == true
&& x.IsDeleted == false
&& (_Type == -2 || x.ArticleCategory.TypeID == _Type)
).GroupBy(p => p.ArticleCategory.ParentID == -1 ?
p.ArticleCategory.ID :
(p.ArticleCategory.ArticleCategory1.ParentID == -1 ? p.ArticleCategory.ArticleCategory1.ID :
(p.ArticleCategory.ArticleCategory1.ArticleCategory1.ParentID == -1 ?
p.ArticleCategory.ArticleCategory1.ArticleCategory1.ID :
p.ArticleCategory.ArticleCategory1.ArticleCategory1.ParentID))
).SelectMany(p => p.OrderByDescending(x => x.ID).Take(2));
the ParentID isn't foreign id and no index was used in database.

Related

choose a few id from db C#

I have a database and excel file
And I want to export strings to excel
How to export strings with 1 id I know
The code you can see below
_Hours = Rep.Where(o => o.Projects.ProjectGroupID == 4).Where(o => o.Projects.ProjectType == 1).Sum(o => (decimal?)o.TaskEfforts) ?? 0m,
But how I can choose several id's?
This does not work
_Hours = Rep.Where(o => o.Projects.ProjectGroupID == 4).Where(o => o.ProjectDescriptionID == 10).Where(o => o.ProjectDescriptionID == 17).Where(o => o.ProjectDescriptionID == 18).Where(o => o.ProjectDescriptionID == 19).Where(o => o.ProjectDescriptionID == 21).Where(o => o.ProjectDescriptionID == 24).Where(o => o.ProjectDescriptionID == 26).Where(o => o.Projects.ProjectType == 1).Sum(o => (decimal?)o.TaskEfforts) ?? 0m,
I know, that it is an error, but how can I choose some ID's?
Thanks for answers.
The reason it didn't work is because each where clause is working on a subset of data that the previous one put out.
Use a list for your LINQ query's boolean logic.
List<int> ids = new List<int>{ 10, 17, 13, 7 };
_Hours = Rep.Where(o => ids.Contains(o.Projects.ProjectGroupID)).Where(o => o.Projects.ProjectType == 1).Sum(o => (decimal?)o.TaskEfforts) ?? default(int);
By manipulating the boolean this way, you can essentially convert a list of ints into a list of objects.
For your second where clause, I see you're needing a different attribute to restrict the list even further. This would work, since each where clause operates on the results of the first, but for the reader's sake it should be a &&.
_Hours = Rep.Where(o => ids.Contains(o.Projects.ProjectGroupID) && o.Projects.ProjectType == 1).ToList();
You're on the right track! As mentioned in the comments, you can use the || (OR) operator to select the o object if the id matches. In this case, you only need one Where. Here is your example written out:
_Hours = Rep.Where(o => o.Projects.ProjectGroupID == 4 || o.ProjectDescriptionID == 10 || o.ProjectDescriptionID == 17 || o.ProjectDescriptionID == 18 || o.ProjectDescriptionID == 19 || o.ProjectDescriptionID == 21 || o.ProjectDescriptionID == 24 || o.ProjectDescriptionID == 26).Where(o => o.Projects.ProjectType == 1).Sum(o => (decimal?)o.TaskEfforts) ?? 0m,
Edit: Didn't realize ProjectType property check at end of ORs. Made it it's own WHERE selector

LINQ: Filtering items List by some condition and do something with item by condition

Assume I have users which is List<User>, and User class has Type and Age properties.
I want to filter that list of users by some condition, and do something per item, based on condition. Let this list has 10 users, and some of them are of Type "complex" and some of them "simple". I want my final list to be users with age above 30, and if Type of user is "complex", to do something on it and then add it to final list. It should be something like:
var users= users.Where(u => u.Age > 30
and if u.Type = "complex" ? u = doSomething(u)).ToList();
and if doSomething(u) returns null, skip current "u" to be added to list.
It is half correct code and half like pseudo code because I don't know how to fit if u.Type = "complex" ? u = doSomething(u) part into LINQ expression. How can it be done?
EDIT: And how to do it if I want in final lsit users with Age > 30 OR users with Type = "complex" (and doSomething()) on complex users?
var users = users.Where(u => u.Age > 30) // common filter
.Select(u => u.Type == "complex" ? doSomething(u) : u) //select user or transformed
.Where(u => u != null) //not null only
.ToList();
Well, I came up with some sick variant...
public bool DoSomething()
{
// Do anything
return true;
}
var v = users.Where(x => x!= null && x.Age > 30 && x.Type == "Complex" && x.DoSomething() == true).ToList();
For your edit:
var v = users.Where(x => x!= null && (x.Age > 30 || x.Type == "Complex") && x.DoSomething() == true).ToList();
I would do something as follow :
users.Where(u => u.Age > 30).Select((u) =>
{
if (u.Type == "complex")
{
// Do something
}
return u;
}).ToList();
Select all users with age > 30 and then altering the result depending on the type property.
EDIT :
And for your Edit question just add the condition to the where :
[...].Where(u => u.Age > 30 || u.Type == "complex")[...]
AND QUERY
var andList = users.Where(u => u.Age > 30 && (u.Type == "complex" && doSomething(u) != null)).ToList();
OR QUERY
var orList = users.Where(u => u.Age > 30 || (u.Type == "complex" && doSomething(u) != null)).ToList();

IQueryable with distinct records and ordered

I've been trying to get an IQueryable with distinct and ordered values, but I've found that I can't apply a distinct after orderby or I'll lose the order.
The last query I tried was the following:
IQueryable<gbd_Pages> Listpagespages =
(from c in _db.gbd_Content
where c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder
orderby c.Content ascending
select c.gbd_Pages);
With this I get repeated results.
The table I want returned is gbd_Pages which has a relation of 1 to many with gbd_Content.
With this i mean that gbd_Content will have a foreign key that will have the primary key of gbd_Pages.
I need to do a sortOrder by the table gbd_Template_Fields wich has a relation 1 to 1 with gbd_Content.
Is there a way for me to do this? I need it to be an IQueryable without converting to IEnumerable or list.
I believe what you need is something like this:
var query =
from p in _db.gbd_Pages
from pc in (from c in p.gbd_Content
where c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder
orderby c.Content ascending
select c).Take(1)
orderby pc.Content ascending
select p;
So you start from one side of the relationship (to avoid the need of Distinct), then you select a single record from the many side matching the criteria and having the smaller value of the sorting field (using ordered subquery + Take(1)), and finally sort the result using the sort field value from that single child record.
I'm assuming you have inverse collection navigation property from gbd_Pages to gbd_Content. If you don't, replace the p.gbd_Content with _db.gbd_Content where c.[gbd_Content_FK] == p.[PK].
Your primary from needs to be the gbd_Pages table, rather than gbp_Content if those are the results that you want to return. I'll have to assume a foreign key here but you'd want to change to something like;
IQueryable<gbd_Pages> Listpagespages = _db.gbp_Pages
.Where(p => (from c in _db.gbd_Content
where c.IsActive == true
&& c.IsDeleted == false
select c.gbd_Pages.PrimaryKeyID)
.Any())
.Select(p => new
{
// select specific fields here...
p,
SortCol = _db.gbp_Content
.FirstOrDefault(c => c.PrimaryKeyID)
.Where(c => c.IsActive == true && c.IsDeleted == false &&
c.gbd_Template_Fields.SortOrder == sortOrder)
.Select(c => c.Content)
})
.OrderBy(v => c.SortCol);
Try this:
IQueryable<gbd_Pages> Listpagespages = _db.gbd_Content
.Select (c=>new { c.gbd_Pages })
.Where(c=>c.IsActive == true && c.IsDeleted == false &&c.gbd_Template_Fields.SortOrder == sortOrder)
.Distinct()
.OrderBy(c => c.Content)
.Select(c => c.gbd_Pages)

How to check if two values exist in a list using linq in c#

I have a linq statement that looks like this:
if(items.Any(x => x.CustomerID == 4))
{
}
however, I want to find an object in my list of items that not only contains the customerID of 4 but also a designID of 6.
I know I can do this:
if(items.Any(x => x.CustomerID == 4) && items.Any(x => x.DesignID == 6))
{
}
but this may not work since I need to find the same object that has both these values (this would check individually if these exist). Is there a way to combine these?
You can combine two conditions like x.CustomerID == 4 && x.DesignID == 6
if(items.Any(x => x.CustomerID == 4 && x.DesignID == 6))

How to do optional parameters in LINQ?

Here is what I'm trying to do:
(Dc.DET_Cases.Where(c => c.ProductID == pl.ProductID
&& oldTOR == false ? c.OldTOR == oldTOR :
&& (productLineName.ToInt() == 0 || productLineName.ToInt() == c.ProductLineID)
&& (productCategory.ToInt() == 0 || productCategory.ToInt() == c.ProductCategoryID)
&& (issueType.ToInt() == 0 || issueType.ToInt() == c.IssueTypeID)
&& (issue.ToInt() == 0 || issue.ToInt() == c.IssueID)
)
.FirstOrDefault() != null)
This is the line I'm trying to do.
oldTOR == false ? c.OldTOR == oldTOR :
inside a where LINQ statement. If the value is false then compare the value. If not, then ignore it.
The easiest way to do this is to just set the other option to be true.
Ie: !oldTOR ? c.OldTOR == oldTOR : true
This means that if oldTor is false, then we want to compare OldTor's, otherwise, keep evaluating the rest of the expression.
As an aside, I would split each part of your massive .Where() boolean comparison into individual .Where() statements. This will improve readability and comprehension of your Linq.
Dc.DET_Cases
.Where(c => c.ProductID == pl.ProductID)
.Where(c => !oldTOR ? c.OldTOR == oldTOR : true)
.Where(c => productLineName.ToInt() == 0 || productLineName.ToInt() == c.ProductLineID)
.Where(c => productCategory.ToInt() == 0 || productCategory.ToInt() == c.ProductCategoryID)
.Where(c => issueType.ToInt() == 0 || issueType.ToInt() == c.IssueTypeID)
.Where(c => issue.ToInt() == 0 || issue.ToInt() == c.IssueID)
.FirstOrDefault() != null;
This makes it clear that if oldTor is false then you want to compare, otherwise, pass that statement (true).
I think you can use the LINQ Dynamic Query Library.
And then create a query as you like.
The fastest way to do this is by evaluation paths:
oldTOR && (c.OldTor == oldTOR)
Why is this the fastest? Answer: if oldTOR is false then the whole and statement is false, and there is no need to compare the rest of the statement.

Categories