Linq/C# orderby in web api - c#

I'm a little lost here. I've tried multiple different methods for returning this list of names I have but I can't seem to return them in the correct alphabetical order. This is what I've got:
[HttpGet]
[Queryable(PageSize=150)]
public IQueryable<BoyName> GetBoyNames(string letter)
{
List<BoyName> names = Db.BoyNames.Where(c => c.Name.StartsWith(letter)).OrderBy(x => x.Name).ToList();
return names.AsQueryable();
}
And my model:
public partial class BoyName
{
public int Id { get; set; }
public string Name { get; set; }
public string Meaning { get; set; }
public string Origin { get; set; }
}
It is giving me names which is nice...but I can't seem to get them to display in order.

As pointed out in the comments above, calling AsQueryable() on your list object may not preserve the ordering of the list. Rather than converting the result of your LINQ query to a List and then calling AsQueryable, just return the result of your LINQ query since it is a valid return type for your method (IQueryable<BoyName>).
var names = Db.BoyNames
.Where(c => c.Name.StartsWith(letter))
.OrderBy(x => x.Name);
return names;

Related

Argument type IOrderedQueryable does not match the corresponding member type IOrderedEnumerable (LINQ to SQL)

I'm trying to run this LINQ-to-SQL query
var sourceFilters = await _context.Sources.Where(s => s.CreatedBy == userId).Select(s => new {
SourceId = s.Id,
SourceName = s.Name,
Models = s.Vehicles.Select(v => new VehicleKeys()
{
Model = v.Model,
Code = v.Code,
RegistrationNumber = v.RegistrationNumber
}).Distinct().OrderBy(k => k.Model).ThenBy(k => k.Code).ThenBy(k => k.RegistrationNumber)
}).ToListAsync()
s.Vehicles is a navigation property
But I get the following Exception:
Argument type 'System.Linq.IOrderedQueryable`1[MyProject.Infrastructure.Services.SourceService+VehicleKeys]' does not match the corresponding member type 'System.Linq.IOrderedEnumerable`1[MyProject.Infrastructure.Services.SourceService+VehicleKeys]'
I tried adding .ToList() after all of the OrderBy calls, but then the resulting sourceFilters.Models collection only has a single element (even if I add Include(s=>s.Vehicles) to _context.Sources)
I also tried turning anonymous type into a normal type SourceKeys, but if I define Models property as a IOrderedQueryable or IQueryable - I get a compile time error. If I define it as a IEnumerable or IOrderedEnumerable - I get an Argument types do not match exception
Another thing I attempted was adding AsQueryable(), right after s.Vehicles, after Distinct() or after all of the OrderBy clauses. In the first and second cases I get the following exception:
Argument type 'System.Linq.IQueryable`1[MyProject.Infrastructure.Services.SourceService+VehicleKeys]' does not match the corresponding member type 'System.Linq.IOrderedQueryable`1[MyProject.Infrastructure.Services.SourceService+VehicleKeys]'
In the third case I get:
Argument type 'System.Collections.Generic.List`1[MyProject.Infrastructure.Services.SourceService+VehicleKeys]' does not match the corresponding member type 'System.Linq.IQueryable`1[MyProject.Infrastructure.Services.SourceService+VehicleKeys]'
I've also tried both having a named type for the elements of the sourceFilters collection, and adding AsQueryable()
If I have Models be IOrderedQueryable<VehicleKeys> and put AsQueryable() after s.Vehicles or after Distinct() I get the Argument types do not match exception. If I put AsQueryable() after all of the OrderBy() clauses - I get a compile error.
If I have Models be IQueryable<VehicleKeys> I get Argument types do not match exception, no matter where I put AsQueryable().
At this point I have absolutely no ideas on what else to try. I can work around the problem by doing the ordering on the client-side, but I would prefer to things like ordering on the SQL Server, to leverage the benefits of indexation, and I also really want to understand what's the cause of the problem, so if similar problems were to pop up in the future I would be able to deal with them.
Edit:
Models:
public class Source {
public int Id { get; set; }
public string Name { get; set; }
public string CreatedBy { get; set; }
public virtual IEnumerable<Vehicle> Vehicles { get; set; }
}
public class Vehicle {
public int Id { get; set; }
public string Model { get; set; }
public string Code { get; set; }
public string RegistrationNumber { get; set; }
public int? SourceId { get; set; }
public Source Source { get; set; }
//some other properties
}
{Model, Code, RegistrationNumber, SourceId, /* some other properties*/} have an index and an unique constraint
private class VehicleKeys {
public string Model { get; set; }
public string Code { get; set; }
public string RegistrationNumber { get; set; }
}
I would suggest to split this query to two parts. One should execute Eager Loading query, second post process records on the client side:
var rawData = await _context.Sources
.Where(s => s.CreatedBy == userId)
.Select(s => new
{
SourceId = s.Id,
SourceName = s.Name,
Models = s.Vehicles
.Select(v => new
{
Model = v.Model,
Code = v.Code,
RegistrationNumber = v.RegistrationNumber
}).ToList()
})
.ToListAsync();
var sourceFilters = rawData
.Select(s => new
{
s.SourceId,
s.SourceName,
Models = s.Models
.Distinct()
.OrderBy(k => k.Model)
.ThenBy(k => k.Code)
.ThenBy(k => k.RegistrationNumber)
.Select(v => new VehicleKeys()
{
Model = v.Model,
Code = v.Code,
RegistrationNumber = v.RegistrationNumber
}).ToList()
})
.ToList();

