C# linq changed constructor can't add new value - c#

I'm new to LinQ and started working with it a couple weeks ago. So maybe i have the wrong understanding how LinQ works. i also tried to use an empty constructor as stated here: Invoke constructor inside a linq query
I changed the constructor of a class. I added the entity energyType
public CSVMeter(int meterID, DateTime datum, int numberOfValues, double total, double total2, double qualityScore, int energyType)
{
this.MeterID = meterID;
this.Datum = datum;
this.NumberOfValues = numberOfValues;
this.total = total;
this.total2 = total2;
this.qualityScore = qualityScore;
this.energyType = energyType;
}
I have the following LinQ query.
public List<CSVMeter> getDisctinctMeters(List<CSVMeter> meterList)
{
newMeterList = newMeterList.GroupBy(x => new { x.MeterID, x.Datum })
.Select(x => new CSVMeter(
x.Key.MeterID,
x.Key.Datum,
x.Sum(s => s.NumberOfValues),
x.Sum(s => s.total),
x.Sum(s => s.total2),
0,
x.energyType))
.ToList();
return meterList;
}
but i get the following error at x.energyType
Error 2 'System.Linq.IGrouping' does not contain a definition for 'energyType' and no extension method 'energyType' accepting a first argument of type 'System.Linq.IGrouping' could be found (are you missing a using directive or an assembly reference?) c:\users\rudi\documents\visual studio 2013\projects\plooscassandra\plooscassandra\handlers\meterbuilder.cs 108 136 PloosCassandra
Why can't i find x.energyType ?

Since you do a grouping, the x in the Select's lambda is an IGrouping<CsvMeter> - not a single CsvMeter.
This IGrouping<CsvMeter> has a Key property of the anonymous type you created above and further represents a sequence of CsvMeter instances.
Since you already did sum Sum operations on that sequence, you actually should have known that. The question is, how you want to aggregate the energyType of all CsvMeter instances in the sequence to evaluate the energyType for your resulting new CsvMeter. Here is how to simply use the energyType of the first element in the group:
newMeterList.GroupBy(x => new { x.MeterID, x.Datum })
.Select(x => new CSVMeter(
x.Key.MeterID,
x.Key.Datum,
x.Sum(s => s.NumberOfValues),
x.Sum(s => s.total),
x.Sum(s => s.total2),
0,
x.First().energyType))
.ToList();
First() returns the first CsvMeter instace in the group.
But maybe you actually wanted to group the CsvMeter instances by that energy type, too. So you could change the grouping to
newMeterList.GroupBy(x => new { x.MeterID, x.Datum, x.energyType })
//...
and use that value in the Select:
.Select(x => new CSVMeter(
x.Key.MeterID,
x.Key.Datum,
x.Sum(s => s.NumberOfValues),
x.Sum(s => s.total),
x.Sum(s => s.total2),
0,
x.Key.energyType))
BTW your code look a little weird: your getDisctinctMeters takes a parameter called meterList and that is returned without any change. Instead you use some newMeterList and did not show its declaration. For my answer I assumed your intention was this:
public List<CSVMeter> getDisctinctMeters(List<CSVMeter> meterList)
{
var newMeterList = meterList.GroupBy // shortened for clarity
return newMeterList;
}

The return type of GroupBy is IGouping, representing the groups themselves, not IEnumerable<CSVMeter>, so x has no property energyType. That being said, you would have to clarify for which member of the group the energyType would have to be selected; energyType is not used in the grouping expression.

Related

Receiving an unclear error when using SelectMany() on an IGrouping

When I write the following code I get the error:
the type argument for method Enumerable.SelectMany cannot be inferred from usage
var model = new Overview()
{
ModelData = data.GroupBy(g => g.GroupingId1).Select(s => new OverviewdataGrouped()
{
Id = s.Key,
Grouping = s.GroupBy(gr => gr.GroupingId2). Select(se => new OverviewdataGroupedFurther()
{
Id= se.Key,
Grouping2 = se.Any() ? se.SelectMany(sel => sel).ToList() : new List<DataModel>()
})
})
};
As far as I know this is how I always selected the data from an IGrouping, but for some reason it is not working this way. Does anyone know what I am missing or what the problem could be?
(Note that the variable sel within the SelectMany contains the correct type (DataModel))
The SelectMany Method projects each element of a sequence to an IEnumerable and flattens the resulting sequences into one sequence.
Your usage of the SelectMany method seems redundant, since se is the result of a grouping operation. Try replacing this:
se.SelectMany(sel => sel).ToList()
By This:
se.ToList()

