Grouping and ordering a list using LINQ - linq

I have a message structure that contains the following
DateTime dateIn;
long? messageId;
string messageContent;
I am trying to group my message list based on messageId and ordered by the dateIn field.
My LINQ currently looks like this
var groups = from c in MessageList
let name = c.messageId
orderby name ascending, c.dateIn ascending
group c by name into g
select g;
When I try and feed it back into a new List< Messages>, the compiler comes back with
"Cannot implicitly convert type System.Collections.Generic.List<
System.Linq.IGrouping < long?, Messages> > to
System.Collections.Generic.List< Messages>"
Is the problem down to the long? more than anything? I have tried to cast messageId to long, but that doesn't seem to work either.

I suppose you can use SelectMany to orbitaine your Message collection from the grouped one (if I understood you correct):
List<Messages> back = groups.SelectMany(m=>m.ToList()).ToList();
UPDATED
According to your comments. When you use GroupBy - Linq creates the Enumerable of new generic type combining your collection type and key type, which you use for grouping. In your case it is Messages type and long? (the type of messageId - key, you are grouping by). So this should work for you:
List<long?,Messages> grouped = groups.ToList();
Or you can use var and this should work also:
var grouped = groups.ToList();

If I understand what are you trying to do, there is simpler way. Look at my demo:
public class Program
{
static void Main(string[] args)
{
List<Message> MessageList = new List<Message>()
{
new Message(){ dateIn = new DateTime(2000,1,11)},
new Message(){ dateIn = new DateTime(2000,1,9)},
new Message(){ dateIn = new DateTime(2000,1,8), messageId = 5},
new Message(){ dateIn = new DateTime(2000,1,12)},
new Message(){ dateIn = new DateTime(2000,1,2), messageId = 7}
};
foreach (var item in MessageList)
{
Console.WriteLine(item);
}
Console.WriteLine("===");
// MUCH SIMPLER
var result = MessageList
.GroupBy(m => m.messageId)
.SelectMany(m => m.ToList())
.OrderBy(m => m.dateIn);
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
struct Message
{
public DateTime dateIn { get; set; }
public long? messageId { get; set; }
public string messageContent { get; set; }
public override string ToString()
{
return messageId + "\t" + dateIn;
}
}

Related

How to select multiple class properties in LINQ Expression?

If I have a class like this
`
class Person
{
public string First;
public string Last;
public bool IsMarried;
public int Age;
}`
Then how can I write a LINQ Expression where I could select properties of a Person. I want to do something like this (user can enter 1..n properties)
SelectData<Person>(x=>x.First, x.Last,x.Age);
What would be the input expression of my SelectData function ?
SelectData(Expression<Func<TEntity, List<string>>> selector); ?
EDIT
In my SelectData function I want to extract property names and then generate SELECT clause of my SQL Query dynamically.
SOLUTION
Ok, so what I have done is to have my SelectData as
public IEnumerable<TEntity> SelectData(Expression<Func<TEntity, object>> expression)
{
NewExpression body = (NewExpression)expression.Body;
List<string> columns = new List<string>();
foreach(var arg in body.Arguments)
{
var exp = (MemberExpression)arg;
columns.Add(exp.Member.Name);
}
//build query
And to use it I call it like this
ccc<Person>().SelectData(x => new { x.First, x.Last, x.Age });
Hopefully it would help someone who is looking :)
Thanks,
IY
I think it would be better to use delegates instead of Reflection. Apart from the fact that delegates will be faster, the compiler will complain if you try to fetch property values that do not exist. With reflection you won't find errors until run time.
Luckily there is already something like that. it is implemented as an extension function of IEnumerable, and it is called Select (irony intended)
I think you want something like this:
I have a sequence of Persons, and I want you to create a Linq
statement that returns per Person a new object that contains the
properties First and Last.
Or:
I have a sequence of Persns and I want you to create a Linq statement
that returns per Person a new object that contains Age, IsMarried,
whether it is an adult and to make it difficult: one Property called
Name which is a combination of First and Last
The function SelectData would be something like this:
IEnumerable<TResult> SelectData<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, TResult> selector)
{
return source.Select(selector);
}
Usage:
problem 1: return per Person a new object that contains the
properties First and Last.
var result = Persons.SelectData(person => new
{
First = person.First,
Last = person.Last,
});
problem 2: return per Person a new object that contains Age, IsMarried, whether he is an adult and one Property called Name which is a combination
of First and Last
var result = Persons.SelectData(person => new
{
Age = person.Name,
IsMarried = person.IsMarried,
IsAdult = person.Age > 21,
Name = new
{
First = person.First,
Last = person.Last,
},
});
Well let's face it, your SelectData is nothing more than Enumerable.Select
You could of course create a function where you'd let the caller provide a list of properties he wants, but (1) that would limit his possibilities to design the end result and (2) it would be way more typing for him to call the function.
Instead of:
.Select(p => new
{
P1 = p.Property1,
P2 = p.Property2,
}
he would have to type something like
.SelectData(new List<Func<TSource, TResult>()
{
p => p.Property1, // first element of the property list
p -> p.Property2, // second element of the property list
}
You won't be able to name the returned properties, you won't be able to combine several properties into one:
.Select(p => p.First + p.Last)
And what would you gain by it?
Highly discouraged requirement!
You could achive similar result using Reflection and Extension Method
Model:
namespace ConsoleApplication2
{
class Person
{
public string First { get; set; }
public string Last { get; set; }
public bool IsMarried { get; set; }
public int Age { get; set; }
}
}
Service:
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public static class Service
{
public static IQueryable<IQueryable<KeyValuePair<string, object>>> SelectData<T>(this IQueryable<T> queryable, string[] properties)
{
var queryResult = new List<IQueryable<KeyValuePair<string, object>>>();
foreach (T entity in queryable)
{
var entityProperties = new List<KeyValuePair<string, object>>();
foreach (string property in properties)
{
var value = typeof(T).GetProperty(property).GetValue(entity);
var entityProperty = new KeyValuePair<string, object>(property, value);
entityProperties.Add(entityProperty);
}
queryResult.Add(entityProperties.AsQueryable());
}
return queryResult.AsQueryable();
}
}
}
Usage:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
class Program
{
static void Main(string[] args)
{
var list = new List<Person>()
{
new Person()
{
Age = 18,
First = "test1",
IsMarried = false,
Last = "test2"
},
new Person()
{
Age = 40,
First = "test3",
IsMarried = true,
Last = "test4"
}
};
var queryableList = list.AsQueryable();
string[] properties = { "Age", "Last" };
var result = queryableList.SelectData(properties);
foreach (var element in result)
{
foreach (var property in element)
{
Console.WriteLine($"{property.Key}: {property.Value}");
}
}
Console.ReadKey();
}
}
}
Result:
Age: 18
Last: test2
Age: 40
Last: test4

LINQ to return list of Object filtered on a property of a Child object in nested List<>

I'm looking for some help with a LINQ query to filter on a property/enum of a custom object which is in a nested List, and want to maintain the parent object in return list.
For example/clarity/sample code, I have a parent object, which has in it a List based on class and enum below:
public class Stage {
public String Name { get; set;}
public List<Evaluation> MyEvaluations { get; set;}
}
public class Evaluation {
public float Result { get; set; }
public enumResultType ResultType { get; set; }
}
public enum enumResultType {
A,B,C
}
One can simulate sample data along those lines with something like:
List<Stage> ParentList = new List<Stage>();
Stage Stage1 = new Stage() { Name = "Stage1",
MyEvaluations = new List<Evaluation>() {
new Evaluation() { ResultType = enumResultType.A, Result=5 },
new Evaluation() { ResultType = enumResultType.B, Result=10},
new Evaluation() { ResultType = enumResultType.B, Result=11},
new Evaluation() { ResultType = enumResultType.C, Result=5}
}};
Stage Stage2 = new Stage() { Name = "Stage2",
MyEvaluations = new List<Evaluation>() {
new Evaluation() { ResultType = enumResultType.A, Result=10},
new Evaluation() { ResultType = enumResultType.B, Result=20},
new Evaluation() { ResultType = enumResultType.C, Result=20}}};
ParentList.Add(Stage1);
ParentList.Add(Stage2);
What I want to be able to do, via LINQ, is to select from the Parentlist object, all the items with only a filtered list where the ResultType in the Evaluations List matches a proper condition...
I don't want to repeat the parent object multiple times (seen selectmany), but rather a filtered down list of the MyEvaluations where the ResultType matches, and if this list has items (it would) return it with the parent.
I've played with:
ParentList.Select(x => x.MyEvaluations.FindAll(y => y.ResultType==enumResultType.B)).ToList();
however this returns only the inner list... whereas
ParentList.Where(x => x.MyEvaluations.Any(y => y.ResultType==enumResultType.B)).ToList();
returns ANY.. however I am missing how to get the list of MyEvaluations to be filtered down..
In my Example/sample data, I would like to query ParentList for all situations where ResultType = enumResultType.B;
So I would expect to get back a list of the same type, but without "Evaluation" which are equal to ResultType.A or .C
Based on dummy data, I would expect to be getting something which would have:
returnList.Count() - 2 items (Stage1 / Stage2) and within that Stage1 --> foreach (item.Result : 10, 11 Stage2 --> foreach (item.Result : 20
Can this be done without going to projections in new anonymous types as I would like to keep the list nice and clean as used later on in DataBinding and I iterate over many ResultTypes?
Feel like I'm missing something fairly simple, but fairly new to LINQ and lambda expressions.
Did you try these approaches already? Or is this not what you're looking for ?
//creating a new list
var answer = (from p in ParentList
select new Stage(){
Name = p.Name,
MyEvaluations = p.MyEvaluations.Where(e => e.ResultType == enumResultType.B).ToList()
}).ToList();
//in place replacement
ParentList.ForEach(p => p.MyEvaluations = p.MyEvaluations.Where(e => e.ResultType == enumResultType.B).ToList());

Dynamic Column Name in LinQ

I am having a class Item.
class Item{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
public string Name { get; set; }
public string Description { get; set;}
}
I want to filter list of items based on dynamic column name.
Suppose I want list of Names then Column Name is "Name" and result will be list of names
If column name is Description, I need list of descriptions.
How to do this with LinQ?
Easy, just select the property you need from the list:
var items = new List<Item>();
//get names
var names = items.Select(x => x.Name);
//get descriptions
var descriptions = items.Select(x => x.Description);
Update:
You'll need a bit of reflection to do this:
var names = items.Select(x => x.GetType().GetProperty("Name").GetValue(x));
Throw this in a method for re-usability:
public IEnumerable<object> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x => x.GetType().GetProperty(columnName).GetValue(x));
return values;
}
Of course this doesn't validate wether the column exists in the object. So it will throw a NullReferenceException when it doesn't. It returns an IEnumerable<object>, so you'll have to call ToString() on each object afterwards to get the value or call the ToString() in the query right after GetValue(x):
public IEnumerable<string> GetColumn(List<Item> items, string columnName)
{
var values = items.Select(x => x.GetType().GetProperty(columnName).GetValue(x).ToString());
return values;
}
Usage:
var items = new List<Item>(); //fill it up
var result = GetColumn(items, "Name");

The entity or complex type 'AdventureWorks2012Model.Product' cannot be constructed in a LINQ to Entities query

As you can see, I got this error when I built Data Gird using Kendo UI. Does anybody could point out where I'm wrong in my code below.
private IEnumerable<Product> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.Select(p => new Product
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
}).ToList();
return subcate;
}
Error:
The entity or complex type 'AdventureWorks2012Model.Product' cannot be constructed in a LINQ to Entities query.
Thank you so much for your time!
Since Product is an entity of model, you are creating new object of this entity while selecting the records, which is NOT good idea, I am NOT sure how model will handle this kind of behaviour that is why it is preventing you to do so, (I guess). Anyway you can change the code to this,
private IEnumerable<Product> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.ToList();
return subcate;
}
BTW your function name indicating that you are missing a Where clause.
Also you can create some custom DTO class and use it instead.
E.g.
class ProductDTO
{
public int ProductID { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public decimal ListPrice { get; set; }
}
private IEnumerable<ProductDTO> GetSubProduct()
{
var context = new AdvenDBEntities();
var subcate = context.Products.Select(p => new ProductDTO
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
}).ToList();
return subcate;
}
The first bad smell code I can point out for you. DBContext implements IDisposable so you are responsible for calling Dispose on it. In all, but one case in here, using block
You must build query to get all the product and then extract from it.
private IEnumerable<Product> GetSubProduct()
{
using (var context = new AdvenDBEntities())
{
// Get all constructed type product and then select from it
var subcate = context.Products
.ToList()
.Select(p => new Product
{
ProductID = p.ProductID,
Name = p.Name,
Color = p.Color,
ListPrice = p.ListPrice,
});
return subcate;
}
}

