LINQ to Entities query to verify all rows exist - entity-framework

I have a need to verify that all the rows in a given set exist in the database and the current user has access to all the rows. I'd like to do this in a single query to the database, something like a .All() query but I can't quite come up with the right syntax (maybe it's not possible).
The iterative version of the code would look like:
bool canAccess;
foreach(var taskId in taskIds)
{
canAccess = await DataContext.WorkTasks.AnyAsync(wt => wt.DealerId == dealerId && wt.Id == taskId);
if(!canAccess) break;
}
I was thinking about something like:
var canAccess = await DataContext.WorkTasks.AllAsync(wt => wt.DealerId == dealerId && taskIds.Contains(wt.Id));
But I don't think that's what I want. Can this be done using LINQ?

Something like this:
var dbCount = await DataContext.WorkTasks.Where(wt => wt.DealerId == dealerId && taskIds.Contains(wt.Id)).Count();
Will send all your IDs to the server and count the matching rows.

To combine into a single query:
var q = DataContext.WorkTasks.Take(0); // setup q to right type
foreach (var taskId in taskIds)
q = q.Concat(DataContext.WorkTasks.Where(wt => wt.Id == taskId && wt.DealerId == dealerId));
var canAccess = (taskIds.Count() == q.Count());

Related

How to Compare two dates in LINQ in Entity Framework in .NET 5

using DbFunctions = System.Data.Entity.DbFunctions;
Using the above namespace I tried below ways but nothing worked.
This code is throwing an exception which states...
public async Task<int> SomeFunction(){
var count = await _context.Drives.CountAsync(c => DbFunctions.TruncateTime(c.CreatedOn) == DateTime.Today);
var count1 = await _context.Drives.Where(c => DbFunctions.TruncateTime(c.CreatedOn) == DateTime.Today).CountAsync();
var data = _context.Drives.Where(c => !c.IsDeleted).ToList();
//This throw an exception
// "This function can only be invoked from LINQ to Entities."
var count2 = data.Count(x=> DbFunctions.TruncateTime(c.CreatedOn) == DateTime.Today)
}
The LINQ expression 'DbSet().Where(d => DbFunctions.TruncateTime((Nullable)d.CreatedOn) == (Nullable)DateTime.Today)' could not be translated
Can someone help me out how can I compare two dates (only date not with time) in LINQ and Entity Framework?
The problem is, that you are applying DbFunctions.TruncateTime to data.
data is of type List<Drive> on not IQueryable<Drive>, because you already called ToList().
So you Count would be evaluated in memory and not on the database.
If that is really, what you want, then you can just use it like this:
var count2 = data.Count(x=> x.CreatedOn.Day == DateTime.Today);
If you want to invoke your query on the database, then you can use
var count2 = _context.Drives.Count(x=> !x.IsDeleted && DbFunctions.TruncateTime(x.CreatedOn) == DateTime.Today);
Depending on the versions and database, you are using, solution 1 may also work on the database
var count2 = _context.Drives.Count(x=> !x.IsDeleted && c.CreatedOn.Day == DateTime.Today);
Another solution would be to just use a time range, which will definetily work on the database and in memory
var start = DateTime.Today;
var end = start.AddDays(1);
var count2InMemory = data.Count(x => x.CreatedOn >= start && x.CreatedOn < end);
var count2InDatabase = _context.Drives.Count(x=> !x.IsDeleted && x.CreatedOn >= start && x.CreatedOn < end);
A final side-note:
You should use async and await when you query the database.

Entity Framework check result null

I am using this query:
var result = from r in db.Registrations
join u in db.UserTypes on r.UserTypeId equals u.UserTypeId
where r.Email.ToLower()==model.LoginEmail.ToLower() && r.Password.ToLower() == model.Password.ToLower()
select new { r,u};
Here how can I check that if no record is found? If so, then
if(result == null)
// no record found
else
// record found
You can use Any or Count extension method:
if(!result.Any())
///norecord found
else
//record found
Using Count would be:
if(result.Count()==0)
///norecord found
else
//record found
First what you assign to a result is just IQueryable so it's query and you need to execute it first. So I suggest renaming it to query and then do it like this:
var query= from r in db.Registrations
join u in db.UserTypes on r.UserTypeId equals u.UserTypeId
where r.Email.ToLower()==model.LoginEmail.ToLower() && r.Password.ToLower() == model.Password.ToLower();
select new { r,u};
var result = query.FirstOrDefault(); // gives first result or null
if (result == null) {
// no record
} else {
// record found and is in result
}

EF Append to an IQueryable a Where that generates an OR

I'm trying to achieve dynamic filtering on a table. My UI has filters that can be enabled or disabled on demand, and as you can imagine, my query should be able to know when to add filters to the query.
What I have so far is that I check if the filter object has a value, and if it does it adds a where clause to it. Example:
var q1 = DBContext.Table1
if (!string.IsNullOrEmpty(filterModel.SubjectContains))
q1 = q1.Where(i => i.Subject.Contains(filterModel.SubjectContains));
if (filterModel.EnvironmentId != null)
q1 = q1.Where(i => i.EnvironmentId == filterModel.EnvironmentId);
if (filterModel.CreatedBy != null)
q1 = q1.Where(i => i.CreatedByUserId == filterModel.CreatedBy);
var final = q1.Select(i => new
{
IssuesId = i.IssuesId,
Subject = i.Subject,
EnvironmentId = i.EnvironmentId,
CreatedBy = i.CreatedByUser.FullName,
});
return final.ToList();
The code above generates T-SQL that contains a WHERE clause for each field that uses AND to combine the conditions. This is fine, and will work for most cases.
Something like:
Select
IssueId, Subject, EnvironmentId, CreatedById
From
Table1
Where
(Subject like '%stackoverflow%')
and (EnvironmentId = 1)
and (CreatedById = 123)
But then I have a filter that explicitly needs an IssueId. I'm trying to figure out how the EF Where clause can generate an OR for me. I'm looking something that should generate a Tsql that looks like this:
Select
IssueId, Subject, EnvironmentId, CreatedById
From
Table1
Where
(Subject like '%stackoverflow%')
and (EnvironmentId = 1)
and (CreatedById = 123)
or (IssueId = 10001)
Found a solution for this that doesn't have to do multiple database call and works for me.
//filterModel.StaticIssueIds is of type List<Int32>
if (filterModel.StaticIssueIds != null)
{
//Get all ids declared in filterModel.StaticIssueIds
var qStaticIssues = DBContext.Table1.Where(i => filterModel.StaticIssueIds.Contains(i.IssuesId));
//Let's get all Issues that isn't declared in filterModel.StaticIssueIds from the original IQueryable
//we have to do this to ensure that there isn't any duplicate records.
q1 = q1.Where(i => !filterModel.StaticIssueIds.Contains(i.IssuesId));
//We then concatenate q1 and the qStaticIssues.
q1 = q1.Concat(qStaticIssues);
}
var final = q1.Select(i => new
{
IssuesId = i.IssuesId,
Subject = i.Subject,
EnvironmentId = i.EnvironmentId,
CreatedBy = i.CreatedByUser.FullName,
});
return final.ToList();

How to write this Query in LINQ

I have one LINQ query with foreach loop. Everything is fine. But it takes more time to get the value. So anybody suggest me how can i do this in LINQ query itself.
Code
NormValue = "";
c = 0;
var NormValuelist = db.BCont.Where(x => x.BId == BId && x.TNo == Tag).ToList();
foreach (var item in NormValuelist)
{
if (c == 0)
NormValue = item.NormValue;
else
NormValue += " " + item.NormValue;
c = 1;
}
Thanks
You can rewrite this query with string.Join to avoid creating multiple string objects in a loop, like this:
string NormValue = string.Join(" ", db.BCont.Where(x => x.BId == BId && x.TNo == Tag));
The number of round-trips to DB will remain the same, but the creation of List<string> and the partially concatenated string objects will be optimized out.
In addition to using String.Join, you could also use Enumerable.Aggregate:
var NormValueList =
db.BCont.Where(x => x.Bid == BId && x.TNo == Tag)
.Select(x => x.NormValue)
.Aggregate((s, x) => s + " " + x);
If you are having large items in "NormValuelist" then it would be better to use StringBuilder instead of string(NormValue)

Entity Framework - ElementAt

I have a Table User(ID, Name....), Projects(ID, Name, Timestamps, IsFavorite...), Projects_Favoite(UserID, ProjectID)
I try to check for each projekt if there is a row for the current user. If there is a row I want set the "IsFavorite" in my Project true, otherwise false.
I tied:
for(int i = 0; i <= erg.Count();i++)
{
if (erg.ElementAt(i).User11.Any(u => u.Guid == ID) == true)
erg.ElementAt(i).SetFavorite = true;
}
but there is no way to use ElementAt, because it can't translated to SQL.
So I tried:
for(int i = 0; i <= erg.Count();i++)
{
if (erg.Take(i).Last().User11.Any(u => u.Guid == ID) == true)
erg.Take(i).Last().SetFavorite = true;
}
same problem here, so I tried:
foreach (Project project in erg)
{
if (project.User11.Any(u => u.Guid == ID))
project.SetFavorite = true;
}
There is the same problem. Is there a way to realise a ElementAt?
You should be able to do:
foreach (Project project in erg.Where(p => p.User11.Any(u => u.Guid == ID))
.ToList())
project.SetFavorite = true;
}
Assuming that erg is an IQuerable than you cannot use ElementAt as it is not supported (http://msdn.microsoft.com/en-us/library/bb399342.aspx check the section Operators With no translation), you can use it on a list though, but of course you will lose the benefits of the deferred query.
Try to use Skip instead
var whatyouwant = erg.Skip(index).First();
By looking at the stacktrace (can't read german though) looks there's something wrong with the SetFavorite column. Try first to remove this bit first:
project.SetFavorite = true;
then check also that your model columns match with what you got on the db.