LINQ include a subset of a query - c#

I have an Entity in EF Core that is using the structure like:
Course has an entity CourseUserRoles which has the CourseId, UserId and RoleId.
CourseViewModel has the same structure as Course, except CourseUserRoles, instead it has two booleans IsAdministrator and IsContributor, that are related to the RoleId.
I am trying to make a query that won't bring all CourseUserRoles for every course queried, but only the ones specific for that user.
I saw that syntactically the below is correct:
query = query.Include(x => x.CourseUserRoles.Where(y => y.UserId == userId));
where the query is trying to return a list of courses, I just want to include the ones that have the same Id as the user.
The problem is that the above is throwing an exception.
Is it possible to only include the CourseUserRoles when the course has the UserId? If it doesn't have it would return null or empty list.

I typically do this by creating separate queries and concatenating as follows:
query = query.Where(x => x.CourseUserRoles.UserId != userId)
var queryWithInclude = query.Where(x => x.CourseUserRoles.UserId == userId)
.Include(x => x.CourseUserRoles)
var fullDataset = query.Concat(queryWithInclude);
This keeps both query objects as type IQueryable and not IEnumerable, allowing execution to happen in SQL/server-side rather than in memory.

Related

Entity Framework SQL Query Generation

I am having difficulty understanding how Entity Framework generates a single SQL Query from a couple of Linq methods. Here is a code which selects Username from a table.
var result1 = context.UserMasters
.Where(t => t.UserRole == "LOCAL")
.Skip(100)
.Take(100)
.Select(t => new { t.UserName });
Typically the above code is identical to the below code.
var test = context.UserMasters.Where(t=> t.UserRole == "LOCAL");
test = test.Skip(100);
test = test.Take(100);
var result2 = test.Select(t => new { t.UserName });
I hope we can get results from the table after any Linq Method. So there is a list readily available always. Is it selecting all results from the table initially and doing operation on the list ?
If the type is IQueryable, then the filtering of data happens at the database side. But if the type of query is IEnumerable or other than IQueryable, all the data from the table is fetched and filtering is done at .Net side. It is always advised to use IQueryable in these cases. Please read about IQueryable and IEnumerable difference.

Querying with methods in EF Core?

I'm trying to figure out a way to use methods in a SELECT query in EF Core to reduce the amount of code that is rewritten over and over again. Instead a simple method could be used to query that piece of data. The query looks like so...
var users = await DbContext.Users.Where(user => user.Id == 1)
.Select(user => new UserDTO
{
Id = user.Id,
Name = user.Name,
Unavailable = user.Tasks
.Any(task => task.HasTasksToday()),
}).ToListAsync();
The HasTasksToday() code looks like so...
public bool HasTasksToday()
{
return Tasks.Any(task => task.StartedAt.Date == DateTime.Now.Date);
}
The problem is I get a...
...could not be translated. Either rewrite the query in a form that can be translated...
...error.
I know that using IQueryable may work for this but I am unsure how that would even happen in the select statement as I know only how to use IQueryable on the Users entity.

Entity Framework 6 IN condition with multi-dimensional object

I have a list of objects that have many properties. I would like to write a EF6 query that pulls all records matching a specific property in the object list.
This is what I have a it is not working.
userRoles is my List<> and RoleID is the property I want to check against the RoleId of the entity.
List<DataAccess.Entities.DB.StorageContainerRole>
containerRoles = db.StorageContainerRoles
.Where(x => userRoles.Select(y => y.RoleID.Value)
.Contains(x.RoleId.Value))
.Include(z => z.StorageContainer)
.ToList();
This is the error I am getting:
Unable to create a constant value of type 'DataAccess.Entities.DB.UserRole'. Only primitive types or enumeration types are supported in this context.
Any help would be appreciated!
When you have the list of the user roles already fetched, you can build a list of ids and check this list in your query:
IList<int> userRoleIds = userRoles.Select(it => it.RoleId.Value).ToList();
IList<StorageContainerRole> containerRoles = db.StorageContainerRoles
.Include(z => z.StorageContainer)
.Where(x => userRoleIds.Contains(x.RoleId))
.ToList();
EF should translate your query to SQL and it doesn't know what to do with complex (non Primitive) structures.
So for your example you can do userRoles.Select(y => y.RoleID.Value) outside of your query this will gives you collection of Primitive types, that should be EF can parse.
In more complex cases, e.g. x.RoleId == a && x.RoleType == b you should force EF to build SQL clause as OR chain: WHERE (roleid = 1 AND roletype = 7) OR (roleid = 2 AND roletype = 8) check this link it explains how to build OR query on a simple example with extensions code provided

