I am currently trying to limit which users can access which groups, and am doing this by using linq. My problem occours when I am adding the relevant groups to the view.
The error I keep getting is:
LINQ to Entities does not recognize the method 'System.String
GetUserId(System.Security.Principal.IIdentity)' method, and this
method cannot be translated into a store expression
This is my code:
var groupUser = db.GroupUsers.Where(u => u.ApplicationUserId == User.Identity.GetUserId()).Select(gr => gr.GroupId);
pv.GroupList = db.Groups.Where(g => groupUser.Contains(g.Id)).ToList();
return View(pv);
You need to call GetUserId() outside the lambda expression, as Linq to Entities is not able to translate it to corresponding SQL (of course there is no SQL alternative for GetUserId()):
var userId = User.Identity.GetUserId();
var groupUser = db.GroupUsers.Where(u => u.ApplicationUserId == userId)
.Select(gr => gr.GroupId);
Related
I using asp.net boilerplate and try to filter users via bool flag and permissions.
Here is code of method
private async Task<List<User>> GetAdminUsers()
{
using (UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
{
var adminUsers = await _userManager.Users.Where(x => x.IsFuelAdmin && _permissionChecker
.IsGranted(new UserIdentifier(null, x.Id),
FuelAdministratorPermissions.Pages_Administration_Missing_PreQual_LoanAmount_Email_Notifications))
.ToListAsync();
return adminUsers;
}
}
But it failing with this error
ionHandling.AbpExceptionFilter : The LINQ expression 'DbSet
.Where(u => __ef_filter__p_0 || !(((ISoftDelete)u).IsDeleted) && __ef_filter__p_1 || ((IMayHaveTenant)u).TenantId == __ef_filter__CurrentTenantId_2)
.Where(u => u.IsFuelAdmin)
.Where(u => ___permissionChecker_0.IsGranted(
user: new UserIdentifier(
null,
u.Id
),
permissionName: "Pages.Administration.QualificationChecks.LoanAmount_Email_Notifications"))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
How I can solve this issue?
There is no "IsGranted" function on the db server. You couldn't use IsGranted as a db function.
If you want to use the local C# IsGranted get the adminUsers into a C# local list and filter then for IsGranted:
var adminUsers = await _userManager.Users.Where(x => x.IsFuelAdmin).ToListAsync();
For a full SQL query you have to build a join query with your users over:
userRoleRepository, userPermissionRepository, rolePermissionRepository
Check out the accept answer:
Get all users with specific permission?
You could define additional property - say, IsAdmin - and set it in the runtime before that query.
I am trying to refactor the following line of an Entity Framework query into a generalized static extension method:
dbContext.Employees
.Where(e => permissionResolver.AuthorizedUsers.Select(p => p.Id).Contains(e.Id))
.OrderBy(...)
PermissionResolver is just an instance I receive a list of IDs from to compare against a user ID stored in the current record. It compiles perfectly to a SQL statement WHERE Id IN (....).
Now I am trying to create an extension method for IQueryable<T> that I can use for any type of record, I just want to pass in a property where the owner's ID is stored in.
So that is what I came up with:
public static IQueryable<T> AuthorizedRecords<T>(this IQueryable<T> query, Expression<Func<T, Int32>> property, IPermissionResolver permissionResolver)
{
Expression<Func<T, Boolean>> idIsAuthorized = entity => permissionResolver.AuthorizedUsers.Select(e => e.Id).ToList().Contains(property.Compile()(entity));
return query.Where(idIsAuthorized);
}
I'm getting a runtime error that this expression cannot be translated into SQL.
How can I combine the property expression to the main query expression that it can be translated into SQL correctly? Is there a better way to rewrite the query expression?
property.Compile() converts the expression tree into a delegate, this delegate cannot be properly translated back to an expression tree/SQL.
You need to construct expression tree like that:
var ids = permissionResolver.AuthorizedUsers.Select(e => e.Id).ToList().AsEnumerable();
// method Enumerable.Contains<int>()
var methodContains = typeof(System.Linq.Enumerable).GetMethods()
.Where(m => m.Name == "Contains" && m.GetParameters().Length == 2)
.First()
.MakeGenericMethod(typeof(int));
var lambdaParam = property.Parameters.Single();
var lambda = Expression.Lambda(
Expression.Call(
methodContains,
Expression.Constant(ids),
property.Body),
lambdaParam
);
var predicate = (Expression<Func<T, bool>>)lambda;
return query.Where(predicate);
The short answer is you cannot use custom extension methods in entity framework queries. Under the hood entity framework parses expression tree Expression<Func<>> into sql query. It seems kind of impossible to translate any possible extension method to sql so they support limited set of Linq methods to properly translate it. Some useful information about expression trees: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees.
Some refactoring you can do to simplify your query is to use Any method like this:
dbContext.Employees
.Where(e => permissionResolver.AuthorizedUsers.Any(u => u.Id == e.Id))
.OrderBy(...
My Code:
elections = elections.Where(e => e.Creator == Username || e.Approver == Username || IsUserInCc(e.Cc,Username))
.OrderBy(e => e.Status)
.ThenByDescending(e => e.Group);
var test = elections.FirstOrDefault();
private bool IsUserInCc(string cc, string username)
{
var ccList = cc.Split(';');
if (ccList.Contains(username))
return true;
return LDAPUtility.Instance.IsUserInGroup(ccList.ToList(), username);
}
Error:
LINQ to Entities does not recognize method IsUserInCc.
From many posts, I can understand why error was thrown. Basically IsUserInCc is not available in SQL execution. I need somehow convert it back to C# to handle it.
LINQ to Entities does not recognize my method
LINQ to Entities does not recognize the method in query
LINQ to Entities does not recognize the method 'System.String ToString(Int32)'
However, in my specific case, what is the best approach?
You need to convert to list first. Also note that elections must be able to hold a list for this to run.
elections = elections.ToList().Where(e => e.Creator == Username || e.Approver == Username || IsUserInCc(e.Cc,Username))
.OrderBy(e => e.Status)
.ThenByDescending(e => e.Group);
For your function written in code, you cannot use that on Queryables. You need to convert to in-memory list and then apply the filter required using your function.
The root cause of your issue is that your underlying data isn't normalised properly. You need to put your CC's in a collection, not have them as a single deliniated string.
In SQL you'd need to add a new table called CC or something and put each user name in there and link it back to an election. Or if it's an in-memory collection, add a new property that in its Getter will do the split for you.
Either way, then you won't run into this kind of problem. If your data isn't properly structured, you will create problems for yourself further up the stack.
When you want to send request to databaseusing Linq like:
var query = listData.Where(x=>x.Id == 123);
Type of this query is IQueryable that means your query not Executed yet!
Now you are sending data as IQueryable to method and can not process on your data, you have to Execute that with methods like: Tolist(), ToListAsync() or something like these.
The best way for these is that you get data from database without that method, after that you execute your query, you can Run this method.
GoodLuck.
Can you try like this :
elections = elections.Where(e => e.Creator == Username || e.Approver == Username).Tolist().Where(e => IsUserInCc(e.Cc,Username))
.OrderBy(e => e.Status)
.ThenByDescending(e => e.Group);
var test = elections.FirstOrDefault();
private bool IsUserInCc(string cc, string username)
{
var ccList = cc.Split(';');
if (ccList.Contains(username))
return true;
return LDAPUtility.Instance.IsUserInGroup(ccList.ToList(), username);
}
This is my code:
var qry = (from u in _db.ApplicationUsers
select new UserBroadcastDTO()
{
UserId = u.Id,
DateCreated = u.DateCreated
}).OrderByDescending(q => q.DateCreated);
I have this exception:
LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
How can I convert date format in linq?
Your problem is that formatting doesn't belong in the Linq query itself. The query is concerned with data, not presentation.
Now, if you are trying to project the results of the query into something for display, then make sure you enumerate the collection before doing your formatting. You can call .ToList(), but in most cases, simply adding an .AsEnumerable() will do the trick, drawing a line in the sand between what can be made part of the SQL (or other back-end store) query, and what can't. Then, you add another select to do your projection.
Like this:
var qry = _db.ApplicationUsers
.OrderByDescending(q => q.DateCreated)
.AsEnumerable()
.Select new UserBroadcastDTO
{
UserId = u.Id,
DateCreated = u.DateCreated.ToString("yyyy-MM-dd hh:mm")
});
The .AsEnumerable call can be a .ToList if you want, but .AsEnumerable will work just fine and still defers execution until later if needed, but the important thing is that you're stopping it from being an IQueryable, so anything after that point doesn't try to work its way into the SQL query, and you won't get the error.
This is happening because LINQ to Entities is trying to convert the expression tree into SQL query and .ToString(string) can not be translated into SQL. you should handle the result in your presentation layer for example inside your view in an MVC app:
#Model.DateCreated.ToString("yyyy-MM-dd hh:mm")
ypu need to materialize the query so that can happen in linq to objects linq to entities cannot do that
var qry = _db.ApplicationUsers.OrderByDescending(q => q.DateCreated)
.Select(u => new UserBroadcastDTO {
UserId = u.Id,
DateCreated = u.DateCreated
});
// do your additional logic then materialize the query
var formattedresults = qry.ToList().Select(q => new UserBroadcastDTO {
UserId = q.Id,
DateCreated = q.DateCreated.ToString("yyyy-MM-dd hh:mm")
});
How can I make this lesser than or equal work in my .Where() clause? I am getting an error.
var filteredProducts = Products.Where(p => p.State.Contains("Bruikbaar"))
.Where(p => p.Privilege <= ui.GetPrivilegeNumber())
.ToList();
Error:
LINQ to Entities does not recognize the method 'Int32 GetPrivilegeNumber()' method, and this method cannot be translated into a store expression.
I hope this question is never asked before. Googled couldn't find it either or I am using the wrong words to express my problem.
ui.GetPrivilegeNumber() is not a recognized method.
Use this:
var uiPrivilege = ui.GetPrivilegeNumber();
var filteredProducts = Products.Where(p => p.State.Contains("Bruikbaar"))
.Where(p => p.Privilege <= uiPrivilege)
.ToList();
And as other users mentionted, you can optimize your Where.
EF does not execute method calls which you use in predicates. It stores them as expression (i.e. syntax tree) and then analyzes this tree to build SQL query by translating C# code to SQL code. It cannot translate GetPrivilegeNumber() method call into SQL, because there is no appropriate SQL code for that. So all you need is move this method call out of expression and pass only result of method call instead:
var privilegeNumber = ui.GetPrivilegeNumber();
var filteredProducts = Products.Where(p => p.State.Contains("Bruikbaar"))
.Where(p => p.Privilege <= privilegeNumber)
.ToList();
Now privilegeNumber is just an integer variable which is translated into SQL parameter
SELECT * FROM Products p
WHERE p.State LIKE '%Bruikbaar%' AND p.Privilege <= #privilegeNumber
You need to move ui.GetPrivilegeNumber() outside of the query. You can also merge those Where queries into a single one:
var privilegeNumber = ui.GetPrivilegeNumber();
var filteredProducts = Products.Where(p =>
p.State.Contains("Bruikbaar")
&& p => p.Privilege <= privilegeNumber)
.ToList();
You can use other evaluation method inside LinQ. To simplified the code, you can use it in little old way of writing LinQ.
var uiPrivilege = ui.GetPrivilegeNumber();
var filteredProducts =(from p in Products
where p.State.Contains("Bruikbaar") && p.Privilege <= uiPrivilege
select p).ToList();
The above query generate same output but easy to understood.