I've tried to mock DbFunctions.Like function by following most popular answer from this ticket and creating it's local implementation like this:
public static class DbFunctions
{
[DbFunction("Edm", "TruncateTime")]
public static DateTime? TruncateTime(DateTime? dateValue)
=> dateValue?.Date;
[DbFunction("Edm", "Like")]
public static bool Like(string searchString, string likeExpression)
=> Regex.IsMatch(searchString, $"^{likeExpression.Replace("%", ".*")}$");
[DbFunction("Edm", "Right")]
public static string Right(string stringArgument, long? length)
=> stringArgument.Substring(stringArgument.Length - ((int?) length ?? 0));
}
And using this function instead of System.Entity.DbFunctions in queries:
var query = Context.Items.AsQueryable();
if (!string.IsNullOrWhiteSpace(Number))
{
var valuesToSearch = Number.Split(';')
.Select(number => number.Trim())
.AsEnumerable();
query = query.Where(x => valuesToSearch.Any(v => DbFunctions.Like(x.Number, v)));
}
It works fine for e.g. "TruncateTime" or "Right" functions.
When I'm debugging the solution the sql versions of the functions are invoked and when I'm running unit tests the local one is invoked and tests are passing.
When it comes to "Like" I'm still getting NotSupportedException:
Is it impossible to mock DbFunctions.Like in the same manner like other system functions?
I'm using EF6 v6.4.4, Moq v4.14.1 and nUnit v3.12.0.
DbFunctions.Like does not have the DbFunctionAttribute, and therefore can't be mocked that way. You can, as a workaround, use SqlFunctions.PatIndex. PatIndex will return the position of the first occurrence of the given pattern in a string or 0 if it does not occur at all.
[DbFunction("SqlServer", "PATINDEX")]
public static int? Like(string searchString, string likeExpression)
=> Regex.IsMatch(searchString, $"^{likeExpression.Replace("%", ".*")}$") ? 1 : 0;
and
query = query.Where(x => valuesToSearch.Any(v => DbFunctions.Like(x.Number, v) > 0));
could work for you. It's not great in terms of readability though.
Related
It seems that for some reason, Microsoft has created an interface for it's messenger and then gone and implemented the logic as extension methods on the interface itself.
Unfortunately, I cannot use this beautiful solution: http://agooddayforscience.blogspot.com/2017/08/mocking-extension-methods.html - because IMessenger extensions calls implemented code on Messenger with an internal type as argument.
Why would Microsoft go to such lengths to make unit testing hard? (If you know a good, technical reason for this, please comment with the answer. I am very curious).
I want to unit test the ViewModels, which injects IMessenger. So how do I do this?
My solution is: Wrap IMessenger in a wrapper with an interface and inject that instead.
Is there a simpler/better solution? (I want it to be easy to understand and maintain).
Moq is incredibly extensible, and provides an extension point for exactly this purpose. You can provide custom type matching logic by creating a type that implements ITypeMatcher. To match the IMessenger.Send signature, for instance:
[TypeMatcher]
public sealed class IsAnyToken : ITypeMatcher, IEquatable<IsAnyToken>
{
public bool Matches(Type typeArgument) => true;
public bool Equals(IsAnyToken? other) => throw new NotImplementedException();
}
You can use this to write Setup and Verify code exactly like It.IsAnyType:
mockMessenger.Setup(x => x.Send(It.IsAny<MyMessage>(), It.IsAny<IsAnyToken>());
...
mockMessenger.Verify(x => x.Send(It.IsAny<MyMessage>(), It.IsAny<IsAnyToken>(), Times.Once);
Wrapping the interface (as others have suggested) is certainly an answer, but I don't like the cognitive overhead of having two interfaces so I came up with this. It does use a bit of reflection, but I couldn't find any other way to get at that object.
Long story short, it manually builds the setup expression against the internal type:
// Compile error, because Unit is internal
mock.Setup(x => x.Send<MyMessage, Unit>(It.IsAny<MyMessage>(), default));
But reflection and expression trees can still give it to us:
private static Type UnitType { get; } = typeof(IMessenger).Assembly
.GetType("Microsoft.Toolkit.Mvvm.Messaging.Internals.Unit");
public static ISetup<IMessenger, TMessage> SetupMessage<TMessage>(this Mock<IMessenger> messenger,
Expression<Func<TMessage, bool>>? validation = null)
where TMessage : class
{
MethodInfo sendMethod = typeof(IMessenger).GetMethod(nameof(IMessenger.Send))
.MakeGenericMethod(typeof(TMessage), UnitType);
ParameterExpression parameter = Expression.Parameter(typeof(IMessenger));
Expression<Func<TMessage>> message = validation switch
{
null => () => It.IsAny<TMessage>(),
not null => () => It.Is(validation),
};
MethodCallExpression methodCall = Expression.Call(
parameter,
sendMethod,
message.Body,
Expression.Default(UnitType));
Type funcType = Expression.GetFuncType(typeof(IMessenger), typeof(TMessage));
Expression lambda = Expression.Lambda(funcType, methodCall, parameter);
return messenger.Setup((Expression<Func<IMessenger, TMessage>>) lambda);
}
I'm looking for a way to merge multiple expression trees in order to build selectors for an Entity Framework query. The query knows which columns to select based on user-provided parameters. For example, a basic query returns ID/Name columns of an entity. If a parameter is explicitly set to also retrieve the Description column, then the query will return ID/Name/Description.
So, what I need it the code for the MergeExpressions method in the following code.
Expression<Func<T, TDto>> selector1 = x => new TDto
{
Id = x.Id,
Name = x.Name
}
Expression<Func<T, TDto>> selector2 = x => new TDto
{
Description = x.Description
}
var selector = selector1;
if (includeDescription)
selector = MergeExpressions(selector1, selector2);
var results = repo.All().Select(selector).ToList();
Thank you.
Not sure for general case, but merging MemberInitExpression bodied lambdas like in your sample is relatively easy. All you need is to create another MemberInitExpression with combined Bindings:
static Expression<Func<TInput, TOutput>> MergeExpressions<TInput, TOutput>(Expression<Func<TInput, TOutput>> first, Expression<Func<TInput, TOutput>> second)
{
Debug.Assert(first != null && first.Body.NodeType == ExpressionType.MemberInit);
Debug.Assert(second != null && second.Body.NodeType == ExpressionType.MemberInit);
var firstBody = (MemberInitExpression)first.Body;
var secondBody = (MemberInitExpression)second.Body.ReplaceParameter(second.Parameters[0], first.Parameters[0]);
var body = firstBody.Update(firstBody.NewExpression, firstBody.Bindings.Union(secondBody.Bindings));
return first.Update(body, first.Parameters);
}
Note that the lambda expressions must be bound to one and the same parameters, so the above code uses the following parameter replacer helper to rebind second lambda body to the first lambda parameter:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
Check out PredicateBuilder.
Example:
Expression<Func<Customer, bool>> expr1 = (Customer c) => c.CompanyName.StartsWith("A");
Expression<Func<Customer, bool>> expr2 = (Customer c) => c.CompanyName.Contains("B");
var expr3 = PredicateBuilder.And(expr1, expr2);
var query = context.Customers.Where(expr3);
or
var expr3 = expr1.And(expr2);
var query = context.Customers.Where(expr3);
I do this kind of thing with extension methods. Its syntactically a bit nicer than using expression trees everywhere. I call this composable repositories.
I also wrote a tool (LinqExpander) to combine the expression trees of different extension methods togeather, which is especially useful for doing projection (selects) from your database. This is only nessacary when you are doing things with sub-entities. (see my post here: Composable Repositories - Nesting extensions)
usage would be something along the lines of:
var dtos = context.Table
.ThingsIWant() //filter the set
.ToDtos() //project from database model to something else (your Selector)
.ToArray();//enumerate the set
ToDtos might look something like:
public static IQueryable<DtoType> ToDtos(this IQueryable<DatabaseType> things)
{
return things.Select(x=> new DtoType{ Thing = x.Thing ... });
}
You want to merge two selects togeather (im assuming to avoid an underfetch but this seems a bit wierd). I would do this by using a projection like this:
context.Table
.AsExpandable()
.Select(x=>new {
Dto1 = x.ToDto1(),
Dto2 = x.ToDto2()
})
.ToArray();
if you really wanted it to return a single entity like this you could probably do something like:
context.Table
.AsExpandable()
.Select(x=> ToDto1(x).ToDto2(x));
but I havent ever tried this.
As this uses a sub projection you will need the .AsExpandable extensions.
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);
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.
We have a class called Task:
public partial class Task : EntityObject
{
public EntityCollection<TaskUser> TaskUsers { get {...} set{...} }
}
It has navigation property called TaskUsers, which contains users attached to this taks:
public partial class TaskUser : EntityObject
{
public User User { get {...} set { } }
}
Every TaskUser object has User object.
We are given IQueryable<Task> tasks. We want to find tasks assigned to user with ID = 1. When we use
tasks.Where(t => t.TaskUsers.Any(a => a.User.ID == 1))
everything works fine. When we use
Func<TaskUser, bool> function = a => a.User.ID == 1;
return tasks.Where(t => t.TaskUsers.Any(function));
we get nice 'Internal .NET Framework Data Provider error 1025' error. Why? I want to build much more complicated filters using Expression class, but if I can't pass simple Func, this can't be done. What should I do?
EDIT
Maybe
Func<TaskUser, bool> function = a => a.User.ID == 1;
return tasks.Where(t => t.TaskUsers.Any(function));
doesn't work, but
Expression<Func<TaskUser, bool>> expression = a => a.User.ID == 1;
return tasks.Where(t => t.TaskUsers.AsQueryable().Any(expression));
works! That is all I needed.
Well the EF can only translate Expressions, not functions.
i.e. it can translate this:
Expression<Func<TaskUser,bool>>
but not this:
Func<TaskUser,bool>
As for how to merge expressions (in pseudo code):
Expression<Func<TaskUser, bool>> expression = a => a.User.ID == 1;
return tasks.Where(t => t.TaskUsers.Any(expression));
There are probably some Expression guru's who can help with that.
I suggest a followup question focused on that particular problem
Alex