EDIT:
Both queries in this example are not ordering by the location sort order. Does anyone have any suggestions on how to do this?
I need some help with ordering an entity collection of groups by a child entity column. I have a one to many relationship from groups to locations.
I need to get the groups ordered by the group name and then by the location sort order.
Since the child entity is a collection I am having trouble.
Here is code I am using that works but returns duplicates:
var groups = db.GetNavigationGroups()
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.SelectMany(g => g.NavigationGroupLocations, (g, l) => new { g, l })
.OrderBy(x => x.g.Name)
.ThenBy(x => x.l.SortOrder)
.Select(x => x.g);
Then I tried this approach using FirstOrDefault():
List<NavigationGroup> groups = db.DataModel.NavigationGroups
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.OrderBy(g => g.Name)
.ThenBy(g => g.NavigationGroupLocations.Where(l => l.Location == selectedLocation && l.GroupID == g.ID).OrderBy(l => l.SortOrder).FirstOrDefault())
.ToList();
The problem is that I cannot seem to get the groups in the order I need them to be in based on the locations SortOrder column.
The second query you have looks like you're trying to sort by the NavigationGroupLocations object instead of on it's fields. Have you tried:
List<NavigationGroup> groups = db.DataModel.NavigationGroups
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.OrderBy(g => g.Name)
.ThenBy(g => g.NavigationGroupLocations.Where(l => l.Location == selectedLocation && l.GroupID == g.ID).OrderBy(l => l.SortOrder).FirstOrDefault().SortOrder)
.ToList();
You might also want to add your selectedLocation condition to the order clause.
List<NavigationGroup> groups = db.DataModel.NavigationGroups
.Where(g => selectedLocation > 0 ? g.NavigationGroupLocations.Any(l => l.Location == selectedLocation) : true)
.OrderBy(g => g.Name)
.ThenBy(g => g.NavigationGroupLocations.Where(l => selectedLocation > 0 ? l.Location == selectedLocation : true && l.GroupID == g.ID).OrderBy(l => l.SortOrder).FirstOrDefault().SortOrder)
.ToList();
Related
I have the following query, which I wish to be distinct on "RegistrationNumber", and sorted by "DriverToLoad".
Distinct/grouping works fine. However, my query sorts by "RegistrationNumber" apparently ignoring the "OrderBy":
Drive = await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber)
.Select(g => g.OrderBy(m => m.DriverToLoad).First())
.ToListAsync(),
Any LINQ experts who can point out what the problem is, and how to solve it?
I'm working with .NET7.
Thanks a lot in advance.
I'm going to suggest you want something like this:
var Drive =
await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber, m => m.DriverToLoad)
.SelectMany(g => g.OrderBy(x => x).Take(1))
.ToListAsync();
Or like this:
var Drive =
await
(
from m in _db.Drive
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
where m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012
group m.DriverToLoad by m.RegistrationNumber into g
from d in g.OrderBy(x => x).Take(1)
select d
)
.ToListAsync();
I have solved it, but probably not the nicest way (...more like a "hack" I would say).
var drive = await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber)
.Select(g => g.OrderBy(m => m.DriverToLoad).First())
.ToListAsync();
DriveListViewModel model = new DriveListViewModel()
{
Drive = drive.OrderBy(a => a.DriverToLoad),
};
This way I get the sorting I wish, and can return that to the view.
Haven't yet seen the power of LINQ, so maybe I should aim for using ADO and SQL directly in my controllers, and somehow convert that to lists to be returned to my views ;-)
Thank you all for all your great suggestions. I really appreciate it.
In result of my select
var names = _db.Messages
.Where(p => p.UserId == parameters.UserId || p.ToUserId == parameters.UserId)
.Select(p => new {p.FirstUser, p.SecondUser})
.Distinct()
.ToList();
I get four elements of type List<User, User>.
As you can see I have one duplicates
[0] and [2]. Only in sequence FirstUser and SecondUser they have difference
How can I Exclude one of them in query before ToList() ? And it will be perfect if in result I will have List<User> with all of them without duplicates.
Looks that you don't care about who send the message and who received it. This is a solution for you:
var names =
_db.Messages
.Where(p => p.UserId == parameters.UserId || p.ToUserId == parameters.UserId)
.Select(p => new {
user1 = p.UserId > p.ToUserId ? p.FirstUser.Id : p.SecondUser.Id,
user2 = p.UserId <= p.ToUserId ? p.FirstUser.Id : p.SecondUser.Id,
})
.Distinct()
.ToList();
I am currently working with a schema which contains entities, each containing attributes and each of them contain one type specification. I want extract a list which fulfills a specific attribute requirement it being of the type lookup and a specific specification.
I have tried this so far
schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.SelectMany(x => x.Entities
.Where(x => x.Attributes
.Any(y => y.Type == DataType.Lookup &&
y.TypeSpecification.EntityInternalName == "Weather")));
This returns a list of entities which fulfills the requirement, but none of the entities has any attributes?
How do I also include this within the query?
I tried adding include at the end,
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Include(x=> x.Entities)
.ThenInclude(x=>x.Attributes)
.ThenInclude(x=>x.TypeSpecification)
.SelectMany(x => x.Entities
.Where(x => x.Attributes
.Any(y => y.Type == DataType.Lookup &&
y.TypeSpecification.EntityInternalName == "Weather")));
but this just include all attributes and etc. ignoring the Any condition for the attributes..
How to get around this?
I tried splitting it a bit
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Take(1)
.SelectMany(x => x.Entities
.Where(y => y.Attributes
.Any(z => z.Type == DataType.Lookup && z.AttributeTypeSpecification.EntityInternalName == lookupEntityName))).AsEnumerable();
IList<ReferenceView> output = new List<ReferenceView>();
foreach (var entity in entitiesWithLookupsTolookupEntityName)
{
var attribute = schemaRepository.Many().OrderByDescending(x => x.Version).First().Entities
.Where(x => x.InternalName == entity.InternalName);
}
but here I get an exception every in for loop
I'm struggling to transform this piece of code in a single LINQ expression.
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.FirstOrDefault();
if (x == null)
{
x = values
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value)
.FirstOrDefault();
}
Basically I have a list of objects. Each objects contains a list of Column objects and a Value.
I want to filter the object that contains a specific Column object (based on Code and Value), but if this combination does not exist, I want to fall back to the entity that contains a list of Column objects all having Code equals to string.Empty (a wild card).
I have tried different approches like the following but without success:
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value)
? v.Columns.Any(c => c.Code == code && c.Value == value)
: v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value)
.FirstOrDefault();
I suggest Concat both alternatives:
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.Concat(values // second alternative if 1st returns empty cursor
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value))
.FirstOrDefault();
Edit: You can simplify the query (see CSharpie's comment) by extracting .Select(v => v.Value) into
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Concat(values // second alternative if 1st returns empty cursor
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code)))
.Select(v => v.Value)
.FirstOrDefault();
You can use DefaultIfEmpty(fallback):
var fallBackValue = values
.Where(v => v.Columns.All(c => string.IsNullOrEmpty(c.Code))
.Select(v => v.Value)
.FirstOrDefault();
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.DefaultIfEmpty(fallBackValue)
.First(); // FirstOrDefault not nessesary anymore;
This has the advantage that you can even select multiple without breaking the logic, so the fallback value would still be returned if Take(3)(for example) would not return any items.
It is also efficient since the fallback value will be calculated independently of the main query and could be returned from a property, so that it needs to be initialized only once.
Another (similar option) is the null coalescing operator(if Value is a reference type):
var x = values
.Where(v => v.Columns.Any(c => c.Code == code && c.Value == value))
.Select(v => v.Value)
.FirstOrDefault() ?? fallBackValue;
But i'd prefer the first because it can be chained and also modified easily(i.e. Take(x)).
I do have a complex query to select a full object called Performance
The Performance relationship with others objects is:
Performance has a list of Index
Index has a list of SubIndex
SubIndex has a list of Indicator
Indicator has a list of Item
Item relationship with Spot and Measurement:
Item has one Spot and one Measurement
The query below returns exactly what I want, but I would like to include the Spot and Measurement to the Item object.
return _context.Performance.Include(i => i.Indexes
.Select(s => s.SubIndexes
.Select(d => d.Indicators
.Select(t => t.Items))))
.SingleOrDefault(p => p.Id == id);
I have tried the query below and it is returning the Measurement object. How to include the Spot object?
return _context.Performance.Include(i => i.Indexes
.Select(s => s.SubIndexes
.Select(d => d.Indicators
.Select(t => t.Items.Select(tm => tm.Measurement)))))
.SingleOrDefault(p => p.Id == id);
You could add second Include too;
return _context.Performance.Include(i => i.Indexes
.Select(s => s.SubIndexes
.Select(d => d.Indicators
.Select(t => t.Items.Select(tm => tm.Measurement)))))
.Include(i => i.Indexes
.Select(s => s.SubIndexes
.Select(d => d.Indicators
.Select(t => t.Items.Select(tm => tm.Spot)))))
.SingleOrDefault(p => p.Id == id);