Using function result inside expression function used by a predicate - entity-framework

I am trying to use predicateBuilder with next expression definition but I always got the message
"LINQ to Entities does not recognize the method 'puedeConsultar' method, and this method cannot be translated into a store expression."
I think i understand more less this problem, but i don´t know how to solve it.
private static readonly IDictionary<int, List<string>> permisosAccesoSolicitudesEstado = new Dictionary<int, List<string>>(){{0, new List<string>(){"A"}}, {1, new List<string>(){"B"}}};
private static bool esPermisoConcedido(List<string> usuariosPermitidos, string erfilUsuario)
{
return usuariosPermitidos.Any(x => x.Equals(perfilUsuario) || perfilUsuario.StartsWith(x + "|") || perfilUsuario.EndsWith("|" + x));
}
public static bool puedeConsultar(int estadoActual, string perfilUsuario)
{
List<string> usuariosPermitidos = permisosAccesoSolicitudesEstado[estadoActual];
return esPermisoConcedido(usuariosPermitidos, perfilUsuario);
}
public static bool puedeConsultar(string estadoActual, string tipoUsuario)
{
return puedeConsultar(Convert.ToInt32(estadoActual), tipoUsuario);
}
public Expression<Func<Solicitud, Boolean>> predicadoEstadoCorrectoSolicitud(string perfil)
{
return x=> EstadosSolicitud.puedeConsultar(x.estado, perfil);
}
//Instantiated by reflection, this works fine
MethodInfo method = .....
Expression<Func<T, bool>> resultado = ConstructorPredicados.True<T>();
resultado = ConstructorPredicados.And(resultado, method);
objectSet.Where(resultado).ToList();
Note:
ConstructorPredicados is based in Monty´s Gush "A universal PredicateBuilder" on http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/
Thanks in advance

You cannot do that. Your puedeConsultar is .NET function. You cannot execute .NET functions in Linq-to-entities query. When you use method in Linq-to-entities you can use only methods which has direct mapping to SQL. It means that method in the query is only placeholder which is translated to execution of some SQL function. There is set of predefined method mappings called cannonical functions and you can map your own SQL function when using EDMX but in your case you will most probably have to first load data to application by using ToList and after that execute predicadoEstadoCorrectoSolicitud on materialized result.

Related

EFCore The Linq Expression Could not be translated and will be evaluated locally

I am attempting to create a compiled query that can be executed on the database side. The idea is to create a reusable function across my code. But EF Core is still saying this query cannot be translated to sql.
Here is the reusable expression
public static class DisplayFilters
{
public static Expression<Func<DisplayItem, bool>> isActiveItem = (x) => x.IsDeleted == false;
}
Here is the EFCore entity call
using df = namespace.DisplayFilters;
...
[HttpGet]
public IActionResult Get()
{
IEnumerable<DisplayItem> xItem = (from a in _context.DisplayItem.AsQueryable()
where df.isActiveItem.Compile()(a)
select a);
return Ok(xItem);
}
I don't think you can embed Expression type expressions in query comprehension syntax, but you can do it in lambda syntax:
IEnumerable<DisplayItem> xItem = _context.DisplayItem
.Where(df.isActiveItem);

2nd level caching EF codeFirst and Predicate not working on a where clause

I have a question about caching with Entity Framework code first.
I need to cache my query's results and I came about something that I didn't know.
Func<T, bool> predicate does not work when filtering whilst Expression<Func<T, bool>> predicate does.
Maybe I'm missing the obvious.
Here is my scenario:
Whenever I call a method, e.g."GetOrders", I use a method called "GetCachedModels" internally to get the cached version.
When subsequently many calls are made with
"GetOrders(customerNo)" it checks the cache and the get it from there if it's there. That is the theory.
However when using Func predicate it does not find the item, but when using the Expression version it does?
My question is how do you use "Expressions" with a Where clause on a list?
The other solution would be to have a method for each search, e.g. "myservice.GetCustomer(etc..) or myservice.GetOrders(etc..) instead of generic
myservice.GetAll(); Which means adding many methods to the interface.
mycode:
public interface IGenericRepository
{
IList<T> GetAll<T>() where T : class;
IList<T> Find<T>(Func<T, bool> predicate) where T : class; //I use this so that I could use the predicate in an where clause against a list.
etc....
}
In my repository I have something like:
public IList<T> Find<T>(Func<T, bool> predicate) where T : class
{
List<T> models = GetCachedModels<T>().ToList();
var result= models.Where(predicate).ToList(); --Does not work!!! tried also with(expression.Compile()).ToList(); !!still no results!!!!
return result;
}
internal IList<T> GetCachedModels<T>() where T : class
{
IList<T> models;
Type typeParameterType = typeof(T);
string cacheName = string.Format("{0}Cache", typeParameterType.Name);
object cacheView = DbCache.Get(cacheName);
if (cacheView == null)
{
models = Set<T>().ToList();
DbCache.Add(cacheName, models, DateTime.Now.AddHours(1));
}
else
{
models = (IList<T>)cacheView;
}
return models;
}
//below method works but cannot use cache!!!!
public IList<T> Find<T>(Expression<Func<T, bool>> predicate) where T : class
{
return Set<T>().Where(predicate).ToList();
}
Expression predicates works only for IQueryable interface. List doesn't inherit it, so if you want to use this expression, you need to return IQueryable in GetCachedModels method, and return Set, so it can query this data. And then you can place it.
Otherwise if you want to cache all items from Set, then you need to pass Func instead of Expression, and then use it in Where extension method, like this - http://dotnetfiddle.net/5YsIy3