LINQ: No overload for method 'GroupBy' takes 6 arguments / IGrouping<t,t> does not contain a definition

Problem: Getting error
"No overload for method 'GroupBy' takes 6 arguments"
A lot of the SO articles are for specific user created methods. GroupBy is part of the library.
I've tried varying the number of arguments. If I change it to 2 arguments, then the errors points to the next area where it has OrderByDescending and gives the error:
"IGrouping does not contain a definition for
'Start_Date' and no extension method 'Start_Date' accepting a first
argument of type 'IGrouping' could be found."
The code that gives this error is:
var someVariable = DbContextObject.View.Where(
m =>
m.Some_ID == CurrentlyEditingSomeID
).GroupBy(d=> d.Start_Date,f=>f.Some_ID).AsQueryable().OrderByDescending(m => m.Start_Date);
To be used in a ListView
You need to create anonymous object with list of all fields to be included in group by, then access those fields using Key property of grouped list, something like below -
var someVariable = DbContextObject.View.Where(
m =>
m.Some_ID == CurrentlyEditingSomeID
).GroupBy(d=> new { d.Start_Date,d.Some_ID}).AsQueryable().OrderByDescending(m => m.Key.Start_Date);
So the input of your GroupBy is a sequence of Views. Every View has at least a StartDate and SomeId.
Your GroupBy groups all input Views into groups of of items extracted from the Views with the same StartDate. Every Group has a Key containing this common StartDate, the elements in the group are the SomeId of the Views in the group.
The result of the GroupBy is already IQueryable<...>. So AsQueryable is unnecesary, it will only slow down your process.
The input of your Orderby is a sequence of groups. Alas, groups don't have a StartDate. Luckily, groups have a Key containing the StartDate that you want to order by.
var result = DbContextObject.Views
// I onlly want the views with a certain SomeId:
.Where(view => view.SomeID == CurrentlyEditingSomeID)
// group the views into groups with same StartDate
// the elements in the group are the SomeId
.GroupBy(view => view.StartDate, view=>view.SomeID)
// result: a sequence of Groups with a Key and a sequence of SomeId objects
// order the Groups by StartDate, This StartDate is in the Key
.OrderBy(group => group.Key);
By the way, if you don't want a Key, and insist on having a StartDate, there is a less known overload of GroupBy. A version where you can Select what you want in your output.
.GroupBy(view = view.StartDate, // make groups with same StartDate
view => view.SomeId, // select SomeId as elements in the group
(commonStartDate, someIds) => new // from every commonStartDate and the someIds
{ // in this group make a new object
StartDate = commonstartDate, // containing this common StartDate
SomeIds = someIds, // and the SomeId objects in the group
})
.OrderBy(group => group.StartDate); // now you can order by StartDate instead of Key
Update:
You have simple problem in you code, change you code to this:
var rooms = roomBinding.GroupBy(g => new { Id = g.R_ID, Name = g.r_name })
.Select(g => new
{
Id = g.Key.Id,
Name = g.Key.Name,
Count = g.Count()
});
After new in grouping you have to new and set your data like my sample.

Select 1 column from a Group By LINQ query

