LINQ Queries Comparison - c#

I have 2 LINQ Queries here, i just want to know which of these query is proper and fast to use.
Sample I
var GetUSer = (from UserItem in dbs.users
where UserItem.UserID == UserID
select new User(UserItem))
.OrderBy(item => item.FirstName)
.Skip(0)
.Take(10)
.ToList();
Sample II
var GetUSer = (from UserITem in dbs.user
.Where(item => item.UserID == UserID)
.OrderBy(item => item.FirstName)
.Skip(0)
.Take(10)
.AsEnumerable()
select new User(UserItem)).ToList();
Although they are both working well, i just want to know which is the best.

The Second one is better, the first 1 does a select then does filtering, meaning it has to get the data from the database first to turn it into a User object, then it filters.
The second one will do the query on the DB side, then turn it into a User object
The first one can be fixed by moving the select till just before the ToList()

Between those two, I would prefer the first (for readability, you'd need to switch some things around if you want the whole query to execute in the database). If they both work, it's up to you though.
Personally, I don't like mixing query syntax with lambda syntax if I don't have to, and I prefer lambda. I would write it something like:
var GetUsers = db.user
.Where(u => u.UserID == UserID)
.OrderBy(u => u.FirstName)
.Take(10)
.Select(u => new User(u))
.ToList();
This uses a single syntax, queries as much as possible in the database, and leaves out any superfluous calls.

Related

How to make a complicated LINQ query to DB Asynchronous?

I have a synchronous project which I am currently working to make asynchronous.
I have the following query. It aims to take data for a particular purchase item and then take the last date the item was purchased and in what quantity it was purchased.
private IQueryable<ItemOverviewDto> GetBaseQuery(string userId)
{
var query = this.Context.Items
.Where(x => x.UserId == userId)
.Select(x => new ItemOverviewDto()
{
Id = x.Id,
Name = x.Name,
ReplenishmentPeriod = x.ReplenishmentPeriod,
NextReplenishmentDate = x.NextReplenishmentDate,
LastReplenishmentDate = x.Purchases
.OrderByDescending(y => y.ReplenishmentDate)
.Select(m => (DateTime?)m.ReplenishmentDate)
.FirstOrDefault(),
LastReplenishmentQuantity = x.Purchases
.OrderByDescending(y => y.ReplenishmentDate)
.Select(m => (int?)m.Quantity)
.FirstOrDefault(),
});
return query;
}
Here is the repo.
I build up the query and materialize it later on. When I materialize it - I use ToListAsync(); But I am wondering can this part - ".Select(m => (int?)m.Quantity).FirstOrDefault()," also be made async in some way?
P.S. Select returns an IEnumerable not IQueryable so we can not use ".FirstOrDefaultAsync()" right away.
When you execute a SQL-based Linq (like EF) query, the entire query is converted to SQL and then executed. In your example, the FirstOrDefault just tells the query generator how to formulate the SQL. It is not running a separate query inside the "main" query.
So when you call ToListAsync, the entire query is converted to SQL and executed asynchronously. There is no need to try and convert the inner queries to async as well.

What is the difference between load and include in sql query

I have a query that looks like this
var query = db.Customer
.Include(c => c.Address)
.Where(c => c.Address.Id > 10)
.ToList();
when i do this instead
var query = db.Customer
.Where(c => c.Address.Id > 10)
.ToList();
db.Address
.Where(a => a.Id > 10)
.Load();
I get the same result as far as I see.
My question is: is there any difference between what these two queries return and is one preferred over the other?
var query = db.Customer
.Include(c => c.Address)
.Where(c => c.Address.Id > 10)
.ToList();
On above query where it brings all the related data using single database trip.
var query = db.Customer
.Where(c => c.Address.Id > 10)
.ToList();
db.Address
.Where(a => a.Id > 10)
.Load();
Here it uses 2 database trips to bring the data.
Load :
There are several scenarios where you may want to load entities from
the database into the context without immediately doing anything with
those entities. A good example of this is loading entities for data
binding as described in Local Data. One common way to do this is to
write a LINQ query and then call ToList on it, only to immediately
discard the created list. The Load extension method works just like
ToList except that it avoids the creation of the list altogether.
Note : We cannot say which one is better.Most of the time we use eager loading method (Include).It is nice and simple.But sometimes it is slow.So you need to decide which one to use according to your data size and etc.

Merge results of 2 LINQ To SQL queries and keep their ordering

I need to append the results of one LINQ To SQL query to another on the database server side without reordering rows.
I need all errored orders first, then the pending orders.
var error = database.Orders.
Where(o => o.Status == XOrderStatus.Error).
OrderByDescending(o => o.Id);
var pending = database.Orders.
Where(o => o.Status == XOrderStatus.PendingReview).
OrderByDescending(o => o.Id);
var r = error.OrderedUnion(pending);
How I can implement the OrderedUnion() method? Concat, Union and Distinct methods completely eliminate OrderByDescending call (by design).
I know it is possible to do with two ToArray() calls, but I am interested having this done at the database level.
You can concatente them together and then order by a column that seperates the order groups. so for instance in an SQL Query you would do this:
ORDER BY XOrderStatus, Id
and that would order by ID but with the two OrderStatuses grouped.
I don't know linq to sql (sorry!) but a quick Google mentioned this may work:
.Orderby(o => o.XOrderStatus).ThenBy(o => o.Id)
Original Source:
Multiple "order by" in LINQ
I found much better solution:
var all = database.Orders.
Where(o =>
o.Status == XOrderStatus.Error ||
o.Status == XOrderStatus.PendingReview).
OrderBy(o => o.Status == XOrderStatus.Error ? 0 : 1).
ThenByDescending(o => o.Id).
ToArray();

Join several queries to optimise QueryOver query

I am using NHibernate and while traversing my code I came upon two functions that are called in sequence. They are probably a school example of 1) extra database round trip and 2) in-memory processing at the application side. The code involved is:
// 1) Getting the surveys in advance
var surveys = DatabaseServices.Session.QueryOver<Survey>()
.Where(x => x.AboutCompany.IsIn(companyAccounts.ToList()))
// Actual query that can be optimized
var unverifiedSurveys = DatabaseServices.Session.QueryOver<QuestionInSurvey>()
.Where(x => x.Survey.IsIn(surveys.ToList()))
.And(x => x.ApprovalStatus == status)
.List();
// 2) In-memory processing
return unverifiedSurveys.Select(x => x.Survey).Distinct()
.OrderByDescending(m => m.CreatedOn).ToList();
I have read that the actual Distinct() operation with the QueryOver API can be done using 1 .TransformUsing(Transformers.DistinctRootEntity)
Could anyone give an example how the queries can be combined thus having one round trip to the database and no application-side processing?
The most suitable way in this scenario is to use Subselect. We will firstly create the detached query (which will be executed as a part of main query)
Survey survey = null;
QueryOver<Survey> surveys = QueryOver.Of<Survey>(() => survey)
.Where(() => survey.AboutCompany.IsIn(companyAccounts.ToList()))
.Select(Projections.Distinct(Projections.Property(() => survey.ID)));
So, what we have now is a statement, which will return the inner select. Now the main query:
QuestionInSurvey question = null;
var query = session.QueryOver<QuestionInSurvey>(() => question)
.WithSubquery
.WhereProperty(() => qeustion.Survey.ID)
.In(subQuery) // here we will fitler the results
.And(() => question.ApprovalStatus == status)
.List();
And what we get is the:
SELECT ...
FROM QuestionInSurvey
WHERE SurveyId IN (SELECT SurveyID FROM Survey ...)
So, in one trip to DB we will recieve all the data, which will be completely filtered on DB side... so we are provided with "distinct" set of values, which could be even paged (Take(), Skip())
This might be something like this, which requires a distinct projection of all properties of Survey. I guess there is a better solution, but can not get to it ;-) Hope this will help anyway.
Survey surveyAlias = null;
var result =
session.QueryOver<QuestionInSurvey>()
.JoinAlias(x => x.Survey, () => surveyAlias)
.WhereRestrictionOn(() => surveyAlias.AboutCompany).IsIn(companyAccounts.ToList())
.And(x => x.ApprovalStatus == status)
.Select(
Projections.Distinct(
Projections.ProjectionList()
.Add(Projections.Property(() => surveyAlias.Id))
.Add(Projections.Property(() => surveyAlias.AboutCompany))
.Add(Projections.Property(() => surveyAlias.CreatedOn))
)
)
.OrderBy(Projections.Property(() => surveyAlias.CreatedOn)).Desc
.TransformUsing(Transformers.AliasToBean<Survey>())
.List<Survey>();