Entity Framework non generic Include on dynamic ObjectSet

I've ran into a problem while creating an data access layer for my application. I am working with derived entities in Entity Framework. If I try to create an ObjectSet for a derived Entity, I get this Exception:
Blockquote There are no EntitySets defined for the specified entity type 'Type Name'. If 'Type Name' is a derived type, use the base type instead.
I've tried to resolve this problem via Reflection. (if the Entity Type is derived by an Entity Type => get an ObjectSet for the base Type)
I found this: How can I obtain ObjectSet<T> from Entity-Framework at runtime where T is dynamic? but I haven't found how to use Include and Where on the built up ObjectSet.
protected IEnumerable<IDataObject> GetData(Type entityType, Expression<Func<dynamic, bool>> whereClause, Expression<Func<dynamic, dynamic>>[] includes)
{
if (typeof(IDataObject).IsAssignableFrom(entityType.BaseType))
{
return GetData(entityType.BaseType, whereClause, includes);
}
var contextType = this.Context.GetType();
MethodInfo createObjectSetMethod = contextType.GetMethod("CreateObjectSet", new Type[] {}).MakeGenericMethod(entityType);
// Builds up an ObjectSet<EntityType>
dynamic objectSet = createObjectSetMethod.Invoke(this.Context, new object[] { });
dynamic query = objectSet;
if (includes != null)
{
foreach (var include in includes)
{
query = query.Include(include);
}
}
if (whereClause == null)
{
whereClause = (item) => true;
}
query = query.Where(whereClause);
return query.ToList().OfType<IDataObject>();
}
The code runs as intended, as long I don't use the Includes and WhereClause.
When I call this function I dont know the resolved ObjectSet (T-Parameter) at compile time.
Is there any way to use dynamic Expressions in an generic ObjectSet?
Thanks in advance.
The issue looks to be with the
query = query.Include(include);
the var include would be an Expression<Func<dynamic,dynamic>>
The Include Method you access is expecting a string or
Expression<Func<T, TProperty>>
Have you tried to pass the include as a string path? I would expect that to work.
Otherwise you need to construct the expression tree with code
since you cant pass Dynamic objects to
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class;
There is also dynamic linq library to can check out.
System.Linq.Dynamic can be found at following links
http://msdn.microsoft.com/en-US/vstudio/bb894665.aspx
with an intro here http://www.scottgu.com/blogposts/dynquery/dynamiclinqcsharp.zip
This is an easier alternative using text to build expression trees.
Building expressions trees with code is more powerful and flexible but harder.

QueryBuider get parameters for Dao.queryRaw

