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.
Related
I have two options to filter my query:
var users = query.Select(x => x.User).FindUsers(searchString);
query = query.Where(x => users.Contains(x.User));
or
var userIds = query.Select(x => x.User).FindUsers(searchString).Select(x => x.Id);
query = query.Where(x => userIds.Contains(x.UserId));
Description of FindUsers extension:
static IQueryable<User> FindUsers(this IQueryable<User> query, string searchString);
So what I will have in SQL finally? Which of these two requests are better for perfomance?
If someone has another suggestion write it in answer please.
Thanks in advance!
Both query are similar in EF v6 or higher; Contains condition will be translated to Sql EXISTS
Take a loot to the code below :
using (var dbContext = new DbContextTest("DatabaseConnectionString"))
{
var users = dbContext.Users.Select(u => u).Where(x => x.UserId > 0);
var query = dbContext.Users.Where(x => users.Contains(x));
Console.WriteLine(query.ToList());
}
using (var dbContext = new DbContextTest("DatabaseConnectionString"))
{
var ids = dbContext.Users.Select(u => u.UserId).Where(x => x > 0);
var query = dbContext.Users.Where(x => ids.Contains(x.UserId));
Console.WriteLine(query.ToList());
}
The output Sql queries are excatly the same (you can use profiler or EF logger to see that). One thing maybe is important, selecting only the Id's will be more agile for materialisation and cache.
Tip:
If you add ToList() in the Ids query dbContext.Users.Select(u => u.UserId).Where(x => x > 0).ToList(); then this fix will imporve your result performance. The next select query will be translated with SQL "IN" instead of of "EXISTS"! Take a look to this link Difference between EXISTS and IN in SQL? and you can decide what is better for you.
Note:
ToList() will materialize the Id's, that means you will work with another interface then IQueryable!
I am trying to cut this linq down
var sys = db.tlkpSystems
.Where(a => db.tlkpSettings.Where(e => e.Hidden < 3)
.Select(o => o.System)
.ToList().Contains(a.System)) //cannot get this part in?
.OrderBy(a => a.SystemName).ToList();
foreach (var item in sys)
model.Add(new SettingSystem {
System = item.System,
SystemName = item.SystemName
});
I have tried the following:
List<SettingSystem> model = new List<SettingSystem>();
model = db.tlkpSettings.Where(e => e.Hidden < 3)
.OrderBy(e => e.Setting)
.Select(e => new SettingSystem
{
System = e.System,
SystemName = e.Setting
}).ToList();
How can I call the .Contains(a.System) part in my query?
Thanks
Some general rules when working with LINQ to Entities:
Avoid using ToList inside the query. It prevents EF to build a correct SQL query.
Don't use Contains when working with entities (tables). Use Any or joins.
Here is your query (in case System is not an entity navigation property):
var sys = db.tlkpSystems
.Where(a => db.tlkpSettings.Any(e => e.Hidden < 3 && e.System == a.System))
.OrderBy(a => a.SystemName).ToList();
As an addendum, there is also AsEnumerable for when you must pull a query into memory (such as calling methods within another clause). This is generally better than ToList or ToArray since it'll enumerate the query, rather than enumerating, putting together a List/Array, and then enumerating that collection.
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>();
I am trying to get the Previous and Next Record, but I am getting an exception at run time. What am I doing wrong? I found this solution to my problem on the internet. I get the basic idea, but I am not very experienced with LINQ.
LINQ to Entities does not recognize the method 'System.Linq.IQueryable1[CXW__DAQ___Web.Models.Report] SkipWhile[Report(System.Linq.IQueryable1[CXW_DAQWeb.Models.Report],System.Linq.Expressions.Expression1[System.Func2[CXW__DAQ_Web.Models.Report,System.Boolean]])' method, and his method cannot be translated into a store expression.
here is my relevant code.
var NextRecord = db.Reports.OrderBy(i => i.ID)
.SkipWhile(i => i.ID != id)
.Skip(1)
.First();
int nextID = NextRecord.ID;
var PrevRecord = db.Reports.OrderBy(i => i.ID)
.Reverse()
.SkipWhile(i => i.ID != id)
.Skip(1)
.Last();
int prevID = PrevRecord.ID;
What the error message is trying to tell you is that LINQ to Entities has no way to translate the IEnumerable.SkipWhile() method into SQL. You'll first have to force the remainder of the query to use LINQ to Objects first instead:
var nextRecord =
db.Reports.OrderBy(i => i.ID)
.AsEnumerable()
.SkipWhile(i => i.ID != id)
.Skip(1)
.First();
The obvious downside here being that your entire table is going to be ordered and then read into memory.
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.