No definition of Sum in linq - c#

I have written following code in which I want to calculate total sum of the price.
But it says no definition of sum. What possibly might be wrong?
public virtual IList<DesignWiseTotal> summary(int customerId)
{
var query = _orderItemRepository.Table;
return query.Where(oi => oi.Product.Designer_Id == customerId)
.Select(oi => new DesignWiseTotal
{
GrandTotal = oi.Sum(x => x.PriceExclTax),
});
}
Above code is quite similar to following code, which works fine. Following code calculates sum group by twine. What I am trying to do in above code is calculate the grand total of all twines for one customer.
public virtual IList<DesignWiseTotal> DesignWiseSplits(int customerId)
{
var query = _orderItemRepository.Table;
return query.Where(oi => oi.Product.Designer_Id == customerId)
.GroupBy(oi => oi.Product.Twine)
.Select(oi => new DesignWiseTotal
{
Total = oi.Sum(x => x.PriceExclTax),
Twine = oi.Key,
}).ToList();
}
I am attaching a screenshot of error too.

A single OrderItem (or whatever the name of the class obtained is from the repository) isn't a collection (e.g. IEnumerable, IQueryable etc) , so .Sum cannot be applied.
In the second query, you are Summing the Groups of .GroupBy(oi => oi.Product.Twine). Each group will have one or more elements, hence Sum is permissable
The choice of lambda parameter name is unfortunate .. more clear would be:
.GroupBy(oi => oi.Product.Twine)
.Select(grp => new DesignWiseTotal ...
Since it appears you want to return a simple scalar value (Grand Total), why not change the method signature to:
public virtual decimal DesignWiseSplits(int customerId)
And then
return query.Where(oi => oi.Product.Designer_Id == customerId)
.Sum(oi => oi.PriceExclTax);
(or I guess return a single DesignWiseTotal with the Sum for the filtered customer`?)
(And I'm hoping of course that oi.PriceExclTax is decimal)
Edit
If you retain this signature:
public virtual IList<DesignWiseTotal> summary(int customerId)
You'll can use:
return new List<DesignWiseTotal>
{
new DesignWiseTotal
{
GrandTotal = query.Where(oi => oi.Product.Designer_Id == customerId)
.Sum(oi => oi.PriceExclTax);
}
};
Although this does seem overkill to return a scalar as an array of a class.

Related

Check if elements from one list elements present in another list

I have 2 c# classes -
class ABC
{
string LogId;
string Name;
}
class XYZ
{
string LogId;
string Name;
}
class Checker
{
public void comparelists()
{
List<ABC> lstABC =new List<ABC>();
lstABC.Add(new ABC...);
lstABC.Add(new ABC...);
lstABC.Add(new ABC...);
List<XYZ> lstXYZ =new List<XYZ>();
lstXYZ.Add(new XYZ...);
lstXYZ.Add(new XYZ...);
lstXYZ.Add(new XYZ...);
var commonLogId = lstABC
.Where(x => lstXYZ.All(y => y.LogId.Contains(x.LogId)))
.ToList();
}
}
As seen from the code , I want to fetch all logids from lstABC which are present in lstXYZ.
Eg. lstABC has ->
LogId="1", Name="somename1"
LogId="2", Name="somename2"
LogId="3", Name="somename3"
LogId="4", Name="somename4"
LogId="5", Name="somename5"
lstXYZ has ->
LogId="1", Name="somename11"
LogId="2", Name="somename22"
LogId="3", Name="somename33"
LogId="8", Name="somename8"
LogId="9", Name="somename9"
Then all logids from lstABC which are present in lstXYZ are - 1,2,3 ; so all those records are expected to get fetched.
But with below linq query -
var commonLogId = lstABC
.Where(x => lstXYZ.All(y => y.LogId.Contains(x.LogId)))
.ToList();
0 records are getting fetched/selected.
approach with Any()
var res = lstABC.Where(x => (lstXYZ.Any(y => y.LogId == x.LogId))).Select(x => x.LogId);
https://dotnetfiddle.net/jRnUwS
another approach would be Intersect() which felt a bit more natural to me
var res = lstABC.Select(x => x.LogId).Intersect(lstXYZ.Select(y => y.LogId));
https://dotnetfiddle.net/7iWYDO
You are using the wrong LINQ function. Try Any():
var commonLogId = lstABC
.Where(x => lstXYZ.Any(y => y.LogId == x.LogId))
.ToList();
Also note that the id comparison with Contains() was wrong. Just use == instead.
All() checks if all elements in a list satisfy the specified condition. Any() on the other hand only checks if at least one of the elements does.
Be aware that your implementation will be very slow when both lists are large, because it's runtime complexity grows quadratically with number of elements to compare. A faster implementation would use Join() which was created and optimized exactly for this purpose:
var commonLogIds = lstABC
.Join(
lstXYZ,
x => x.LogId, // Defines what to use as key in `lstABC`.
y => y.LogId, // Defines what to use as key in `lstXYZ`.
(x, y) => x) // Defines the output of matched pairs. Here
// we simply use the values of `lstABC`.
.ToList();
It seems pretty unnatural to intersect entirely different types, so I would be tempted to interface the commonality and write an EqualityComparer:
class ABC : ILogIdProvider
{
public string LogId {get;set;}
public string Name;
}
class XYZ : ILogIdProvider
{
public string LogId{get;set;}
public string Name;
}
interface ILogIdProvider
{
string LogId{get;}
}
class LogIdComparer : EqualityComparer<ILogIdProvider>
{
public override int GetHashCode(ILogIdProvider obj) => obj.LogId.GetHashCode();
public override bool Equals(ILogIdProvider x, ILogIdProvider y) => x.LogId == y.LogId;
}
Then you can Intersect the lists more naturally:
var res = lstABC.Intersect(lstXYZ, new LogIdComparer());
Live example: https://dotnetfiddle.net/0Tt6eu

How to orderby more then one parameter in the same weight?

Im trying to make a program that sorts objects by more then one parameters.
I need the order by to be in the same weight for all the parameters. what functions do i need to use in order to get that result?
I tried to use OrderBy() and then- ThenBy() but its ordering the first parameter first so the ordering isn't equal weight.
values = File.ReadAllLines(filepath)
.Skip(1)
.Select(v => Fund.FromCsv(v))
.OrderByDescending(x => x.sharp)
.ThenBy(x=>x.yearlychange)
.ToList();
For example you can take the stocks market, in that case i want to order the stocks by the yield in the last year but also to order by standard deviation. in that way i can get stock that have the best yield in the last year but also the best standard deviation. it wont be the best yield from all, it will be combined.
As you have been already informed, it is not really a programistic problem, more like algorithm/domain one. Nevertheless, if you already would have the algorithm, you can, of course, do it like this way. (basing on the example you present in the comment)
void Main()
{
var funds = new List<Fund>();
funds.Add(new Fund() { Age = 18, Money = 100000 });
funds.Add(new Fund() { Age = 20, Money = 101000 });
//Here is normally your code with loading it from CSV
//File.ReadAllLines(filepath)
// .Skip(1)
// .Select(v => Fund.FromCsv(v))
var value = funds.OrderBy(t => t.SortingFactor);
}
public class Fund
{
public int Age { get; set; }
public decimal Money { get; set; }
public decimal SortingFactor
{
get
{
//Here is your domain algorithm you must sort out first (your example data would be)
return Money / Age;
}
}
}
I'm not sure if I fully understand your aim but another alternative if fund is not code you can modify is an anonymous object in your order by e.g.
values = File.ReadAllLines(filepath)
.Skip(1)
.Select(v => Fund.FromCsv(v))
.OrderByDescending(x => new { x.sharp, x.yearlychange })
.ToList();

LINQ IN type query to get records based on result set of another query

UserAuthorizations table has Company per User wise, and I want to get the list of Companies for the Users which are assigned as to them.
// GET: odata/Companies/GetUserCompanies
[EnableQuery]
public IQueryable<UserAuthorization> GetUserCompanies()
{
List<int> userCompanyIds = db.UserAuthorizations.Where(u => u.Cwid == this.UserId).Select(s => s.CompanyId).ToList();
return db.Companies.Where(m => m.Id.ToString().Contains(userCompanyIds));
}
For above I am getting error like
Cannot convert from 'System.Collections.Generic.List<int>' to 'string'
try it
public IQueryable<Company> GetUserCompanies()
{
List<int> userCompanyIds = db.UserAuthorizations.Where(u => u.Cwid == this.UserId).Select(s => s.CompanyId).ToList();
return db.Companies.Where(m => userCompanyIds.Contains(m.Id));
}

cannot implicitly convert type when groupby

I get this error when I try to group by CellID:
Cannot implicitly convert type 'System.Collections.Generic.List
System.Linq.IGrouping int,p2pControllerLogAnalyser.Models.GSMData' to
'System.Collections.Generic.List
p2pControllerLogAnalyser.Models.GSMData'
public List<GSMData> GetCellID()
{
return Gsmdata.GroupBy(x => x.CellID).ToList();
}
What am I doing wrong and how can I fix it?
If you really need to do this, though I can't imagine why, you'll need to flatten the per-group enumerables into a single list using SelectMany:
public List<GSMData> GetCellID()
{
return Gsmdata
.GroupBy(x => x.CellID)
.SelectMany(gr => gr)
.ToList();
}
Of course, this looks like you are trying to batch items with the same CellID together, so you could always simply order it:
public List<GSMData> GetCellID()
{
return Gsmdata
.OrderBy(x => x.CellID)
.ToList();
}
Further to your comments, distinct CellID values can be returned thus:
return Gsmdata.Select(x => x.CellID).Distinct();
If you wish to return an ID and a count of grouped data, you can bundle that into an anonymous type:
return Gsmdata
.GroupBy(x => x.CellID)
.Select(gr => new { CellID = gr.Key, Count = gr.Count() });
Though if you are returning this from a method I'd make a discoverable type and not use an anonymous type.
if i understand right you need something like this
var result = (from gdata in Gsmdata
group gdata by gbata.CellID into g
select new Result{
CellID = g.Key,
Meters = g.Sum(i=>i.Meter)
}
).ToList();
where Result is
public class Result{
public /*type your CellID */ CellID;
public /*type your Meter */ Meters;
}
The problem is that GroupBy returns a grouped collection, in this case System.Collections.Generic.List<System.Linq.IGrouping<int, p2pControllerLogAnalyser.Models.GSMData>>. Your method returns a List<GSMData>.
In order to fix this, you need to adjust your method declaration and/or your Linq query so that the types match.
From your comments, I understand that you want to return a list of the distinct cell ids. You can do this by changing both the method declaration and the Linq query (I'm assuming that CellID is of type int based upon the type in the IGrouping):
public List<int> GetCellID()
{
return Gsmdata.Select(x => x.CellID)
.Distinct().ToList();
}

How to query child tables values

I have 2 tables: POHeader and PODetail. I want to return all POHeaders that have an associated PODetail.ItemId = intItemId. How can I do this in LINQ?
This is what I've tried.
First I have a method in my Repository that uses the Include parameter to include the PODetails:
public IQueryable<POHeader> SearchForWithDetails(int intFacilityId)
{
return DbSet.Include("PODetails").Where(x => x.FacilityId == intFacilityId);
}
Then the result of that gets passed to:
public IQueryable<POHeader> SearchForPODetailsByItemId(IQueryable<POHeader> poHeaders, int intItemId)
{
//This returns a type of PODetail not POHeader
var q = poHeaders.SelectMany(c => c.PODetails).Where(c => c.ItemId == intItemId);
//In this case, I can't figure out the syntax :(
var p = from poHeader in poHeaders
let filteredPOs = from poDetail in poHeader.PODetails
where poDetail.ItemId == intItemId
select ????
return p;
}
What is the correct way to do this?
Also, I can foresee needing 2 results of this:
just return a IQueryable
return a joined table result.
Try this;
var result = poHeaders.Where(e => e.PODetails.Any(a => a.ItemId == intItemId));
Assuming your a Header->Detail is a 1-to-many relationship, and Detail has a navigation back to it's header called .Header:
public IQueryable<POHeader> SearchForPODetailsByItemId(IQueryable<POHeader> poHeaders, int intItemId)
{
var headersForThisItem = poHeaders.SelectMany(pod => pod.PODetails).Where(pod => pod.ItemId == intItemId)
.Select(pod=> pod.Header).Distinct();//.Distinct to eliminate duplicates when 2 Details have the same header. Not necessary if ItemId filter naturally provides distinct results.
return headersForThisItem ;
}
Untested, but I think that will give you what you want.

Categories