I'm using QueryBuider to create raw query, but I need to fill parameters to raw query manually.
Properties 'from' and 'to' are filled two times. One in 'where' section of QueryBuider, and one in queryRaw method as parameters.
Method StatementBuilder.prepareStatementString() returns query string with "?" for substitution.
Is there any way to get these parameters directly from QueryBuider instance?
For example, imagine a new method in ormlite - StatementBuilder.getPreparedStatementParameters();
QueryBuilder<AccountableItemEntity, Long> accountableItemQb = accountableItemDao.queryBuilder();
QueryBuilder<AccountingEntryEntity, Long> accountingEntryQb = accountingEntryDao.queryBuilder();
accountingEntryQb.where().eq(
AccountingEntryEntity.ACCOUNTING_ENTRY_STATE_FIELD_NAME,
AccountingEntryStateEnum.CREATED);
accountingEntryQb.join(accountableItemQb);
QueryBuilder<AccountingTransactionEntity, Long> accountingTransactionQb =
accountingTransactionDao.queryBuilder();
accountingTransactionQb.selectRaw("ACCOUNTINGENTRYENTITY.TITLE, " +
"ACCOUNTINGENTRYENTITY.ACCOUNTABLE_ITEM_ID, " +
"SUM(ACCOUNTINGENTRYENTITY.COUNT), " +
"SUM(ACCOUNTINGENTRYENTITY.COUNT * CONVERT(ACCOUNTINGENTRYENTITY.PRICEAMOUNT,DECIMAL(20, 2)))");
accountingTransactionQb.join(accountingEntryQb);
accountingTransactionQb.where().eq(
AccountingTransactionEntity.ACCOUNTING_TRANSACTION_STATE_FIELD_NAME,
AccountingTransactionStateEnum.PRINTED)
.and().between(AccountingTransactionEntity.CREATE_TIME_FIELD_NAME, from, to);
accountingTransactionQb.groupByRaw(
"ACCOUNTINGENTRYENTITY.ACCOUNTABLE_ITEM_ID, ACCOUNTINGENTRYENTITY.TITLE");
String query = accountingTransactionQb.prepareStatementString();
accountingTransactionQb.prepare().getStatement();
Timestamp fromTimestamp = new Timestamp(from.getTime());
Timestamp toTimestamp = new Timestamp(to.getTime());
//TODO: get parameters from accountingTransactionQb
GenericRawResults<Object[]> genericRawResults =
accountingEntryDao.queryRaw(query, new DataType[] { DataType.STRING,
DataType.LONG, DataType.LONG, DataType.BIG_DECIMAL },
fromTimestamp.toString(), toTimestamp.toString());
Is there any way to get these parameters directly from QueryBuider instance?
Yes, there is a way. You need to subclass QueryBuilder and then you can use the appendStatementString(...) method. You provide the argList which then can be used to get the list of arguments.
protected void appendStatementString(StringBuilder sb,
List<ArgumentHolder> argList) throws SQLException {
appendStatementStart(sb, argList);
appendWhereStatement(sb, argList, true);
appendStatementEnd(sb, argList);
}
For example, imagine a new method in ormlite - StatementBuilder.getPreparedStatementParameters();
Good idea. I've made the following changes to the Github repo.
public StatementInfo prepareStatementInfo() throws SQLException {
List<ArgumentHolder> argList = new ArrayList<ArgumentHolder>();
String statement = buildStatementString(argList);
return new StatementInfo(statement, argList);
}
...
public static class StatementInfo {
private final String statement;
private final List<ArgumentHolder> argList;
...
The feature will be in version 4.46. You can build a release from current trunk if you don't want to wait for that release.

Combining Includes with Entity Framework

I generally use a generic repository to boilerplate my EF queries so I have to write limited code and also use caching. The source code for the repository can be found here.
The backbone query within the code is this one below. FromCache<T>() is an IEnumerable<T> extension method that utilizes the HttpContext.Cache to store the query using a stringified representation of the lambda expression as a key.
public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null)
where T : class, new()
{
// Check for a filtering expression and pull all if not.
if (expression == null)
{
return this.context.Set<T>()
.AsNoTracking()
.FromCache<T>(null)
.AsQueryable();
}
return this.context.Set<T>()
.AsNoTracking<T>()
.Where<T>(expression)
.FromCache<T>(expression)
.AsQueryable<T>();
}
Whilst this all works it is subject to the N+1 problem for related tables since If I were to write a query like so:
var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false)
.Include(p => p.Author);
The Include() will have no effect on my query since it has already been run in order to be cached.
Now I know that I can force Entity Framework to use eager loading within my model by removing the virtual prefix on my navigation properties but that to me feels like the wrong place to do it as you cannot predict the types of queries you will be making. To me it feels like something I would be doing in a controller class. What I am wondering is whether I can pass a list of includes into my Any<T>() method that I could then iterate though when I make the call?
ofDid you mean something like...
IQueryable<T> AnyWithInclude<T,I>(Expression<Func<T,bool>> predicate,
Expression<Func<T,I>> includeInfo)
{
return DbSet<T>.where(predicate).include(includeInfo);
}
the call
Context.YourDbSetReference.AnyWithInclude(t => t.Id==someId, i => i.someNavProp);
In response to extra question on as collection.
I realised late, there was an overload on Property. You can just pass a string
This might work but call is not easy. Well I find it hard.
IQueryable<T> GetListWithInclude<I>(Expression<Func<T, bool>> predicate,
params Expression<Func<T, I>>[] IncludeCollection);
so i tried
public virtual IQueryable<T> GetListWithInclude(Expression<Func<T, bool>> predicate,
List<string> includeCollection)
{ var result = EntityDbSet.Where(predicate);
foreach (var incl in includeCollection)
{
result = result.Include(incl);
}
return result;
}
and called with
var ic = new List<string>();
ic.Add("Membership");
var res = context.DbSte<T>.GetListWithInclude( t=>t.UserName =="fred", ic);
worked as before.
In the interest of clarity I'm adding the solution I came up with based upon #soadyp's answer.
public IQueryable<T> Any<T>(Expression<Func<T, bool>> expression = null,
params Expression<Func<T, object>>[] includeCollection)
where T : class, new()
{
IQueryable<T> query = this.context.Set<T>().AsNoTracking().AsQueryable<T>();
if (includeCollection.Any())
{
query = includeCollection.Aggregate(query,
(current, include) => current.Include(include));
}
// Check for a filtering expression and pull all if not.
if (expression != null)
{
query = query.Where<T>(expression);
}
return query.FromCache<T>(expression, includeCollection)
.AsQueryable<T>();
}
Usage:
// The second, third, fourth etc parameters are the strongly typed includes.
var posts = this.ReadOnlySession.Any<Post>(p => p.IsDeleted == false,
p => p.Author);