Sort a List with OrderBy - c#

I have the following model
public class UserViewModel
{
public String CVR_Nummer { get; set; }
public DateTime LastActivityDate { get; set; }
public DateTime CreationDate { get; set; }
public String FirmaNavn { get; set; }
public int ProcentAnswered { get; set; }
}
I create a List<UserViewModel> and try to sort it:
userviewmodel.OrderBy(x => x.ProcentAnswered);
It compiles, but the list doesnt get sorted. Howcome?

LINQ is side-effect free by design so it won't change the input. Also it uses lazy execution, it won't do anything till you try to reach the data. Re-assign the output to the list and it should work. Notice that I'm using ToList() method here because OrderBy() returns IEnumerable<UserViewModel> and it's not evaluated till we try to get the items in it. We create a new list from this sequence using ToList() method and forcing the query to execute.
userviewmodel = userviewmodel.OrderBy(x => x.ProcentAnswered).ToList();

Linq queries are executed lazily, which means that they do not execute until the source object is enumerated. Moreover, the query never operates on the actual object in place, but returns a new instance.
To force execution on the spot., you must explicitly call .ToList() after creating the query:
userviewmodel = userviewmodel.OrderBy(x => x.ProcentAnswered).ToList();

In this userviewmodel.OrderBy(x => x.ProcentAnswered); you have just prepared the query, you even didn't sorted your list.
In this userviewmodel.OrderBy(x => x.ProcentAnswered).ToList(); you have fired the query, and have sorted list, but above expression will give you a fresh list.
So you will need to do this
userviewmodel = userviewmodel.OrderBy(x => x.ProcentAnswered).ToList();
as suggested by everyone.
Hope this works for you.

userviewmodel.OrderBy(x => x.ProcentAnswered);
That's an expression, not an instruction to modify something. The value of the expression is your sorted list but you are not capturing it in a variable, and LINQ expressions are lazy-evaluated, so because you do not capture the result thereby forcing it to resolve, this statement has no effect whatsoever.

you can try
userviewmodel = userviewmodel.OrderBy(x => x.ProcentAnswered).ToList();

Related

C# linq Select objects in list looking inside another list of objects

I have a class like this:
public class MailMessage
{
public string From {get; set; };
public string To {get; set; };
public ICollection<Attachment> Attachments { get; set; }
}
public class Attachment
{
public string Name {get; set;}
public byte[] Content {get;set;}
}
I would like to get all attachments in Attachments collection whose name ends with .pdf.
I tried the following:
List<MailMessage> list = new List<MailMessage>();
List<attachment> pdfAttachmentsCollection = list.Where(x => x.Attachments
.Where(attachment =>
attachment.Name.EndsWith(".pdf")).Select(y => y.Content));
However, this doesnt work. Any suggestions. It gives error:
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable ' to 'bool'
A couple issues:
You need to SelectMany from the MailMessage object to get all attachments in a single collection
You're selecting y.Content which is byte[] but trying to get a list of Attachment
LINQ returns IEnumerables, not Lists
Rewritten, you probably want to do something like:
List<MailMessage> list = new List<MailMessage>();
IEnumerable<Attachment> pdfAttachmentsCollection =
list.SelectMany(message =>
message.Attachments
.Where(attachment => attachment.Name.EndsWith(".pdf")));
This is also a good candidate to write as a query expression:
IEnumerable<Attachment> pdfAttachmentsCollection =
from message in list
from attachment in message.Attachments
where attachment.Name.EndsWith(".pdf")
select attachment;
Both of these do the same thing, just with different syntax.
If you want this as a List<Attachment> you can also do .ToList() at the end of either query. As an IEnumerable, it won't be evaluated until you start iterating through it (and depending on how you use it, you might never have the full collection in memory at any time). However, if you do multiple iterations or operations on it, it'll cause it to be evaluated multiple times -- so in those cases .ToList() makes sense for sure.

Issue with Filtering Entity Framework data by Calculated Fields

