Linq query fine tuning - c#

Query logic is Grouping the items by Id and Ordering it by Id. Then inside grouped items Ordering by Item1 then by Item2.
Linq query below,
var group1Items = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code = 1)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
var group2Items = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code = 2)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
MyList.Clear();
MyList.InsertRange(Mylist.Count, group1Items);
MyList.InsertRange(Mylist.Count, group2Items);
In the above two queries, only difference is Where condition. Is it possible to rewrite into single query?

Single query:
var groupItems = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == 1 || a.Code == 2)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
If Code 1 must come before Code 2:
var groupItems = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == 1)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items)
.Union(MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == 2)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items)).ToList();

If you only want to avoid code duplication it may be easiest to capture a variable holding the code by which to filter.
int code = 0; // initialize with any value
var groupItems = MyList.GroupBy(g => g.Id)
.Where(w => w.Any(a => a.Code == code)
.Select(s => new
{
key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
})
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items); // No ToList() here!
MyList.Clear();
code = 1;
MyList.InsertRange(Mylist.Count, groupItems.ToList());
code = 2;
MyList.InsertRange(Mylist.Count, groupItems.ToList());

Removed where clause in the LINQ expression and implemented at InsertRange() method. This avoids redundant queries
var groupItems = MyList
.GroupBy(g => g.Id)
.Select(s => new
{ key = s.Key,
items = s.OrderBy(o => o.Item1)
.ThenBy(t => t.Item2)
}
)
.OrderBy(o => o.Id)
.SelectMany(sm => sm.Items).ToList();
MyList.Clear();
MyList.InsertRange(Mylist.Count, groupItems.Where(w => w.Code == 1));
MyList.InsertRange(Mylist.Count, groupItems.Where(w => w.Code == 2));

Related

select after groupby returns same records

I have this query :
_ctx.TestPackageReportDetails.GroupBy(i => new { i.Status, i.TestPackageId })
.Select(m => new { pid = m.Key.TestPackageId, count = m.Count(i => i.Status == "Accept") })
This query returns 185 items with the count of each one :
But i need the number of items with count=5 so i have this query :
_ctx.TestPackageReportDetails.GroupBy(i => new { i.Status, i.TestPackageId })
.Select(m => new { pid = m.Key.TestPackageId, count = m.Count(i => i.Status == "Accept") }).Select(i => i.count == 5).Count();
But this returns 186 why ?
does have my code any problems?
You need to filter the grouping results using Where:
int count =
_ctx.TestPackageReportDetails.GroupBy(i => new { i.Status, i.TestPackageId })
.Where(m => m.Count(i => i.Status == "Accept") == 5).Count();
When you need to filter, you use .Where and not .Select. Select loops through all elements and return a new form that you specify (Maybe selecting one property out of a complex object, or converting an int into a string for some reason).
But where you need to filter, you use .Where and you pass the condition as a parameter.
Set count=5 in where clause
_ctx.TestPackageReportDetails.GroupBy(i => new { i.Status, i.TestPackageId })
.Select(m => new { pid = m.Key.TestPackageId, count = m.Count(i => i.Status == "Accept") })
.Where(i => i.count == 5)
.Count();

LINQ remove items with specific value in group by

