DbSet to return multiple entries - entity-framework

To return a single entry I use DbSet.SingleOrDefault(l => l.UserName == userName). There must be an equivilent that will return a list of items (assuming there is more than 1). But I can't find it.
What's the Linq call on a DbSet to get a list of matching items?

_context.YourEntity.Where(condition).ToList()

Related

Return parent entities based on (and including) child entities

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();

How can I determine how many rows were loaded into an EF6 DbContext when using Load/LoadAsync?

The DbContext DbSet<T>.Load / DbSet<T>.LoadAsync methods return void and Task respectively: they execute queries and then add the loaded/instantiated entity objects into the DbContext's DbSet and update the navigation properties and reverse-navigation of already-loaded objects, but they don't return any information about what they loaded: there doesn't seem to be a way of getting the actual count of the number of rows that were loaded.
Which is surprising, considering that the SaveChanges / SaveChangesAsync method does return the number of rows affected by any DML statements it executes.
I know there's a workaround in that I could use ToList/ToListAsync instead and then use the List<T>.Count property, but that's defeating the point of using Load/LoadAsync.
For example, consider this two-step query operation:
async Task<PageViewModel> LoadOrdersAsync( Int32 customerId, Expression<Func<Order,Boolean>> predicate )
{
// Step 1:
List<Order> orders = await this.dbContext.Orders
.Where( o => o.CustomerId == customerId )
.Where( predicate )
.ToListAsync()
.ConfigureAwait(false);
// Step 1.5:
List<Int32> orderIds = orders.Select( o => o.OrderId ).ToList();
// Step 2:
await this.dbContext.OrderItems
.Where( i => orderIds.Contains( i.OrderId ) )
.LoadAsync()
.ConfigureAwait(false);
// Done!
return new PageViewModel( orders );
}
I want to get the quantity of OrderItem entities that were loaded in the second step, but as far as I know that isn't possible without using ToList/ToListAsync.
You’re right, there is no easy way to get the number of loaded entries of the Load. It is essentially the same as ToList without creating the list and adding the loaded elements to it. If you really don’t want to use ToList, one option is to access the DbContext.ChangeTracker and get the number of entries from that:
var entriesBefore = context.ChangeTracker.Entries().Count();
// load more entities
var loaded = context.ChangeTracker.Entries().Count() - entriesBefore;
Note, that this is not accurate when you include other, related entities in your query.

JPQL Aggregation return

Lets say I have the following JPQL query
SELECT e.column1, e.column2, SUM(e.column3), SUM(e.column4) FROM Entity e GROUP BY e.column1, e.column2
Obviously I wont be returning an Entity object but something a bit more complex. How do I return this in the method?
public List<???> query1() {
Query q = entityManager.createQuery("...");
List<Something???> list = q.getResultList();
return list;
}
Such a query returns a List<Object[]>, where each element is thus an array of Objects. The first element of the array will have the type of Entity.column1, the second one will have the type of Entity.column2, and the last 2 ones will be (with Hibernate at least) of type Long (check with EclipseLink).
It's up to you to transform the List<Object[]> in a List<Foo>, by simply looping over the list of objects and transforming each one into a Foo. You may also use the constructor notation directly in the query (provided Foo has such a constructor), but I personally dislike it, because it isn't refactorable:
select new com.baz.bar.Foo(e.column1, e.column2, SUM(e.column3), SUM(e.column4)) from ...

Linq to Entities Distinct on Column without Anonymous Type

I am using Entity Framework 5.0 and I wish to return a list of objects, however, I want to perform a DISTINCT on one of the properties on each object within the list.
I know there are a few questions similar to mine already on Stackoverflow, however, I am still struggling with this one.
Currently my query looks like this
public IList<tblcours> GetAllCoursesByOrgID(int id)
{
return _UoW.tblcoursRepo.All.Where(c => c.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id))
.OrderBy(c => c.CourseTitle)
.ToList();
}
However, I need to perform a DISTINCT on the property MainHeadingID to remove any objects already with the same ID, but still returning the entire object with all its properties.
Preferably, I would like to return IList, as you can see from my method, and not an Anonymous Type.
Any help with this is greatly appreciated.
Thanks.
Have you tried using GroupBy?
_UoW.tblcoursRepo.All.GroupBy(c => c.MainHeadingId)
.Select(g => g.FirstOrDefault())

Using FirstOrDefault() in query

Is there a way to use FirstOrDefault() inside a complex query but not throw an exception if it returns null value?
My query:
contex.Table1.Where(t => t.Property == "Value").FirstOrDefault()
.Object.Table2.Where(t => t.Property2 == "Value2").FirstOrDefault();
If the query on the first table (Table1) doesn't return an object the code throws an exception. Is there a way to make it return just null?
Try a SelectMany on Table2, without the intermediate FirstOrDefault():
context.Table1.Where(t1 => t1.Property1 == "Value1")
.SelectMany(t1 => t1.Table2.Where(t2 => t2.Property2 == "Value2"))
.FirstOrDefault();
Also, you might want to use SQL Profiler to check the SQL that is being sent by EF. I believe the query as constructed in your question will result in two queries being sent to the database; one for each FirstOrDefault().
You could build your own helper function, that takes IEnumerable
public static TSource CustomFirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
return source.FirstOrDefault() ?? new List<TSource>();
}
This would effectively return an empty list, which, when called upon, providing your code in your Object property can handle nulls, won't bomb out, cause you'll just be returning a 0 item collection, instead of a null.
Only the first query with Where is a database query. As soon as you apply a "greedy" operator like FirstOrDefault the query gets executed. The second query is performed in memory. If Object.Table2 is a collection (which it apparently is) and you don't have lazy loading enabled your code will crash because the collection is null. If you have lazy loading enabled a second query is silently executed to load the collection - the complete collection and the filter is executed in memory.
You query should instead look like #adrift's code which would really be only one database query.