Performance - get data through navigation property vs compiled query - c#

I have compiled queries for both, the main entity customer and for related entities(orders).
var customer = MyService.GetCustomer<Customer>().where(c => c.Id == fooId).ToList();
var customerOrders = MyService.GetOrders<Order>().where(o => o.CustomerId == fooId && o.IsActive).ToList();
But I think I can get all orders through navigation properties instead of compiled query calls since customer is already loaded in memory by the code below:
var customerOrders = customer.Orders.where(o => o.IsActive).ToList(); // I didn't do filter further more
But when I measure the timings I couldn't find any considerable amount of difference (The DB has 500 customers and 4000 orders. And every particular customer has 30 active orders and around 400 inactive orders).
Which one of these two would have a better performance?
I could not fully understand this related question

Linq to Entities translates the Linq queries to SQL.
var customer = MyService.GetCustomer<Customer>().where(c => c.Id == fooId).ToList();
This can actually be simplified since c.Id is unique:
Customer customer = MyService.GetCustomer<Customer>().SingleOrDefault(c=> c.Id == fooId);
What you do here is just get a customer with a certain Id first. (constant and does not depend on the Count of the Orders you query)
Then you query the orders of this customer(this query is dependend on how many orders this customer have):
var customerOrders = customer.Orders.where(o => o.IsActive).ToList();
Then you do another query for which will lead to the exact same SQL statement as the one above.
var customerOrders = MyService.GetOrders<Order>().where(o => o.CustomerId == fooId && o.IsActive).ToList();
This is why the performance difference is only the first query.

You way depend of your case.
if you're going to actively use related entities:
Best way include this in you query:
using System.Data.Entity;
...
var customer = MyService.GetCustomer<Customer>().where(c => c.Id == fooId).Include(u => u.Orders).ToList();
In other cases prefer lazy loading.

Related

Is there any possible way to join 2 tables from 2 different databases that use 2 different DbContexts?