I have code where I am grouping my requirements by Opportunity Number as below :
List<Requirement> result = requirements
.GroupBy(l => l.CRMOpportunityNumber)
.Select(cl => new Requirement
{
CRMOpportunityNumber = cl.First().CRMOpportunityNumber,
OpportunityTitle = cl.First().OpportunityTitle,
ClientName = cl.First().ClientName,
TentativeStartDate = cl.Min(c => c.TentativeStartDate),
TotalPositions = cl.Sum(c => c.Required),
RegionName = cl.First().RegionName,
TotalCVProposed = cl.Sum(c => c.Associates.Count),
TotalDeployed = cl.Sum(c => c.Associates.Count(x => x.IsIdentified == true)),
NetGap = cl.Sum(c => c.Required) - cl.Sum(c => c.Associates.Count(x => x.IsIdentified == true)),
}).OrderByDescending(l => l.CRMOpportunityNumber).ToList();
Now I don't want to include those requirements in grouping (result) which have all CATEGORY field values equal to "work commenced" .
Requirement class has a field category.
Exclude the 'Work Commenced' category using a Where clause before grouping
List<Requirement> result = requirements
.Where(r => r.Category != "work commenced")
.GroupBy(l => l.CRMOpportunityNumber)
.Select(cl => new Requirement
{
CRMOpportunityNumber = cl.First().CRMOpportunityNumber,
OpportunityTitle = cl.First().OpportunityTitle,
ClientName = cl.First().ClientName,
TentativeStartDate = cl.Min(c => c.TentativeStartDate),
TotalPositions = cl.Sum(c => c.Required),
RegionName = cl.First().RegionName,
TotalCVProposed = cl.Sum(c => c.Associates.Count),
TotalDeployed = cl.Sum(c => c.Associates.Count(x => x.IsIdentified == true)),
NetGap = cl.Sum(c => c.Required) - cl.Sum(c => c.Associates.Count(x => x.IsIdentified == true)),
}).OrderByDescending(l => l.CRMOpportunityNumber).ToList();
Edit
If you want to exclude groups where all requirements have a category of 'work commenced', how about:
List<Requirement> result = requirements
.GroupBy(l => l.CRMOpportunityNumber)
.Where(cl => cl.All(l => l.Category != "work commenced"))
.Select(cl => new Requirement
{
CRMOpportunityNumber = cl.First().CRMOpportunityNumber,
OpportunityTitle = cl.First().OpportunityTitle,
ClientName = cl.First().ClientName,
TentativeStartDate = cl.Min(c => c.TentativeStartDate),
TotalPositions = cl.Sum(c => c.Required),
RegionName = cl.First().RegionName,
TotalCVProposed = cl.Sum(c => c.Associates.Count),
TotalDeployed = cl.Sum(c => c.Associates.Count(x => x.IsIdentified == true)),
NetGap = cl.Sum(c => c.Required) - cl.Sum(c => c.Associates.Count(x => x.IsIdentified == true))
}).OrderByDescending(l => l.CRMOpportunityNumber)
.ToList();
You want to exclude a group if all items in that group has CATEGORY field value equals to "work commenced". In other words, if any item in a group has value other than "work commenced", you want to include that group. That can be achieved using LINQ Any() like so :
List<Requirement> result =
requirements.GroupBy(l => l.CRMOpportunityNumber)
.Where(g => g.Any(item => item.Category != "work commenced")
.Select(cl => new Requirement
{
......
}).OrderByDescending(l => l.CRMOpportunityNumber)
.ToList();

Lambda for Groupby and Sum?

var product = db.Products
.Where(x => x.ProductKey == 310)
.Join(db.InternetSales, p => p.ProductKey,
c => c.ProductKey, (p, c) => c)
.SingleOrDefault();
This is my query but I dont know how to group it by ProductName and Sum the Sales Amount.
Try this:
var product = db.Products.Where(x => x.ProductKey == 310)
.Join(db.InternetSales, p => p.ProductKey,
c => c.ProductKey, (p, c) => c)
.GroupBy(c => c.ProductName)
.Select(g => new { g.Key, TotalSales = g.Sum(x => x.Sales) })
.SingleOrDefault();
Or if you only need the sum, try this:
var totalSales = db.Products.Where(x => x.ProductKey == 310)
.Join(db.InternetSales, p => p.ProductKey,
c => c.ProductKey, (p, c) => c)
.GroupBy(c => c.ProductName)
.SingleOrDefault()
.Sum(x => x.Sales);
You don't need Join at all, because you already have ProductKey which is then used to join InternetSales table.
I think you can end up with following:
var TotalSales = db.InternetSales
.Where(s => s.ProductKey == 310)
.Select(s => s.Sales)
.Sum();

OrderByDescending doesn't work in nested linq statement

In Linqpad, i can see the correct list. But in code, after putting in the list collection, order by doesn't work for BeginDate. If i use BeginDate with Max, it works. I don't understand where i am wrong?
var templist = contentRepository
.Get(q => (q.Status == (int)StatusEnum.Active) &&
(q.CategoryId == category.GetHashCode() || q.Category.ParentId == category.GetHashCode())
&& q.MinorVersion == 0
&& q.MajorVersion > 0)
.GroupBy(q => q.VersionId)
.OrderByDescending(q => q.Key)
.Select(q => new
{
VersionId = q.Key,
Id = q.Max(x => x.Id),
MajorVersion = q.Max(x => x.MajorVersion),
UpdatedAt = q.Max(x => x.UpdatedAt),
//BeginDate = q.Max(x=>x.BeginDate),
BeginDate = (q.OrderByDescending(x => x.Id).Take(1).Select(x=>x.BeginDate)).First(),
Title = (q.OrderByDescending(x => x.Id).Take(1).Select(x => x.Title)).First(),
ShowOnHomePage = (q.OrderByDescending(x => x.Id).Take(1).Select(x=>x.ShowOnHomePage)).First()
})
.OrderByDescending(x => x.BeginDate)
.Take(maxItemCount)
.ToList();
List<ContentEntity> contents = new List<ContentEntity>();
templist.ForEach(q => contents.Add(
contentRepository
.Get(x => x.VersionId == q.VersionId && x.MajorVersion == q.MajorVersion && x.MinorVersion == 0)
.FirstOrDefault()
));
return contents.Where(q => q.ShowOnHomePage == true)
.OrderByDescending(q => q.MajorVersion)
.OrderByDescending(q => q.BeginDate)
.Take(maxItemCount)
.ToList();
You are ordering by Id, not by BeginDate. Equivalent code for
q.Max(x => x.BeginDate)
Will be
q.OrderByDescending(x => x.BeginDate).Take(1).Select(x => x.BeginDate).First()
Or simplified
q.OrderByDescending(x => x.BeginDate).First().BeginDate

Handle null values in queryover projections

I have a counterpart, which has an address, which MIGHT have a country assigned.
How do I handle this:
InvoiceAddress invoiceAddres = null;
Country InvoiceAddressCountry = null;
Counterpart counterpart = null;
CounterpartTabDTO result = null;
// projections for DTO-mapping
var projections = new[]
{
Projections.Property(() => counterpart.CounterpartId).WithAlias(() => result.InternalID),
Projections.Property(() => counterpart.GlobalCounterpartId).WithAlias(() => result.BasicInfo_GlobalCounterpartyID),
Projections.Property(() => counterpart.Name).WithAlias(() => result.BasicInfo_Name),
Projections.Property(() => counterpart.ShortName).WithAlias(() => result.BasicInfo_ShortName),
Projections.Property(() => counterpart.PhoneNumber).WithAlias(() => result.BasicInfo_Telephone),
Projections.Property(() => counterpart.Webpage).WithAlias(() => result.BasicInfo_WWW),
Projections.Property(() => counterpart.Language).WithAlias(() => result.BasicInfo_Language),
Projections.Property(() => counterpart.VAT).WithAlias(() => result.BasicInfo_VAT),
Projections.Property(() => counterpart.CompanyRegistrationNumber).WithAlias(() => result.BasicInfo_CompanyRegistationno),
Projections.Property(() => invoiceAddres.Name).WithAlias(() => result.BasicInfo_InvoiceAddressContactPerson),
Projections.Property(() => invoiceAddres.Street).WithAlias(() => result.BasicInfo_InvoiceAddressAddress),
Projections.Property(() => invoiceAddres.PostalCode).WithAlias(() => result.BasicInfo_InvoiceAddressPostalCode),
Projections.Property(() => invoiceAddres.City).WithAlias(() => result.BasicInfo_InvoiceAddressCity),
Projections.Property(() => invoiceAddres.Area).WithAlias(() => result.BasicInfo_InvoiceAddressArea),
Projections.Property(() => InvoiceAddressCountry.PrintableName).WithAlias(() => result.BasicInfo_InvoiceAddressCountry),
Projections.Property(() => invoiceAddres.Department).WithAlias(() => result.BasicInfo_InvoiceAddressDepartment),
Projections.Property(() => invoiceAddres.Fax).WithAlias(() => result.BasicInfo_InvoiceAddressFax),
Projections.Property(() => invoiceAddres.MainEmail).WithAlias(() => result.BasicInfo_InvoiceAddressEmail),
};
var query = Session.QueryOver(() => counterpart)
.JoinQueryOver<InvoiceAddress>(x => x.InvoiceAddresses, () => invoiceAddres)
.Where(x => x.IsDefault)
.JoinQueryOver<Country>(() => invoiceAddres.Country, () => InvoiceAddressCountry)
.Select(projections);
The issue is InvoiceAddressCountry, which might be null. If that happens, I'd like the result.BasicInfo_InvoiceAddressCountry property stays null.
To clarify, the above code does not work. It can't handle the null.
From your words I can suppose that you'll need to use left join:
.Left.JoinQueryOver(() => invoiceAddres.Country, () => InvoiceAddressCountry)

Categories

Resources