How can I condense the following Linq query into one IQueryable

I have an EntityFramework model that has User entity that has an EntityCollection of Organisations.
For a particular User I am trying to write a Linq Query to return the names of the organisations that the user belongs where that query will hit the db only once.
My problem is that I cannot see how to write this query without having to materialise the user first then query the users organisation collection.
I would like to try and write one query that hits the db once.
What I have so far:
var orgNames = context.Users
.Where(u => u.LoweredUserName == userName.ToLower())
//materialises user
.FirstOrDefault()
.Organisations
//second hit to the db
.Select(o => o.Name);
What I was psuedo aiming for but cannot see the wood for the trees:
orgNames = context.Users
.Where(u => u.LoweredUserName == userName.ToLower())
//don't materialise but leave as IQueryable
.Take(1)
//The problem: turn what the query sees as possibly multiple
// (due to the Take method) EntityCollection<Organisation> into a List<String>
.Select(u => u.Organisations.Select(o => o.Name));
I have looked at aggregates but I seem to be going in circles :)
Doh! I think I can answer my own question by using SelectMany to condense the Collection of Collections into one collection like so:
orgNames = context.Users.Where(u => u.LoweredUserName == userName.ToLower())
.Take(1)
.SelectMany(u => u.Organisations)
.Select(o => o.Name);
I'm assuming that Lowered User name is unique, otherwise the query would be fairly meaningless, so you can just use.
context.Users
.Where(u => u.LoweredUserName == userName.ToLower())
.Select(u => u.Organisations.Select(o => o.Name));

Categories

Resources