I think what I need is relatively simple but every example I Google just returns results using First(), which I'm already doing. Here is my expression:
var options = configData.AsEnumerable().GroupBy(row => row["myColumn"]).Select(grp => grp.First());
What I need is only ONE column from the grp portion and to be able to suffix .ToList() on there without an error. As it stands I receive 4 columns, but only need a specific one, kind of like if this (grp => grp["myColumn"]), didn't result in error the Error 153 Cannot apply indexing with [] to an expression of type 'System.Linq.IGrouping<object,System.Data.DataRow>'
Also, Key does not work in the grouping portion as these results are from a DataTable object. See here - >
If you want only the keys, you can use
var options = configData.AsEnumerable().Select(row=>row["myColumn"]).Distinct();
I think that this is what you want:
configData.AsEnumerable()
.GroupBy(r => r["myColumn"])
.Select(g => new
{
myColumnValue = g.Key,
myColumnItems = g.Select(r => r["OtherColumn"]).ToList()
});
Do you understand how/what this does though? Try it out and inspect the resulting IEnumerable. I'm not sure you have a perfect understanding on how GroupBy works but take your time with above example.
See this part:
new
{
myColumnValue = g.Key,
myColumnItems = g.Select(r => r["OtherColumn"]).ToList()
}
This creates an anonymous type which outputs the values of "OtherColumn" column into a list grouped by "myColumn" where value of "myColumn" is in the myColumnValue property.
I'm not sure this answers your question but it looks like this is what you want.
The variable g is of the type IGrouping<object, DataRow>, it's not DataRow. The IGrouping interface is designed to provide a list of DataRow's grouped by object values - it does not produce a flat list, if it did then it would just be a Sort, not GroupBy.
Just specify the field you want after your call to First() e.g.
.Select(grp => grp.FirstOrDefault()["MyFieldName"]);
This will take the first record from the grouping and select the specified field from that record.

Linq with boolean function to relational db in Entity Framework