So, I'm trying to run the following queries:
var qarDevicesAffected = from a in context.Alerts
join d in context.Devices on a.ResourceId equals d.Id
where !d.Inactive && !a.Inactive && a.Name.Equals(alertName)
select new AffectedEntity.Device
{
Id = a.Id,
DeviceId = d.Id,
TenantId = d.TenantId,
OperatorName = context.Operators
.Select(o => new { o.Id, o.Name })
.First(o => o.Id == d.TenantId).Name,
DeviceSerialNumber = d.SerialNumber,
CellModuleId = d.CellModule.Id,
CellModuleSerialNumber = d.CellModule.SerialNumber,
LastCheckIn = d.LastCheckIn,
LastDownload = d.LastDownload,
LastPackageCreation = d.LastPackageCreation,
TailNumber = d.Tail.TailNumber,
TailId = d.Tail.Id,
CreationDate = oldDate == "CheckIn" ? d.LastCheckIn.Value : role == "LastDownload" ? d.LastDownload.Value : d.LastPackageCreation.Value,
IsPinned = parameters.PinnedIds != null && parameters.PinnedIds.Contains(d.TenantId)
};
var avWifiDevicesAffected = from a in context.Alerts
join d in dataSyncDbContext.Device on a.ResourceId equals d.DeviceId
join p in dataSyncDbContext.PostedFile on a.ResourceId equals p.DeviceId
join c in dataSyncDbContext.CheckIn on a.ResourceId equals c.DeviceId
where d.Status != "ACTIVE" && !a.Inactive && a.Name.Equals(alertName)
select new AffectedEntity.Device
{
Id = a.Id,
DeviceId = d.DeviceId,
TenantId = a.TenantId,
OperatorName = context.Operators
.Select(o => new { o.Id, o.Name })
.First(o => o.Id == a.TenantId).Name,
DeviceSerialNumber = d.SerialNumber,
LastCheckIn = c.UpdatedDate,
LastDownload = p.CreatedDate,
TailId = Guid.Parse(d.TailId)
};
var devicesAffected = qarDevicesAffected.Concat(avWifiDevicesAffected);
And I end up getting the following error:
System.InvalidOperationException:
Cannot use multiple DbContext instances within a single query
execution. Ensure the query uses a single context instance.
It seems obvious that EF Core just does not support this, so, is there a way around using queries that would get me the same result? (Join 2 tables from 2 different dbContexts)
EF Core is an ORM. It wraps raw sql and object mapping behind a nice interface. You can use its Linq-to-Entities functionality to construct a query, which will be converted to sql. However, with two different databases and db contexts, EF core has no idea how to combine them to one sql statement.
If the databases are on one server, you'll have to manually (sql) join the tables and you can use something like Dapper for lower level ORM.
If the databases are on separate server, you must see why EF Core is having a hard time, as there is no simple solution. The relatively simple solution would be to have separate queries and join them in memory. This could have a big performance impact, but there is no good alternative. You can switch from Linq-to-entities (IQueryable) to normal Linq (IEnumerable) by calling .AsEnumerable(). (There also are async options, but that's a more complex story)
In your case, I would split it up into several queries, fetching the exact data you want. Instead of big expensive joins, that just fetch a lot of data. I.e. you first get the Alerts data with the appropriate filter, and then get the appropriate columns in the other table based on matching alert.ResourceId. You can do that by putting the resourdeids together in an array, and filter the tables with with something like
.Where(x => resourceIds.Contains(x.DeviceId)
You will finally have to do some object mapping yourself

Query returns all results quickly, but then when call to JSON times out due to loading all unnecessary properties

I have the following query that gets a list of schools based on the criteria provided. Note: This database is very, very large with 10,000+ records. The end result is a list of 188 schools, which is exactly as we need.
return (from s in Context.Schools
join d in Context.Districts on s.DistrictID equals d.DistrictID
join r in Context.Rosters on s.SchoolID equals r.SchoolID
join te in Context.TestEvents on r.TestEventID equals te.TestEventID
join ta in Context.TestAdministrations on te.TestAdministrationID equals ta.TestAdministrationID
join sr in Context.ScoreResults on r.RosterID equals sr.RosterID into exists
from any in exists.DefaultIfEmpty()
where d.DistrictID == DistrictID
&& ta.SchoolYearID == SchoolYearID.Value
select s)
.Distinct()
.OrderBy(x => x.Name)
.ToList();
The problem is when we call return Json(Schools, JsonRequestBehavior.AllowGet); to send our schools back to the client the operation times out. It appears when stepping thorough the code that for some reason the DbContext is trying to pull in ALL of the properties for this result set, including the ones we don't need. I already have everything I need from the database in this Schools object. Why does it go back and start creating all the associated objects. Is there a way to stop this?.
This is an MVC application using EF 5 Code First.
Instead of selecting the whole entity, select a projection of only what you need:
var results = from s in Context.Schools
...
select new MyClassContainingOnlyAFewProperties {
Prop1 = s.Prop1,
Prop2 = s.Prop2,
//etc.
}
return results;
See also: What does Query Projection mean in Entity Framework?

sub linq query is making this take a very long time, how can I make this faster?

I have a list of employees that I build like this:
var employees = db.employees.Where(e => e.isActive == true).ToList();
var latestSales = from es in db.employee_sales.Where(x => x.returned == false);
Now what I want is a result like this:
int employeeId
List<DateTime> lastSaleDates
So I tried this, but the query takes a very very long time to finish:
var result =
(from e in employees
select new EmployeeDetails
{
EmployeeId = e.employeeId,
LastSaleDates =
(from lsd in latestSales.Where(x => x.EmployeeId == e.EmployeeId)
.Select(x => x.SaleDate)
select lsd).ToList()
};
The above works, but literally takes 1 minute to finish.
What is a more effecient way to do this?
You can use join to get all data in single query
var result = from e in db.employees.Where(x => x.isActive)
join es in db.employee_sales.Where(x => x.returned)
on e.EmployeeId equals es.EmployeeId into g
select new {
EmployeeId = e.employeeId,
LastSaleDates = g.Select(x => x.SaleDate)
};
Unfortunately you can't use ToList() method with Linq to Entities. So either map anonymous objects manually to your EmployeeDetails or change LastSalesDates type to IEnumerable<DateTime>.
Your calls to ToList are pulling things into memory. You should opt to build up a Linq expression instead of pulling an entire query into memory. In your second query, you are issuing a new query for each employee, since your are then operating in the Linq-to-objects domain (as opposed to in the EF). Try removing your calls to ToList.
You should also look into using Foreign Key Association Properties to makes this query a lot nicer. Association properties are some of the most powerful and useful parts of EF. Read more about them here. If you have the proper association properties, your query can look as nice as this:
var result = from e in employees
select new EmployeeDetails
{
EmployeeId = e.employeeId,
LastSaleDates = e.AssociatedSales
}
You might also consider using a join instead. Read about Linq's Join method here.
Is there an association in your model between employees and latestSales? Have you checked SQL Profiler or other profiling tools to see the SQL that's generated? Make sure the ToList() isn't issuing a separate query for each employee.
If you can live with a result structure as IEnumerable<EmployeeId, IEnumerable<DateTime>>, you could consider modifying this to be:
var result = (from e in employees
select new EmployeeDetails
{
EmployeeId = e.employeeId,
LastSaleDates = (from lsd in latestSales
where e.employeeId equals lsd.EmployeeId
select lsd.SaleDate)
};
I have some more general recommendations at http://www.thinqlinq.com/Post.aspx/Title/LINQ-to-Database-Performance-hints to help track issues down.

LINQ Query - Only get Order and MAX Date from Child Collection

I'm trying to get a list that displays 2 values in a label from a parent and child (1-*) entity collection model.
I have 3 entities:
[Customer]: CustomerId, Name, Address, ...
[Order]: OrderId, OrderDate, EmployeeId, Total, ...
[OrderStatus]: OrderStatusId, StatusLevel, StatusDate, ...
A Customer can have MANY Order, which in turn an Order can have MANY OrderStatus, i.e.
[Customer] 1--* [Order] 1--* [OrderStatus]
Given a CustomerId, I want to get all of the Orders (just OrderId) and the LATEST (MAX?) OrderStatus.StatusDate for that Order.
I've tried a couple of attempts, but can seem to get the results I want.
private IQueryable<Customer> GetOrderData(string customerId)
{
var ordersWithLatestStatusDate = Context.Customers
// Note: I am not sure if I should add the .Expand() extension methods here for the other two entity collections since I want these queries to be as performant as possible and since I am projecting below (only need to display 2 fields for each record in the IQueryable<T>, but thinking I should now after some contemplation.
.Where(x => x.CustomerId == SelectedCustomer.CustomerId)
.Select(x => new Custom
{
CustomerId = x.CustomerId,
...
// I would like to project my Child and GrandChild Collections, i.e. Orders and OrderStatuses here but don't know how to do that. I learned that by projecting, one does not need to "Include/Expand" these extension methods.
});
return ordersWithLatestStatusDate ;
}
---- UPDATE 1 ----
After the great solution from User: lazyberezovsky, I tried the following:
var query = Context.Customers
.Where(c => c.CustomerId == SelectedCustomer.CustomerId)
.Select(o => new Customer
{
Name = c.Name,
LatestOrderDate = o.OrderStatus.Max(s => s.StatusDate)
});
In my hastiness from my initial posting, I didn't paste everything in correctly since it was mostly from memory and didn't have the exact code for reference at the time. My method is a strongly-typed IQueryabled where I need it to return a collection of items of type T due to a constraint within a rigid API that I have to go through that has an IQueryable query as one of its parameters. I am aware I can add other entities/attributes by either using the extension methods .Expand() and/or .Select(). One will notice that my latest UPDATED query above has an added "new Customer" within the .Select() where it was once anonymous. I'm positive that is why the query failed b/c it couldn't be turn into a valid Uri due to LatestOrderDate not being a property of Customer at the Server level. FYI, upon seeing the first answer below, I had added that property to my client-side Customer class with simple { get; set; }. So given this, can I somehow still have a Customer collection with the only bringing back those 2 fields from 2 different entities? The solution below looked so promising and ingenious!
---- END UPDATE 1 ----
FYI, the technologies I'm using are OData (WCF), Silverlight, C#.
Any tips/links will be appreciated.
This will give you list of { OrderId, LatestDate } objects
var query = Context.Customers
.Where(c => c.CustomerId == SelectedCustomer.CustomerId)
.SelectMany(c => c.Orders)
.Select(o => new {
OrderId = o.OrderId,
LatestDate = o.Statuses.Max(s => s.StatusDate) });
.
UPDATE construct objects in-memory
var query = Context.Customers
.Where(c => c.CustomerId == SelectedCustomer.CustomerId)
.SelectMany(c => c.Orders)
.AsEnumerable() // goes in-memory
.Select(o => new {
OrderId = o.OrderId,
LatestDate = o.Statuses.Max(s => s.StatusDate) });
Also grouping could help here.
If I read this correctly you want a Customer entity and then a single value computed from its Orders property. Currently this is not supported in OData. OData doesn't support computed values in the queries. So no expressions in the projections, no aggregates and so on.
Unfortunately even with two queries this is currently not possible since OData doesn't support any way of expressing the MAX functionality.
If you have control over the service, you could write a server side function/service operation to execute this kind of query.

Filtering a graph of entity framework objects

I'm trying to filter down the results returned by EF into only those relevant - in the example below to those in a year (formattedYear) and an ordertype (filtOrder)
I have a simple set of objects
PEOPLE 1-M ORDERS 1-M ORDERLINES
with these relationships already defined in the Model.edmx
in SQL I would do something like...
select * from PEOPLE inner join ORDERS on ORDERS.PEOPLE_RECNO=PEOPLE.RECORD_NUMBER
inner join ORDERLINE on ORDERLINE.ORDER_RECNO=ORDERS.RECORD_NUMBER
where ORDERLINE.SERVICE_YEAR=#formattedYear
and ORDERS.ORDER_KEY=#filtOrder
I've tried a couple of approaches...
var y = _entities.PEOPLE.Include("ORDERS").Where("it.ORDERS.ORDER_KEY=" + filtOrder.ToString()).Include("ORDERLINEs").Where("it.ORDERS.ORDERLINEs.SERVICE_YEAR='" + formattedYear + "'");
var x = (from hp in _entities.PEOPLE
join ho in _entities.ORDERS on hp.RECORD_NUMBER equals ho.PEOPLE_RECNO
join ol in _entities.ORDERLINEs on ho.RECORD_NUMBER equals ol.ORDERS_RECNO
where (formattedYear == ol.SERVICE_YEAR) && (ho.ORDER_KEY==filtOrder)
select hp
);
y fails with ORDER_KEY is not a member of transient.collection...
and x returns the right PEOPLE but they have all of their orders attached - not just those I am after.
I guess I'm missing something simple ?
Imagine you have a person with 100 orders. Now you filter those orders down to 10. Finally you select the person who has those orders. Guess what? The person still has 100 orders!
What you're asking for is not the entity, because you don't want the whole entity. What you seem to want is a subset of the data from the entity. So project that:
var x = from hp in _entities.PEOPLE
let ho = hp.ORDERS.Where(o => o.ORDER_KEY == filtOrder
&& o.ORDERLINES.Any(ol => ol.SERVICE_YEAR == formattedYear))
where ho.Any()
select new
{
Id = hp.ID,
Name = hp.Name, // etc.
Orders = from o in ho
select new { // whatever
};
I am not exactly sure what your question is but the following might be helpful.
In entity framework if you want to load an object graph and filter the children then you might first do a query for the child objects and enumerate it (i.e. call ToList()) so the childern will be fetched in memory.
And then when you fetch the parent objects (and do not use .include) enitity framework will able to construct the graph on its own (but note that you might have to disable lazy loading first or it will take long to load).
here is an example (assuming your context is "db"):
db.ContextOptions.LazyLoadingEnabled = false;
var childQuery = (from o in db.orders.Take(10) select o).ToList();
var q = (from p in db.people select p).ToList();
Now you will find that every people object has ten order objects
EDIT: I was in a hurry when I wrote the sample code, and as such I have not tested it yet, and I probably went wrong by claiming that .Take(10) will bring back ten orders for every people object, instead I believe that .Take(10) will bring back only ten overall orders when lazy loading is disabled, (and for the case when lazy loading is enabled I have to actually test what the result will be) and in order to bring back ten orders for every people object you might have to do more extensive filtering.
But the idea is simple, you first fetch all children objects and entity framework constructs the graph on its own.

Categories