Include datatable in existing linq query - c#

I have the below piece of code which gives my time zones as per each country
var zones = TzdbDateTimeZoneSource.Default
.ZoneLocations
.Where(x => x.CountryCode == countryCode)
.Select(x => x.ZoneId);
Now i need to include datatable within the above linq query to check if that datatable have the zone locations in database already as explained in below code. How do i do that ?
var zones = TzdbDateTimeZoneSource.Default
.ZoneLocations
.Where(x => x.CountryCode == countryCode &&
oratznamesoratznames.Select().ToList()
.Exists(row => row["TZNAME"].ToString().ToUpper() == x.ZoneId))
.Select(x => x.ZoneId);

Id I understand your question correcty, you could use .Any(). It returns a boolean indicating where any of the elements of the collection matches the predicate.
Also, note that there is no need to use && in this case.
var zones = TzdbDateTimeZoneSource.Default
.ZoneLocations
.Where(x => x.CountryCode == countryCode)
.Select(x => x.ZoneId)
.Where(x => oratznamesoratznames
.Any(r => r["TZNAME"].ToString().ToUpper() == x))

Related

Is there a way to simplify these linq statements using an .Include()?

Currently I am doing a keyword search on the Plates table (Name column) but also have a Search (searching on SearchTerm column) table which contains Plat Id's that I also want to search and return the corresponding platforms.
The code below works but I'd like to simplify the logic using an .Include statement if possible although I'm not quite sure how. Any help would be greatly appreciated.
if (!string.IsNullOrEmpty(request.Keyword))
{
var searchTermPlateIds = await _db.Search
.Where(x=> x.SearchTerm.ToLower().Contains(request.Keyword.Trim().ToLower()))
.Select(x => x.PlatformId)
.ToListAsync(ct);
var plateFromPlateIds = await _db.Plate
.OrderBy(x => x.Name)
.Where(x => searchTermPlateIds.Contains(x.Id) && x.Status != PlateStatus.Disabled)
.ToListAsync(ct);
plates = await _db.Plates
.OrderBy(x => x.Name)
.Where(x => !string.IsNullOrEmpty(request.Keyword.Trim()) && x.Name.ToLower().Contains(request.Keyword.Trim().ToLower()) && x.Status != PlateStatus.Disabled)
.ToListAsync(ct);
plates = plates.Union(platesFromPlateIds).ToList();
}
Remember simple thing, Include ONLY for loading related data, not for filtering.
What we can do here - optimize query, to make only one request to database, instead of three.
var query = _db.Plates
.Where(x => x.Status != PlateStatus.Disabled);
if (!string.IsNullOrEmpty(request.Keyword))
{
// do not materialize Ids
var searchTermPlateIds = _db.Search
.Where(x => x.SearchTerm.ToLower().Contains(request.Keyword.Trim().ToLower()))
.Select(x => x.PlatformId);
// queryable will be combined into one query
query = query
.Where(x => searchTermPlateIds.Contains(x.Id);
}
// final materialization, here you can add Includes if needed.
var plates = await query
.OrderBy(x => x.Name)
.ToListAsync(ct);

Entity Framework include child items with filter

I am currently working with a schema which contains entities, each containing attributes and each of them contain one type specification. I want extract a list which fulfills a specific attribute requirement it being of the type lookup and a specific specification.
I have tried this so far
schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.SelectMany(x => x.Entities
.Where(x => x.Attributes
.Any(y => y.Type == DataType.Lookup &&
y.TypeSpecification.EntityInternalName == "Weather")));
This returns a list of entities which fulfills the requirement, but none of the entities has any attributes?
How do I also include this within the query?
I tried adding include at the end,
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Include(x=> x.Entities)
.ThenInclude(x=>x.Attributes)
.ThenInclude(x=>x.TypeSpecification)
.SelectMany(x => x.Entities
.Where(x => x.Attributes
.Any(y => y.Type == DataType.Lookup &&
y.TypeSpecification.EntityInternalName == "Weather")));
but this just include all attributes and etc. ignoring the Any condition for the attributes..
How to get around this?
I tried splitting it a bit
var entitiesWithLookupsTolookupEntityName = schemaRepository
.Many()
.OrderByDescending(x => x.Version)
.Take(1)
.SelectMany(x => x.Entities
.Where(y => y.Attributes
.Any(z => z.Type == DataType.Lookup && z.AttributeTypeSpecification.EntityInternalName == lookupEntityName))).AsEnumerable();
IList<ReferenceView> output = new List<ReferenceView>();
foreach (var entity in entitiesWithLookupsTolookupEntityName)
{
var attribute = schemaRepository.Many().OrderByDescending(x => x.Version).First().Entities
.Where(x => x.InternalName == entity.InternalName);
}
but here I get an exception every in for loop

ANY with ALL in Entity Framework evaluates locally

I have the following Entity Framework 2.0 query:
var user = context.Users.AsNoTracking()
.Include(x => x.UserSkills).ThenInclude(x => x.Skill)
.Include(x => x.UserSkills).ThenInclude(x => x.SkillLevel)
.FirstOrDefault(x => x.Id == userId);
var userSkills = user.UserSkills.Select(z => new {
SkillId = z.SkillId,
SkillLevelId = z.SkillLevelId
}).ToList()
Then I tried the following query:
var lessons = _context.Lessons.AsNoTracking()
.Where(x => x.LessonSkills.All(y =>
userSkills.Any(z => y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
.ToList();
This query evaluates locally and I get the message:
The LINQ expression 'where (([y].SkillId == [z].SkillId) AndAlso ([y].SkillLevelId <= [z].SkillLevelId))' could not be translated and will be evaluated locally.'.
I tried to solve it using userSkills instead of user.UserSkills but no luck.
Is there a way to run this query on the server?
You should try limiting the usage of in-memory collections inside LINQ to Entities queries to basically Contains on primitive value collection, which currently is the only server translatable construct.
Since Contains is not applicable here, you should not use the memory collection, but the corresponding server side subquery:
var userSkills = context.UserSkills
.Where(x => x.UserId == userId);
var lessons = context.Lessons.AsNoTracking()
.Where(x => x.LessonSkills.All(y =>
userSkills.Any(z => y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
.ToList();
or even embed the first subquery into the main query:
var lessons = context.Lessons.AsNoTracking()
.Where(x => x.LessonSkills.All(y =>
context.UserSkills.Any(z => z.UserId == userId && y.SkillId == z.SkillId && y.SkillLevelId <= z.SkillLevelId)))
.ToList();
Use Contains on the server then filter further on the client:
var userSkillIds = userSkills.Select(s => s.SkillId).ToList();
var lessons = _context.Lessons.AsNoTracking()
.Where(lsn => lsn.LessonSkills.All(lsnskill => userSkillIds.Contains(lsnskill.SkillId)))
.AsEnumerable() // depending on EF Core translation, may not be needed
.Where(lsn => lsn.LessonSkills.All(lsnskill => userSkills.Any(uskill => uskill.SkillId == lsnskill.SkillId && lsnskill.SkillLevelId <= uskill.SkillLevelId)))
.ToList();

Getting no results from simple Query

I've tried the following and it returned me every Tutor
List<Tutor>tutorsList = tutors.ToList();
Furthermore, I tried to select only Tutors with a specific subject (Tutor-Subject is n:n)
Subject subjectEntity = subjects.Where(s => s.Name == input).FirstOrDefault();
List<Tutor>tutorsList = tutors.Where(t => t.Subjects.Contains(subjectEntity)) .ToList();
As a result, my tutorsList is empty, even subjectEntity is correct (I printed it to console).
But when I loop every Tutor and print the Subjects, there is a Tutor with Subject input.
Any ideas?
If you also have id's, you can do the following:
Subject subjectEntity = subjects
.Where(s => s.Name == input)
.FirstOrDefault();
List<Tutor> tutorsList = tutors
.Where(t => t.Subjects
.Select(x => x.SubjectId)
.Contains(subjectEntity.SubjectId)
)
.ToList();
If not, you can try to do it in a single query
List<Tutor> tutorsList = tutors
.Where(t => t.Subjects.Any(x => x.Name == input))
.ToList()
simplify in one line, using Any, when working on an inner collection.
var tutorsList = tutors.Where(t => t.Subjects
.Any(s => s.Name == input)).ToList();
Try below
Subject subjectEntity = subjects.Where(s => s.Name == input).FirstOrDefault();
List<Tutor>tutorsList = tutors.Where(t => t.Subjects.Any(x=>x.UniqueField==subjectEntity.UniqueField)).ToList();

I want to convert this foreach loop to a LINQ statement

I am not an great at linq by any means but I usually have no issues with a problem of this sort. I want to convert this foreach statement to a LINQ statement:
var existingKeys = new List<int>();
foreach (var taskKey in request.Keys)
{
existingKeys.AddRange(_context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(x => x.TaskGroupNameKey));
}
I thought this would do it:
var existingKeys = request.Keys.ForEach(taskKey => _context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(x => x.TaskGroupNameKey));
That apparently returns a void not a list...
This:
var existingKeys = request.Keys.Select(taskKey =>
_context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(keys => keys.TaskGroupNameKey));
Gives me an "IEnumerable<IQueryable<int>>. So what is the secret sauce that I am missing here?
You shouldn't be performing N database queries in the first place. Using LINQ to perform those N queries instead of a foreach loop doesn't fix that core problem.
You need to re-conceptualize your query so that you have just one query that gets all of the data that you need. In this case that means getting all of the items that match your collection of keys rather than trying to match a single key and then performing N of those queries.
var requestedKeys = request.Keys;
var existingKeys = _context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key &&
requestedKeys.Contains(x.TaskKey))
.Select(x => x.TaskGroupNameKey))
.ToList();
var existingKeys = request
.SelectMany(r => r.Keys)
.SelectMany(tk =>
_context.WebTaskGroups
.Where(x.TaskGroupNameKey == key && x.TaskKey == tk)
.Select(x => x.TaskGroupNameKey))
.ToList();
var existingKeys = _context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && request.Keys.Contains(x.TaskKey))
.Select(x => x.TaskGroupNameKey)
.ToList();
ForEach return a void: http://msdn.microsoft.com/en-us/library/bwabdf9z(v=vs.110).aspx
ForEch: Performs the specified action on each element of the List.
So what to do, is for each item in the list of request.Keys to perform the action to add to the list of existingKeys.
For example:
request.Keys.ForEach(taskKey =>
existingKeys.AddRange(_context.WebTaskGroups
.Where(x => x.TaskGroupNameKey == key && x.TaskKey == taskKey)
.Select(x => x.TaskGroupNameKey));

Categories