Problem getting same result from DataContext.Translate

This question is a follow up to this question:
How to create a list from two values
Consider this code:
class MainClass()
{
string MainKey {get;set;}
string MainName {get;set;}
IEnumerable<SmallObject> MainList {get;set}
}
class SmallObject()
{
string SmallKey {get;set}
}
and:
var mainQuery = (from v from DataContext.myTable
select v);
var myQuery = (from v in mainQuery
select new MainClass()
{
MainKey = v.Field1,
MainName = v.Field2,
MainList = new []
{
new SmallObject { SmallKey = v.Field3 },
new SmallObject { SmallKey = v.Field4 },
}
});
var result1 = myQuery.ToList();
//Changing datatypes for optimization reasons in SQLServer2000
var cmd = DataContext.GetCommand(myQuery);
foreach (System.Data.Common.DbParameter param in cmd.Parameters)
{
// nvarchar -> varchar
// decimal -> numeric
}
var result2 = DataContext.Translate<MainClass>(cmd.ExecuteReader()).ToList();
result1.MainList is OK
result2.MainList is null
The original query was very slow running on SQLServer2000, and I got it fixed when changing datatypes (Linq uses nvarchar and decimal, as my database use varchar and numeric)
So I want result2 to be the same as result1, but that doesn't happen when doing a DataContext.Translate like this.
Any thoughts of getting the same result here?
I've also tryed anonymous types, like this:
IEnumerable<object> MainList {get;set;}
...
MainList = new []
{
new { SmallKey = v.Field3},
new { SmallKey = v.Field4},
}
but the result is the same:
I think you are asking too much from Translate.
If I understand you correctly, it is the first query (mainQuery) that is too slow, so I would look to replace it.
I would create a simpler temporary class like
public class TmpClass
{
public string Field1 {get;set;}
public string Field2 {get;set;}
public string Field3 {get;set;}
public string Field4 {get;set;}
}
Once the list is in this format, you can use the second query to change it to a list of MainClass.
Just a matter of interest, what is the difference between the sql outputted by Linq and your customized version? Unless it is does some casting, I would not expect this type of query to need optimizing.
I would use the AsEnumerable extension method which basically converts the IQueryable to an IEnumerable which forces the enumerator to be processed. You could achieve the same thing by calling ToArray() or ToList() but AsEnumerable() magically lets you return it back to an IQueryable by calling AsQueryable()
So probably doing the following will work for you:
var result1 = DataContext.myTable.AsEnumerable()
.Select(v=> new MainClass {
MainKey = v.Field1,
MainName = v.Field2,
MainList = new []
{
new SmallObject { SmallKey = v.Field3 },
new SmallObject { SmallKey = v.Field4 },
}
});

Resources