NOT IN Condition in Linq

I have a simple scenario.I want to list out all the employees except the logged in user.
Similar SQL Condition is
select * from employee where id not in(_loggedUserId)
How can I acheive the above using LINQ.I have tried the following query but not getting the desired list
int _loggedUserId = Convert.ToInt32(Session["LoggedUserId"]);
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(_loggedUserId)
.ToList();
Except expects argument of type IEnumerable<T>, not T, so it should be something like
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id)
.Except(new[] {_loggedUserId})
.ToList();
Also note, this is really redundant in the case when exclusion list contains only one item and can be replaces with something like .Where(x => x != _loggedUserId)
Why not use a very simple Where condition?
_empIds = _cmn.GetEmployeeCenterWise(_loggedUserId).Where(e=>e.Id != _loggedUserId).ToList();
The title of your question is how to perform a not in query against a database using LINQ. However, as others have pointed out your specific problem is better solved by a using users.Where(user => user.Id != loggedInUserId).
But there is still an answer on how to perform a query against a database using LINQ that results in NOT IN SQL being generated:
var userIdsToFilter = new[] { ... };
var filteredUsers = users.Where(user => !userIdsToFilter.Contains(user.Id));
That should generate the desired SQL using either Entity Framework or LINQ to SQL.
Entity Framework also allows you to use Except but then you will have to project the sequence to ID's before filtering them and if you need to original rows you need to fetch them again from the filtered sequence of ID's. So my advice is use Where with a Contains in the predicate.
Use LINQ without filtering. This will make your query execute much faster:
List<int> _empIds = _cmn.GetEmployeeCenterWise(_loggedUserId)
.Select(e => e.Id).ToList();
Now use List.Remove() to remove the logged-in user.
_empIds.Remove(_loggedUserId);

Linq to sql expression tree execution zone issue

I have got a bit of an issue and was wondering if there is a way to have my cake and eat it.
Currently I have a Repository and Query style pattern for how I am using Linq2Sql, however I have got one issue and I cannot see a nice way to solve it. Here is an example of the problem:
var someDataMapper = new SomeDataMapper();
var someDataQuery = new GetSomeDataQuery();
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.Select(x => someDataMapper.Map(x));
return results.Where(x => x.SomeMappedColumn == "SomeType");
The main bits to pay attention to here are Mapper, Query, Repository and then the final where clause. I am doing this as part of a larger refactor, and we found that there were ALOT of similar queries which were getting slightly different result sets back but then mapping them the same way to a domain specific model. So take for example getting back a tbl_car and then mapping it to a Car object. So a mapper basically takes one type and spits out another, so exactly the same as what would normally happen in the select:
// Non mapped version
select(x => new Car
{
Id = x.Id,
Name = x.Name,
Owner = x.FirstName + x.Surname
});
// Mapped version
select(x => carMapper.Map(x));
So the car mapper is more re-usable on all areas which do similar queries returning same end results but doing different bits along the way. However I keep getting the error saying that Map is not able to be converted to SQL, which is fine as I dont want it to be, however I understand that as it is in an expression tree it would try to convert it.
{"Method 'SomeData Map(SomeTable)' has no supported translation to SQL."}
Finally the object that is returned and mapped is passed further up the stack for other objects to use, which make use of Linq to SQL's composition abilities to add additional criteria to the query then finally ToList() or itterate on the data returned, however they filter based on the mapped model, not the original table model, which I believe is perfectly fine as answered in a previous question:
Linq2Sql point of retrieving data
So to sum it up, can I use my mapping pattern as shown without it trying to convert that single part to SQL?
Yes, you can. Put AsEnumerable() before the last Select:
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.AsEnumerable()
.Select(x => someDataMapper.Map(x));
Please note, however, that the second Where - the one that operates on SomeMappedColumn - will now be executed in memory and not by the database. If this last where clause significantly reduces the result set this could be a problem.
An alternate approach would be to create a method that returns the expression tree of that mapping. Something like the following should work, as long as everything happening in the mapping is convertible to SQL.
Expression<Func<EntityType, Car>> GetCarMappingExpression()
{
return new Expression<Func<EntityType, Car>>(x => new Car
{
Id = x.Id,
Name = x.Name,
Owner = x.FirstName + x.Surname
});
}
Usage would be like this:
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.Select(GetCarMappingExpression());

Categories

Resources