Here's a simplified EF Scenerio of my issue:
public partial class MyClass
{
public int ID { get; set; }
public byte Month { get; set; }
public int Year { get; set; }
public DateTime CalculatedDate
{
get
{
return new DateTime(this.Year, this.Month, 1);
}
}
}
I'm using a repository pattern to access these objects, which is also implementing a Where(predicate) method that returns an IEnumrable, just like LINQ. it is being used like this:
var myClasses = myClassRepo.Where(mc=> mc.ID > 10);
this works well and returns the expected objects with all fields, including CalculatedDate.
HOWEVER, when i try to use the calculated field as part of the predicate like this:
var myClasses = myClassRepo.Where(mc=> mc.CalculatedDate == DateTime.Now);
I Receive an error:
Object reference not set to an instance of an object.
I know I can "walkaround" this by retrieving a first set of results, and then filtering it further by the calculated field. but I'm trying to understand why this is happening and what could be done to fix this.
Entity Framework tries to convert your LINQ to SQL, so it has trouble with trying to convert your MyClass.CalculatedDate method into something recognizable in SQL. You may be able to get around this by adding an .AsEnumerable() call in your LINQ before your where, like so:
var myClasses = myClassRepo.AsEnumerable().Where(mc => mc.CalculatedDate == DateTime.Now);
I think it's because EF doesn't support querying on custom properties, as they cannot be translated into a DB column

LINQ Update or Insert objects into List

Ok, so I have a List that contains a collection of message objects. An updated list of message objects comes in every 60 seconds. Some of the objects in the first collection will have updated data based on an ID property inside each object.
public class Message
{
public Int64 Id { get; set; }
public string Property1 { get; set; }
public DateTime MessageDate { get; set; }
}
How in LINQ can I Insert in the updated object based on the Id?
You need to get a reference to the object Message.
Something like this:
this.Messages.ForEach(mess => {
if(mess.Id == someValue){
// do something here
}
})
List<Message> LocalList;
List<Message> ArrivingList;
var mergedItems = LocalList.Contcat(ArrivingList);
mergedItems = (from msg in mergedItems
group msg by msg.Id into grp
let sameKey = mergedItems.Where(obj => obj.Id == grp.Id)
select sameKey.Where(obj => obj.MessageDate == grp.Max(obj2 => obj2.MessageDate)).Single()).ToList();
LocalList = (List<Message>)mergedItems;
I think something similar to the above would probably "work" but I would just use a standard Dictionary /List and write a small updating routine. Just because you CAN do something with a particular tool does not mean you SHOULD.
For me LINQ based stuff can be MUCH harder to troubleshoot, understand, debug, trace, evaluate, etc.
(Replace LocalList and ArrivingList in the above example with whatever your actual variable are)