Perform a query over a nested collection in ElasticSearch with NEST in .NET Core

I am trying to perform a search over an index for the following object:
public class IndexedElement
{
public Guid Id { get; set; }
public long RowId { get; set; }
public IndexedElementType Type { get; set; }
public string Summary { get; set; }
public string Description { get; set; }
public IList<string> Tags { get; set; }
}
The purpose is to search by the Summary property, or by matching any of the strings inside the Tags collections
What I currently have is this:
public IEnumerable<IndexedElement> Search(string description)
{
var query = GetClient().Search<IndexedElement>(s => s.From(0).Size(5)
.Query(
q => q.Term(p => p.Summary, description)
||
q.Nested(n => n.Path(p => p.Tags).Query(q2 => q2.Terms(t => t.Field(f => f.Tags).Terms(description))))
));
return query.Documents.ToList();
}
But the Nested part is not working, I don't know if I am using it in the proper way or maybe I have to find another solution for that.
Any ideas?
Thank you all in advance
You don't need to perform a nested query to query the Tags field, as each tag is only a primitive JSON value i.e. a string. Just the terms query will suffice.
Where a nested query would be needed is where Tags is a POCO with multiple properties and is mapped as a nested datatype.

Group By is not aggregating

I am aggregating data that I retrieve from multiple identical web services. The same row count and data points are returned with only a variance in the Value. The GroupBy clause I am using is not condensing any of the rows. I have the same row count before and after the GroupBy.
MyWebServiceUrls
.AsParallel()
.SelectMany(url => GetMetricItemData(url))
.GroupBy(item => new { item.DateTime, item.Group, item.Metric }, item => item.Value)
.Select(grp => new MetricItem()
{
DateTime = grp.Key.DateTime,
Group = grp.Key.Group,
Metric = grp.Key.Metric,
Value = // type = decimal?
grp.Any(mi => mi.HasValue)
? grp.Key.Metric.AggregationType == Metric.MetricAggregationTypes.Sum
? grp.Sum(mi => mi.Value)
: grp.Average(mi => mi)
: null
})
.AsEnumerable();
The syntax looks correct based on other examples I have found.
I send this data back to my database and can aggregate with the statement GROUP BY [DateTime], [Group], [Metric] and everything works great. While I can use the database to solve this issue, I would like to know how to correctly use LINQ in this instance.
What am I missing to get this LINQ expression to work?
UPDATE:
This is the relevant MetricItem and Metric class definition:
public class MetricItem
{
public string Group { get; set; }
public DateTime DateTime { get; set; }
public Metric Metric { get; set; }
public Decimal? Value { get; set; }
}
public class Metric
{
public string Code { get; set; }
public string Label { get; set; }
private List<string> SumMetrics = new List<string>(new string[] { "TPI", "TPO", "TPIO" });
public enum MetricAggregationTypes { Sum, Average };
public MetricAggregationTypes AggregationType
{
get
{
if (SumMetrics.IndexOf(this.Code) >= 0)
return MetricAggregationTypes.Sum;
else
return MetricAggregationTypes.Average;
}
}
}
You need to override Equals and GetHashCode on the Metric class. Most Linq methods use hash codes for comparison operations, so for most objects you define yourself, you need to override this class if you plan to use something like GroupBy, Union, etc.

