I have 3 related entities with 1 to many relationships.
Account 1-* Collection 1-* items
Each of these has a many to many relationship with Users, (with join tables AccountManagers , CollectionManagers, ItemManagers)
I'm trying to get a list of Collections for a user if he's in any of the join tables.
I was thinking create 3 expressions and then merge the 3 results at the end and somehow remove duplicates.
It seemed like Lambda expressions are the way to go, but I'm still learning them.
I think the middle one is easier , like
db.Collections.where(C => C.CollectionManagers.UserID == CurrentUserID)
But how would you gather a list of collections from the users account manager and item manager entries?
Thanks in advance
Using LINQ, the Union operator will return only the unique Collection rows, so assembling each category and combining them should work.
For Items, I thought it would be most efficient to find all Items managed by the current user and then find all collections they belong to:
var iCollections = Items.Where(i => i.ItemManagers.Any(im => im.UserId == CurrentUserID)).SelectMany(i => Collections.Where(c => c.Items.Contains(i)));
It is also possible to do this the other way, e.g. find all Collections that contain an Item managed by the current user:
var iCollections = Collections.Where(c => c.Items.Any(i => i.ItemManagers.Any(im => im.UserId == CurrentUserID)));
For Collections, as you pointed out, you just need to find all collections where the current user manages the collection:
var cCollections = Collections.Where(c => c.CollectionManagers.Any(cm => cm.UserId == CurrentUserID));
For Accounts, we find all accounts managed by the current user and then all collections owned by the account:
var aCollections = Accounts.Where(a => a.AccountManagers.Any(am => am.UserId == CurrentUserID)).SelectMany(a => a.Collections);
Then you can Union the results together:
var CurrentUserCollections = iCollections.Union(cCollections).Union(aCollections);
Related
I am trying to retrieve a list of entities based on properties of their child entities - and include those same child entities. I am using EntityFramework Core 3.1, although I am happy to upgrade to 5.x if there've been any changes that would solve this for me. I have also not explored EntityFramework much beyond some very basic CRUD boilerplate until now, so I am not sure if this is something more LINQ-oriented or specific to EF (Core). The below is a heavily simplified example of a method in a project that I will be using to ultimately return data to a consumer of my API.
A point of interest (POI) has a number of historical records (History). POI has a List<History> and a History has a PointID which is used by EF Core to populate the POI's List<History>.
Here is how I would get all the POIs and their histories, where a point was first registered since a certain date (using a nullable date parameter for this method)
var result = _context.POIs
.Where(point => (registeredSince == null || point.RegisteredAt >= registeredSince))
.Include(point => point.Histories)
.ToList();
However, my question is.. how would I then get only POIs based on an attribute within the History of that POI (and include those same History records?) Or, to use an example; I want to return only POIs that have History records with an areaId == 5 (and include those in the results)
One way, without hugely in-depth EF knowledge, would be:
First run a query to return History entities where history.areaId == 5 and only select history.PointId
Second query would be to get all POIs where id is in the returned PointId list above
..including History where history.areaId == 5 (a duplication)
However, I would be running part of this twice, which seems inefficient. Basically, could I efficiently use LINQ/EF to get all POIs where history.areaId == 5 (and then only include those History records with an areaId of 5)? Would I have to write something that unavoidably loads all POI and their History records, before I am able to narrow the results down, or is that something EF can happily do?
You can use Filtered include introduced in EF Core 5.x, to query like -
var result = _context.POIs
.Include(p => p.Histories.Where(h => h.areaId == 5))
.ToList();
This will return a list of POI where each will contain only histories for which areaId == 5.
EDIT:
If you want only the POIs which has any History with areaId == 5, you can simply filter them accordingly -
var result = dbCtx.POIs
.Include(p => p.Histories.Where(h => h.areaId == 5))
.Where(p => p.Histories.Any(h => h.areaId == 5))
.ToList();
You should be able to use the following:
var result = _context.POIs
.Include(poi => poi.Histories)
// Enumerate linked Histories & get the areaId from each into a list)
// ... then see if that list contains the areaID we're looking for.
.Where(poi => poi.Histories.Select(h => h.areaId).Contains(areaIdParam))
.ToList();
I am trying to build a query using asp.net core c#
https://www.reflectionit.nl/blog/2017/paging-in-asp-net-core-mvc-and-entityframework-core
I trying to do a filtering however I need the data from another table which have my unique id
var result = _context.UserRoles.Where(y => y.RoleId.Contains(selectedRoles.Id)); // Retrieve the the userid i have from another table with the selected roleid
var query = _context.Users.Where(x => //I have already tried contains, where join );
If there is a site where i can learn this query please recommend. "Join()" does not work as I am doing paging
a least two solutions (please note that I do not check the identity classes members, so the following is the "spirit" of the solution (you miss the select clauses) ):
var result = _context.UserRoles.
Where(y => selectedRoles.Contains(y.RoleId)).
Select(y => y.User);
or
var result = _context.UserRoles.
Where(y => selectedRoles.Contains(y.RoleId)).
Select(y => y.UserId);
query = _context.Users.
Where(x => result.Contains(x.Id));
That said, assuming that there is no UserRoles table exposed in Identity (v2), you probably want:
userManager.Users.
Where(u => u.Roles.Any(r => selectecRoles.Contains(r.RoleId)));
Up to you to instanciate the userManager.
Kind of a specific question but I wasn't sure how to approach it. I've got a list of rooms, that I am trying to group first by type, then by owner. I am doing this to check if there are duplicate rooms for a given owner and type (which shouldn't be possible so I need to prune them out). Right now my code looks like this:
IQueryable<IGrouping<Guid, Room>> allRoomsByOwner = _dbContext.Rooms.GroupBy(x => x.OwnerId);
List<Room> duplicates = new List<Room>();
foreach (IGrouping<Guid, Room> roomsByOwner in allRoomsByOwner)
{
IEnumerable<IGrouping<Guid, Room>> roomsOfOwnerByType = roomsByOwner.ToList().GroupBy(x => x.TypeId);
foreach (IGrouping<Guid, Room> grouping in roomsOfTypeByType)
{
if (grouping.Count() > 1)
{
duplicates.AddRange(grouping.ToList());
}
}
}
I'm just wondering if it's possible to put this all into one LINQ statement? I've got similar things before, but not quite this complex and not using two group bys. Thanks.
You can group by multiple columns ( OwnerId and TypeId) and flatten the groups with more than one elements (using the SelectMany method) to get the duplicates:
var duplicates = _dbContext.Rooms.GroupBy(x => new{x.OwnerId,x.TypeId})
.Where(g=>g.Count()>1)
.SelectMany(g=>g.Skip(1))// If you like you can skip the first element as representative of the group and the treat the rest as a duplicate.
.ToList();
I'm using EF4. Having 2 entities:
Person { Name }
Hobbys { Person.Name, IsCoolHobby }
1 Person can have several hobbys.
I now have
IQueryable<Person> p;
p = container.PersonSet.Include("Hobbys").AsQueryable();
p = p.Where(x => x ?????);
List<Person> tmp = p.ToList();
How can i return only those Persons who have cool hobbys (IsCoolHobby == true)? I tried join but i was not able to load them into the list (select can only return Person, Hobby or new Type - but how to map them to entity objects again?)
Thanks
How can i return only those Persons who have cool hobbys (IsCoolHobby
== true)?
List<Person> tmp = container.PersonSet.Include("Hobbys")
.Where(p => p.Hobbys.Any(h => h.IsCoolHobby))
.ToList();
This will load the people who have at least one cool hobby but the Hobbys collection for those people will always contain all hobbys, also the uncool hobbys.
Edit
Unfortunately filtering and sorting children during eager loading (Include) is currently not supported. There is a request on the EF feature suggestion page for this feature. The request has status "Under review", so there is a little hope that it might get implemented in the future. (Probably far future: At least the first docs about EF 5 (beta) on MSDN say explicitly that eager loading with filtering/sorting is still not implemented.)
For now there are only two workarounds. The first is to use a projection:
var projectedData = container.PersonSet
.Where(p => p.Hobbys.Any(h => h.IsCoolHobby))
.Select(p => new
{
Person = p,
CoolHobbys = p.Hobbys.Where(h => h.IsCoolHobby)
})
.ToList();
The result is a collection of anonymous objects which contain a user who has cool hobbys and a collection of those cool hobbys. If you don't disable change tracking (by using the NoTracking option for the query) the person's hobbys collection should be filled with the result automatically.
The second option is to use "explicit" loading with CreateSourceQuery:
List<Person> tmp = container.PersonSet
.Where(p => p.Hobbys.Any(h => h.IsCoolHobby))
.ToList();
foreach (var person in tmp)
{
person.Hobbys.Attach(person.Hobbys.CreateSourceQuery()
.Where(h => h.IsCoolHobby).ToList());
}
Two things to note here:
CreateSourceQuery is only available on EntityCollections, i.e. if you are using EntityObject derived entities. It's not available for POCO entities in EF 4.0. (EF >= 4.1/DbContext has the option for explicit loading also for POCOs -> Query() method.)
The above code represents 1+N roundtrips to the database: The first for the person collection without the hobbys and then one additional query per person to load the cool hobbys.
I'm quite a newbie in EF, so I'm sorry if my question has been answered before.. I just can't figure out the syntax..
I have two entities, Category & Product, where one category has many products.
I want to get all categories, with only their latest product (it has a date property named timestamp)
I have no idea how to do that. :-/
If possible I'd like to know the syntax of the two ways to write it, both the sql-like syntax, and the C# like syntax, e.g.:
ctx.Categories.Include("Products").ToList()
from c in ctx.Categories.Include("Products")
Thanks!
Here's the SQL-like way:
var categories =
from p in products
group p by p.Category into g
select new { Category = g.TheKey, LatestProduct = g.Max(p => p.TimeStamp) };
This is the Lambda-way (warning, untested):
var categories = products.GroupBy(p => p.Category)
.Select(g => new { Category = g.TheKey,
LatestProduct = g.Max(p => p.TimeStamp)});
A note on Categories.Include("Products"), you don't need this in your example. You use "Include" for eager-loading, so that for example if you had a list of Categories returned from EF, when you do Categories.Product you will get the associated product.
But all you require is a list of categories, and a single product for each one - which is already returned in the above LINQ query, so no need for Include.