Any idea why the LINQ OrderBy is not working in following code, (have no errors but method does not sort ...)
First my own type
public class IQLinksView
{
public int id { get; set; }
public int catid { get; set; }
public int? viewed {get;set;}
public string name {get;set;}
public string desc {get;set;}
public string url {get;set;}
public string pic {get;set;}
public string cat {get;set;}
}
then query :
IQueryable<IQLinksView> newView =
from links in this.emContext.tbl_otherlinks
select new IQLinksView { id = links.pklinkid, catid =
links.tbl_catgeory.pkcategoryid, viewed = links.linkviewed, name = links.linkname,
desc = links.linkdesc, pic = links.linkpicture, url = links.linkurl, cat =
links.tbl_catgeory.categoryname };
Untill here all fine :-), but then
newView.OrderBy(x => x.viewed);
just changes nothing,... Page is loading results showing ... but no ordering ... sniff
i have Try with (creating a comparer object ... ):
newView.OrderBy(x => (Int32)x.viewed, new CompareIntegers());
same result, no ordering ...
I do have workarounds but just wondering what is missing ....
Any suggestions will be appreciated thanks a lot :-)
Don't throw away the return value. The OrderBy extension method is does not mutate the input. Try:
newView = newView.OrderBy(x => x.viewed);
There is no reason why that won't work, assuming the viewed value is correct. Also, make sure that OrderBy is after any operations (e.g. Distinct) which will ruin ordering.
Happy coding!
No-Tracking Queries
Consider use the asnotracking() after orderby() if the result is a readonly result.
Example:
query = query.OrderByDescending(x => x.Rating).AsNoTracking();
Related
Here is sample code to reproduce the exception:
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore;
namespace Demo
{
[Keyless]
public class Contact
{
public string Name { get; set; } = default!;
public string? Address { get; set; } = default!;
public int? CCode { get; set; } = 0;
public int OtherValue { get; set; } = 0;
}
public class Foo
{
public static void Main()
{
List<Contact> raw = new();
raw.Add(new Contact { CCode = 1, Name = "Foo", Address = "Bar" });
raw.Add(new Contact { CCode = 2, Name = "Foo", Address = "Bar" });
ProcessRawResults(raw);
}
public static void ProcessRawResults(List<Contact> raw)
{
var q = from i in raw
group i by new { i.CCode, i.Name, i.Address } into g
orderby g.Key
select g;
foreach (var group in q)
{
}
}
}
}
When executing this program, an exception is thrown when execution reaches foreach (var group in q):
System.InvalidOperationException: 'Failed to compare two elements in the array.'
Inner Exception
ArgumentException: At least one object must implement IComparable
I have looked at other SO questions about this error message occurring when trying to Sort a List; but in this code I'm not sure which operation needs the comparator. It seems like the orderby g.Key operation might need to compare the anonymous class in the group, but then shouldn't the anon class have a default comparator? Or if it doesn't, I'm not sure where to put the code to implement this.
Confusingly, if I take i.CCode out of the group i by new line, then the exception doesn't happen any more.
Background: My real project is a Blazor app using EFCore 6 , and am receiving a List<Contact> from a Stored Procedure result, so it has to be [Keyless]. I have to work with the existing Stored Procedure unmodified, so am performing a transformation of the result in my code. I hope to collapse the set of results so that all entries with the same (CCode, Name, Address) result in a single row, and I'll concatenate the OtherValue into a list within that single row.
I guess it's because int? is actually Nullable<int> and Nullable<T> doesn't implement IComparable. I just tested your code but changed the grouping to this:
group i by new { CCode = i.CCode.HasValue ? i.CCode.Value : (int?)null, i.Name, i.Address } into g
and it seemed to work. It didn't throw that exception, at least.
Anonymous type do not have comparator, specify oder by properties:
var q = from i in raw
group i by new { i.CCode, i.Name, i.Address } into g
orderby g.Key.CCode, g.Key.Name, g.Key.Address
select g;
here is code illustration
interface IObjectA
{
int Id { get; }
string Name { get; }
}
class ObjectA : IObjectA
{
public int Id { get; set; }
public string Name { get; set; }
public ObjectA(int id, string name)
{
Id = id;
Name = name;
}
}
There are two ways for me to generate List<IObjectA> from some other objects
First one is using forloop:
IList<IObjectA> list = new List<IObjectA>();
foreach(var item in someList)
{
list.Add(new ObjectA(item.Id, item.Name));
}
This works perfectly fine.
Then I tried with linq
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
The compiler will throw me a error basically saying cannot convert ObjectA to IObjectA
To make it work, i have to add
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
Can some one explain why the compile would complain?
Thanks in advance!
The problem is that the linq expressions result in a List<ObjectA>. If you can treat this result as a List<IObjectA>, the compiler might let you add hypothetical OtherObjectA objects to the list, which would blow up on you if you ever tried to cast back to the original List<ObjectA> type, which should be allowed.
To get around this, you can .Cast() the elements before calling .ToList() to get a list of the correct type:
IList<IObjectA> list = someList.Select(c => new ObjectA(c.Id, c.Name)).Cast<IObjectA>().ToList();
You could also use the var keyword:
var list = someList.Select(c => new ObjectA(c.Id, c.Name)).ToList();
But this will still result in a List<ObjectA> and I suspect you need the List<IObjectA> for code further on.
So I am new to C#, LINQ, and MVC. I am trying to get a list of Ages, but it says
The specified type member 'Age' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties
are supported.
For a previous tutorial, they use this exact same logic, except they check a string, not an int (Age). Why is this giving me a fit, and how can I fix it?
public ActionResult SearchIndex(string ageValue, string searchString)
{
if (!string.IsNullOrEmpty(ageValue))
{
var AgeList = new List<string>();
var AgeListQry = from d in db.Actors orderby d.Age select d.Age.ToString();
AgeList.AddRange(AgeListQry.Distinct());
}
// other stuff
}
I want to learn what is going on, so that I can avoid this in the future!
Entity Model code
public class Actor
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public int Age
{
get {
return (int)(DateTime.Now - BirthDate).TotalDays / 365;
}
}
public decimal NetValue { get; set; }
}
public class ActorDBContext : DbContext
{
public DbSet<Actor> Actors { get; set; }
}
As mentioned in the comments, you can't call ToString() in a Linq to Entities query. Instead do it like this:
var AgeList = new List<string>();
//retrieve as whatever type Age is, no conversion in SQL Server
var AgeListQry = (from d in db.Actors orderby d.Age select d.Age).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(a => a.ToString()).Distinct());
EDIT
I saw your latest update that does show that Age is not a database column. You are then required to do something like this (assuming BirthDate is properly mapped):
var AgeList = new List<string>();
//retrieve BirthDate from SQL Server and use ToList() to get it to run immediately
var AgeListQry = (from d in db.Actors orderby d.BirthDate select d.BirthDate).ToList();
//convert them after the fact, using Linq to Objects
AgeList.AddRange(AgeListQry.Select(bd => ((int)(DateTime.Now - bd).TotalDays / 365).ToString()).Distinct());
Linq to Entities maps your expressions to SQL statements and there is nothing for it to map to when you use your Age property. Instead, you need to get what you can from SQL Server (BirthDate) and then do the translation to Age yourself. You could replace the inline code with a method call like this if you'd rather:
AgeList.AddRange(AgeListQry.Select(bd => CalculateAge(bd)).Distinct());
//...
private string CalculateAge(DateTime birthday)
{
return ((int)(DateTime.Now - bd).TotalDays / 365).ToString();
}
You haven't the Age in you DB scheme and it is impossible to convert LINQ to DB query.
You must order the Age collection in client side or add calculated column to your table.
There is another way. Have a converter file, where you pass the object, works with the birthdate and produces the age, returns the same object. That also means, that you can't search the database for the age column
I have two generic Lists, one called "Featured" and the other called "Filtered".
List<Content> Featured = new List<Content>();
List<Content> Filtered = new List<Content>();
Both contain "Content" items which are simple classes like so :
public class Content
{
public long ContentID { get; set;}
public string Title { get; set; }
public string Url { get; set; }
public string Image { get; set; }
public string Teaser { get; set; }
public Content(long contentId, string title, string url, string image, string teaser)
{
ContentID = contentId;
Title = title;
Url = url;
Image = image;
}
}
Any items that appear in "Filtered" but also appear in "Featured" need to be removed from "Filtered". Additionally, both lists will then be combined into a single generic list with the "Featured" items appearing first.
I know I could write a couple of foreach loops to do this but I can't help feel there must be a more elegant method using LINQ.
I am using C# 4.0.
If you have an IEqualityComparer defined you can use the Union method:
List<Content> FeaturedAndFiltered = Featured.Union(Filtered, new MyContentComparer());
A rough implementation of MyContentComparer would be:
public class ContentEqualityComparer : IEqualityComparer<Content>
{
public bool Equals(Content c1, Content c2)
{
return (c1.ContentID == c2.ContentID);
}
public int GetHashCode(Content c)
{
return c.ContentID.GetHashCode();
}
}
You're looking for the LINQ method Union, specifically
var singleList = Featured.Union(Filtered);
This will return all the Featured items, followed by all the Filtered items that were not Featured. Note that it will also remove any duplicates within a list - so if an item is Featured twice, it will only show up once.
You do, however, need to be able to compare instances of Content in order to do this, either by adding in implementations of Equal and GetHashCode or by providing an IEqualityComparer.
Assuming the objects presenter in both lists are the actual same objects, you can do the following:
var totalList = Featured
.Concat(Filtered.Where(f => !Featured.Contains(f)))
.ToList()
Update
Or, using the Except method as mentioned by Mahmoud Gamal:
var totalList = Featured
.Concat(Filtered.Except(Featured))
.ToList()
I'm faced with an error, "Cannot serialize interface System.Linq.IQueryable." when I try to run my method in my web service. My class is as such:
public class AirlineSearchStrong
{
public Flight_Schedule flightSchedule { get; set; }
public Flight_Schedule_Seats_and_Price flightScheduleAndPrices { get; set; }
public Airline airline { get; set; }
public Travel_Class_Capacity travelClassCapacity { get; set; }
}
[WebMethod]
public IQueryable SearchFlight(string dep_Date, string dep_Airport, string arr_Airport, int no_Of_Seats)
{
AirlineLinqDataContext db = new AirlineLinqDataContext();
var query = (from fs in db.Flight_Schedules
join fssp in db.Flight_Schedule_Seats_and_Prices on fs.flight_number equals fssp.flight_number
join al in db.Airlines on fs.airline_code equals al.airline_code
join altc in db.Travel_Class_Capacities on al.aircraft_type_code equals altc.aircraft_type_code
where fs.departure_date == Convert.ToDateTime(dep_Date)
where fs.origin_airport_code == dep_Airport
where fs.destination_airport_code == arr_Airport
where altc.seat_capacity - fssp.seats_taken >= no_Of_Seats
select new AirlineSearchStrong {
flightSchedule = fs,
flightScheduleAndPrices = fssp,
airline = al,
travelClassCapacity = altc
});
return query;
}
I've tried IQueryable, IList and returning .ToList() but most of it has turned out to be unsuccessful
i dont think
you can use Iqueryable or Ienumerable as they both do lazy execution and are not serializable. The query gets executed only when you iterate through the collection.so it doesn't make sense to return the query to the caller and asking him to iterate as his end.you need to pass a List or an Array.
You may need to change the return type to List<Type>
Hows about
public IEnumerable<AirlineSearchStrong> SearchFlight(string dep_Date, string dep_Airport, string arr_Airport, int no_Of_Seats)
{
...
return query.ToList();
}
Your trying to serialize a representation of the data, the linq query itself, instead of the data resulting from executing the query, thats why it isnt working.
You need to enumerate the linq query into an enumerable set, and serialize that.
AirlineSearchStrong might need to be marked [Serializable()]