Probably a few things wrong with my code here but I'm mostly having a problem with the syntax. Entry is a model for use in Entries and contains a TimeStamp for each entry. Member is a model for people who are assigned entries and contains an fk for Entry. I want to sort my list of members based off of how many entries the member has within a given period (arbitrarily chose 30 days).
A. I'm not sure that the function I created works correctly, but this is aside from the main point because I haven't really dug into it yet.
B. I cannot figure out the syntax of the Linq statement or if it's even possible.
Function:
private bool TimeCompare(DateTime TimeStamp)
{
DateTime bound = DateTime.Today.AddDays(-30);
if (bound <= TimeStamp)
{
return true;
}
return false;
}
Member list:
public PartialViewResult List()
{
var query = repository.Members.OrderByDescending(p => p.Entry.Count).Where(TimeCompare(p => p.Entry.Select(e => e.TimeStamp));
//return PartialView(repository.Members);
return PartialView(query);
}
the var query is my problem here and I can't seem to find a way to incorporate a boolean function into a .where statement in a linq.
EDIT
To summarize I am simply trying to query all entries timestamped within the past 30 days.
I also have to emphasize the relational/fk part as that appears to be forcing the Timestamp to be IEnumerable of System.Datetime instead of simple System.Datetime.
This errors with "Cannot implicitly convert timestamp to bool" on the E.TimeStamp:
var query = repository.Members.Where(p => p.Entry.First(e => e.TimeStamp) <= past30).OrderByDescending(p => p.Entry.Count);
This errors with Operator '<=' cannot be applied to operands of type 'System.Collections.Generic.IEnumerable' and 'System.DateTime'
var query = repository.Members.Where(p => p.Entry.Select(e => e.TimeStamp) <= past30).OrderByDescending(p => p.Entry.Count);
EDIT2
Syntactically correct but not semantically:
var query = repository.Members.Where(p => p.Entry.Select(e => e.TimeStamp).FirstOrDefault() <= timeComparison).OrderByDescending(p => p.Entry.Count);
The desired result is to pull all members and then sort by the number of entries they have, this pulls members with entries and then orders by the number of entries they have. Essentially the .where should somehow be nested inside of the .count.
EDIT3
Syntactically correct but results in a runtime error (Exception Details: System.ArgumentException: DbSortClause expressions must have a type that is order comparable.
Parameter name: key):
var query = repository.Members.OrderByDescending(p => p.Entry.Where(e => e.TimeStamp <= timeComparison));
EDIT4
Closer (as this line compiles) but it doesn't seem to be having any effect on the object. Regardless of how many entries I add for a user it doesn't change the sort order as desired (or at all).
var timeComparison = DateTime.Today.AddDays(-30).Day;
var query = repository.Members.OrderByDescending(p => p.Entry.Select(e => e.TimeStamp.Day <= timeComparison).FirstOrDefault());
A bit of research dictates that Linq to Entities (IE: This section)
...var query = repository.Members.OrderByDescending(...
tends to really not like it if you use your own functions, since it will try to map to a SQL variant.
Try something along the lines of this, and see if it helps:
var query = repository.Members.AsEnumerable().Where(TimeCompare(p => p.Entry.Select(e => e.TimeStamp).OrderByDescending(p => p.Entry.Count));
Edit: I should just read what you are trying to do. You want it to grab only the ones within the last X number of days, correct? I believe the following should work, but I would need to test when I get to my home computer...
public PartialViewResult List()
{
var timeComparison = DateTime.Today.AddDays(-30);
var query = repository.Members.Where(p => p.Entry.Select(e => e.TimeStamp).FirstOrDefault() <= timeComparison).OrderByDescending(p => p.Entry.Count));
//return PartialView(repository.Members);
return PartialView(query);
}
Edit2: This may be a lack of understanding from your code, but is e the same type as p? If so, you should be able to just reference the timestamp like so:
public PartialViewResult List()
{
var timeComparison = DateTime.Today.AddDays(-30);
var query = repository.Members.Where(p => p.TimeStamp <= timeComparison).OrderByDescending(p => p.Entry.Count));
//return PartialView(repository.Members);
return PartialView(query);
}
Edit3: In Edit3, I see what you are trying to do now (I believe). You're close, but OrderByDescending would need to go on the end. Try this:
var query = repository.Members
.Select(p => p.Entry.Where(e => e.TimeStamp <= timeComparison))
.OrderByDescending(p => p.Entry.Count);
Thanks for all the help Dylan but here is the final answer:
public PartialViewResult List()
{
var timeComparison = DateTime.Today.AddDays(-30).Day;
var query = repository.Members
.OrderBy(m => m.Entry.Where(e => e.TimeStamp.Day <= timeComparison).Count());
return PartialView(query);
}

Best Practices: Adding properties to a LINQ-to-Entities query result?

I'm writing an ASP.NET Web Pages application and in it, I have a massive LINQ to Entities query. This query pulls data from a table in the database, filters it, groups the data twice, and adds extra properties to the result set. I then loop through the table, outputting the rows.
The query is quite big, sorry:
accountOrders = db.EventOrders
.Where(order => order.EventID == eventID)
.OrderBy(order => order.ProductCode)
.GroupBy(order => new { order.AccountNum, order.Exhibitor, order.Booth })
.Select(orders =>
new {
Key = orders.Key,
ProductOrders = orders
.GroupBy(order => new { order.ProductCode, order.Product, order.Price })
.Select(productOrders =>
new {
Key = productOrders.Key,
Quantity = productOrders.Sum(item => item.Quantity),
HtmlID = String.Join(",", productOrders.Select(o => (o.OrderNum + "-" + o.OrderLine))),
AssignedLines = productOrders.SelectMany(order => order.LineAssignments)
})
})
.Select(account =>
new {
Key = account.Key,
// Property to see whether a booth number should be displayed
HasBooth = !String.IsNullOrWhiteSpace(account.Key.Booth),
HasAssignedDigitalLines = account.ProductOrders.Any(order => order.AssignedLines.Any(line => line.Type == "digital")),
// Dividing the orders into their respective product group
PhoneOrders = account.ProductOrders.Where(prod => ProductCodes.PHONE_CODES.Contains(prod.Key.ProductCode)),
InternetOrders = account.ProductOrders.Where(prod => ProductCodes.INTERNET_CODES.Contains(prod.Key.ProductCode)),
AdditionalOrders = account.ProductOrders.Where(prod => ProductCodes.ADDITIONAL_CODES.Contains(prod.Key.ProductCode))
})
.ToList();
I use the added properties to help style the output. For example, I use HasBooth property to check whether or not I should output the booth location in brackets beside the exhibitor name. The problem is I have to save this big query as an IEnumerable, meaning I get the error: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type. Should I even be manipulating the query this way?
Any advice is much appreciated!
At some point, you are passing in a dynamic datatype to the method, which in turn changes the return type to simply dynamic. You can either cast the dynamic type to a type that is recognised at compile time or explicitly set the return type instead of using var.
You can read more about this issue here: http://www.mikesdotnetting.com/Article/198/Cannot-use-a-lambda-expression-as-an-argument-to-a-dynamically-dispatched-operation

Categories