Remove duplication's from my DropDownList

In my controller i am returning list of my object with specific property:
public ActionResult Index()
{
List<MyObject> list = db.MyObjects.Where(x => x.family == "Web").ToList();
ViewBag.Files = new SelectList(list, "Id", "protocol");
return View();
}
This is my object:
public class MyObject
{
public int id { get; set; }
public string fileName { get; set; }
public string browser { get; set; }
public string protocol { get; set; }
public string family { get; set; }
}
Index.cshtml:
#Html.DropDownList("File", new SelectList(ViewBag.Files, "Id", "protocol_site"), "Select webmail site", new { style = "vertical-align:middle;" })
And i try to made 2 changes with no succeed:
Remove all the duplication protocol from my DropDownListfor exapmle if i have 10 objects : 9 is doc protocol and 1 pdf i wand to see in my DropDownList only 2 items: DOC and PDF and not all the 10 items.
Sort this DropDownList in alphabet order
As #Dreamcatcher mentioned in his answer, you should pass already prepared collection to SelectList constructor and use linq for these tasks. For Distinct linq method you will need to create custom comparer, which will compare objects by protocol field:
public sealed class MyObjectByProtocolComparer: IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.protocol.Equals(y.protocol);
}
public int GetHashCode(MyObject obj)
{
return obj.protocol.GetHashCode();
}
}
This is rather simple implementation, you might need to check for null values. After that use it in your linq query:
var list = db.MyObjects.Where(x => x.family == "Web").ToArray();
list = list.Distinct(new MyObjectByProtocolComparer())
.OrderBy(x => x.fileName)
.ToArray();
You should add second line in your code. However i am not sure correct spelling, i did not use VS. Also if Disctinct does not work correctly, you should write Comparer.
List<MyObject> list = db.MyObjects.Where(x => x.family == "Web").ToList();
list= list.OrderBy(x => x.fileName).Distinct().ToList();
Follow the guidelines in this page http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx so that you can call linq Distinct

Convert IGrouping from one class to another using linq

I have two classes Teams and PlayerTeams
public class PlayerTeams
{
public int ID { get; set; }
public string PlayerName { get; set; }
public string PlayerCountry { get; set; }
public bool IsActive { get; set; }
public string PlayerTeam { get; set; }
}
public class Players
{
public int ID { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public bool? Status { get; set; }
}
I have a list of PlayerTeams which is grouped by PlayerTeam like this.
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy( x => x.PlayerTeam)
.ToList();
Its of type List<IGrouping<string, PlayerTeams>> but I want it to be of type List<IGrouping<string, Players>> as I do not want the redundant key information on every row.
How could I possibly achieve that? I could only think of something like .ConvertAll() on the IGrouping. I am not able to make it also.
Is there an efiicient way to do this?
If you can change the grouping, I'd just use:
var groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.ToList();
Where Players.FromPlayerTeam is a static method in Players which takes a PlayerTeam and returns a Players.
Additionally, I'd suggest using ToLookup instead of GroupBy - a Lookup is exactly what you want here, without bothering with the ToList call.
This not testet, just an idea.
If you have trouble converting your linq statement, which is expecting the IGrouping type, to a string list, then you might have to select it before.
var groupedPlayers = new List<string>();
groupedPlayers = teams
.OrderBy(x => x.PlayerName)
.GroupBy(x => x.PlayerTeam, Players.FromPlayerTeam)
.Select(x => x.Key) // << added select
.ToList();

Categories

Resources