Casting issue in LINQ (C# .NET 4)

I have come across a very confusing problem that I hope someone can help me with. In my application I have the following data structures:
public struct EntityDetails
{
public string EntityName { get; set; }
public List<AttributeDetails> Attributes { get; set; }
public bool EntityExists { get; set; }
}
public struct AttributeDetails
{
public string AttributeName { get; set; }
public string DisplayName { get; set; }
public string DataType { get; set; }
public string Description { get; set; }
public bool AttributeExists { get; set; }
}
I instantiate the object with the following:
public static List<EntityDetails> entityList { get; set; }
So, what I need to do is to be able to return a filtered list of attributes based on an entity name and an attribute name. To do this I wrote the following piece of LINQ:
public static List<AttributeDetails> GetFilteredAttributeList(string pEntityName, string pAttributeFilter)
{
return (List<AttributeDetails>)entityList.Where(e => e.EntityName == pEntityName)
.Select(e => e.Attributes
.Where (a => a.AttributeName
.Contains (pAttributeFilter)));
}
Initially, when I did this I didn't have the cast at the start, but it brought up a compile time error, so I added the cast to allow it to compile. However, when it gets to this method I get the following message:
{"Unable to cast object of type 'WhereSelectListIterator2[MPYA.BU.CRMClientUpdateTool.CRMAccess.CRMAccessLayer+EntityDetails,System.Collections.Generic.IEnumerable1[MPYA.BU.CRMClientUpdateTool.CRMAccess.CRMAccessLayer+AttributeDetails]]' to type 'System.Collections.Generic.List`1[MPYA.BU.CRMClientUpdateTool.CRMAccess.CRMAccessLayer+AttributeDetails]'."}
Now, from the research I've done it would be appear that one is of type IEnumerable and the other is list, which I understand, but I can't for the life of me work out how to cast it so it is acceptable! I've also tried ToList(), casting it through the extension methods and various other things. I've also confirmed the data structure contains the correct data.
Any help would be appreciated.
UPDATE
Apologies, but for some reason I can't reply to answers for 8 hrs sigh. I have followed the advice of everyone to use ToList and now I get the following error:
Thanks for the answers so far. In my mind ToList() was the only logical way to go, but when I do that I get the following compile-time error:
error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.IEnumerable<MPYA.BU.CRMClientUpdateTool.CRMAccess.CRMAccessLayer.AttributeDetails>>' to 'System.Collections.Generic.List<MPYA.BU.CRMClientUpdateTool.CRMAccess.CRMAccessLayer.AttributeDetails>'
The actual error message when I hover over it is "System.ArgumentNullException".
You can simply end your LINQ query with a call to .ToList() to convert the results to a List<T>.
One thing to keep in mind is that calling .ToList() on a LINQ query "realizes" it, meaning that execution is no longer deferred;the results are now stored in memory in your List<T>.
In addition, I believe you want to use the .SelectMany() clause, instead of .Select(). e.Attributes is a List<AttributeDetails>. If you use Select(), it will create an IEnumarable<list<AttributeDetails>>, with each element being the attributes from one of your entities. SelectMany will combine the returned lists and return an IEnumerable<AttributeDetails>, which appears to be what you want.
Ultimately, you want to use the following:
return entityList.Where(e => e.EntityName == pEntityName)
.SelectMany(e => e.Attributes
.Where (a => a.AttributeName
.Contains(pAttributeFilter)))
.ToList();
Use this code:
return entityList.Where(e => e.EntityName == pEntityName)
.Select(e => e.Attributes
.Where (a => a.AttributeName
.Contains (pAttributeFilter))).ToList()
LINQ returns IEnumerable which is not a list, so you cannot cast this object to list. When you call ToList() the linq query will be executed and converted to list
You need SelectMany to pull out the attributes into a single list. You also need ToList() to convert the result to a List(). The cast will be unnecessary.
Try
return entityList.Where(e => e.EntityName == pEntityName)
.SelectMany(e => e.Attributes
.Where (a => a.AttributeName
.Contains (pAttributeFilter)))
.ToList();
Given that your Attributes are in fact instances of AttributeDetails, then you can call .ToList() at the end of your query. However, another appropriate way could be for you to change the signature of the method to return an IEnumerable<AttributeDetails>, depending on what functionality the caller is expecting.
In fact, the caller can still transform the query results at their leisure if they need to do so, where you can reliably return a further readable and queryable collection. So...
public static IEnumerable<AttributeDetails> GetFilteredAttributeList(
string pEntityName, string pAttributeFilter) {
return entityList
.Where(e => e.EntityName == pEntityName)
.Select(e => e.Attributes
.Where (a => a.AttributeName.Contains (pAttributeFilter)
)
);
}
public static List<AttributeDetails> GetFilteredAttributeList(string pEntityName, string pAttributeFilter)
{
var entityList = new List<EntityDetails>(); //added just to makte it compile
var filtered =
from entity in entityList
where entity.EntityName == pEntityName
from attribute in entity.Attributes
where attribute.AttributeName.Contains(pAttributeFilter)
select attribute;
return filtered.ToList();
}

How to sort on the Value of an member of a List<> within a parent List<>

I have a List sort question. I am using c# 3.0 and a generic List structure like this:
public class myObject
{
public int ID { get; set; }
public List<mySetting> setting { get; set; }
}
public class mySetting
{
public int ID { get; set; }
public string Name { get; set; }
public string Value { get; set; } // sort on this!
}
with this structure, I am filling a List of myObject with a LINQ query.
List<myObject> lmo = new List<myObject>();
lmo.SomeFillOperation():
What I want to do now is sort the entire List<myObject> on the individual <mySetting>[].Value values. EDIT: ( So this would be sorting on one keyed index of , for example mySetting[3].Value). I realize I could possibly do it in my SomeFillOperation(), but I want to do it after the List is formed.
Is there a recommended or easy way to do this? Is there a good example you have seen? Thanks in advance!
Well, List<T> already has a Sort method if you want to sort it in place - or you could use LINQ's OrderBy method. OrderBy is slightly easier than Sort:
var sorted = lmo.OrderBy(x => x.Value);
but even Sort isn't too bad:
lmo.Sort((x, y) => x.Value.CompareTo(y.Value));
EDIT: Having read the comment to the question, I no longer understand the question! Leaving this answer here as a potentially useful placeholder while I have dinner...
int MyObjectComparison(MyObject x, MyObject y)
{
return x.setting[0].Value.CompareTo(y.setting[0].Value);
}
lmo.Sort(MyObjectComparison);
Of course, this assumes that you want to use the Value of the first element in setting (and that setting is guarunteed to have at least one element). Solution with less assumption will be forthcoming when more info is given.
Since you are using 3.0, use LINQ:
var newList = lmo.OrderBy(i => i.Value);
and
var newList = lmo.OrderByDescending(i => i.Value);

Categories