I have the following method within a repository that I am trying to Mock:
IEnumerable<TEntity> GetAll(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
I've setup the following:
mockContactNumberRepository.Setup(x => x.GetAll(
It.IsAny<Expression<Func<ContactNumber, bool>>>(),
It.IsAny<Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>>(),
It.IsAny<string>()))
.Returns(new Func<Expression<Func<ContactNumber, bool>>,
IQueryable<ContactNumber>>(ex => _contactNumbers.Where(ex.Compile()).AsQueryable()));
When running the unit test I receive an error message about Parameter count mismatch. I understand this is because the Returns is only specifying the first parameter but I am unsure how to specify the further parameters.
I've found many questions that ask similar questions but not found one with multiple lambda expressions.
Any help you can give would be much appreciated.
Your GetAll method takes three arguments and returns an IEnumerable<TEntity>. The valueFunction parameter in Returns needs to have a matching signature and return type. The valueFunction parameter in your example only has two input arguments and the second argument does not match any of the argument types passed to GetAll. It should look something like this (I don't have the benefit of the compiler checking my syntax but I think what I have here should be correct):
mockContactNumberRepository
.Setup(x =>
x
.GetAll(
It.IsAny<Expression<Func<ContactNumber, bool>>>(),
It.IsAny<Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>>(),
It.IsAny<string>()))
.Returns(new Func<
Expression<Func<ContactNumber, bool>>,
Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>,
string,
IEnumerable<TEntity>>((arg1, arg2, arg3) =>
{
// arg1 is Expression<Func<ContactNumber, bool>>
// arg2 is Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>
// arg3 is string
// Do something here and return an IEnumerable<TEntity>
}));
Related
Here is what I am trying to do. I have done a few simple expressions, but this one is a little too much for me right now.
public static Expression<Func<IQueryable<TEntityType>, IOrderedQueryable<TEntityType>>> SortMeDynamically<TEntityType>(bool isAsc, string propertyname)
{
var param = Expression.Parameter(typeof(TEntityType), "x");
var prop = Expression.PropertyOrField(param, propertyname);
var sortLambda = Expression.Lambda(prop, param);
string sortOrder = isAsc ? "OrderBy" : "OrderByDescending";
var selector = Call(
typeof(Queryable),
sortOrder,
new[] { prop.Type},
sortLambda);
var lambda = Lambda<Func<IQueryable<TEntityType>, IOrderedQueryable<TEntityType>>>(selector, param);
return lambda;
}
The error I get is the following.
System.InvalidOperationException: No generic method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic
The closest solution I found was the one below.
LINQ to Entities OrderBy Expression Tree
Calling the various .OrderBy linq methods dynamically is quite a pain.
The most difficult part of this process is locating the MethodInfo of the four relevant Queryable.OrderBy methods, with the correct generic constraints. There are a number of ways to achieve this.
You could pull the method out of a template Expression<Func<...>>, as in your linked answer;
Expression<Func<IOrderedEnumerable<TEntityType>>> sortMethod =
(() => query.OrderBy<TEntityType, object>(k => null));
var methodCallExpression = (sortMethod.Body as MethodCallExpression);
var method = methodCallExpression.Method.GetGenericMethodDefinition();
You could use Type.GetMethods and filter the results. Though you'd have to worry about future runtime changes breaking this approach.
var method = typeof(Queryable).GetMethods()
.Where(m => m.IsGenericMethod
&& m.Name == nameof(Queryable.OrderBy)
&& m.GetParameters().Length == 2)
.Single();
In both cases you'd then need to call .MakeGenericMethod to supply the correct generic parameters.
var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
Or you could create a delegate and pull the method from there. Again, getting the generic constraints correct is a bit fiddly. But can be easier with a helper method. Which is similar to how the linq runtime locates this MethodInfo.
public MethodInfo GetOrderFunc<T, V>(Func<IQueryable<T>, Expression<Func<T, V>>, IOrderedQueryable<T>> func)
=> func.Method;
var genericSortMethod = GetOrderFunc<TEntityType, V>(Queryable.OrderBy);
If you don't know the argument value type, you could call that method via reflection.
Now you can either invoke the method;
var orderedQuery = (IOrderedQueryable<TEntityType>)genericSortMethod.Invoke(null, new object[] { query, sortLambda });
Or by reading the source code, recreate what those methods actually do.
var expression = query.Expression;
expression = Expression.Call(
typeof(Queryable),
genericSortMethod,
new Type[] { typeof(TEntityType), prop.Type },
expression,
Expression.Quote(sortLambda));
var orderedQuery = (IOrderedQueryable<T>)query.Provider.CreateQuery<T>(expression);
No matter how you approach this, you need to take an IQueryable<TEntity> query parameter, and return an IOrderedQueryable<TEntity>. Or just stop at creating the Expression<Func<>>.
The other, other option is to move all the generic mucking around into a helper method. Then invoke that method via reflection. Obtaining the generic MethodInfo using one of the same approaches explained above.
public IOrderedQueryable<T> Order<T, V>(this IQueryable<T> query, Expression<Func<T, V>> key, bool then, bool desc)
=> (desc)
? (then ? ((IOrderedQueryable<T>)query).ThenByDescending(key) : query.OrderByDescending(key))
: (then ? ((IOrderedQueryable<T>)query).ThenBy(key) : query.OrderBy(key));
I'm using Rhino Mocks to unit test a service method that makes the following call:
var form = Repo.GetOne<Form>(f => f.Id == formId, "FormTemplate, Event");
The Repo.GetOne method signature is:
TEntity GetOne<TEntity>(
Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null)
where TEntity : class, IEntity;
So far I have only managed to do this by ignoring the Function Expression argument:
_mockRepo.Stub(
r =>
r.GetOne<Form>(
Arg<Expression<Func<Form, bool>>>.Is.Anything,
Arg<string>.Is.Equal("FormTemplate, Event")))
.Return(form);
How do I stub the Repo.GetOne method in order to set the return value when the method is called with arguments f => f.Id == formId, "FormTemplate, Event"?
When you setup Stub/Mocks, the value used for the arguments must return true when you call mockArg.Equals(runtimeArg). Your Func<> isn't going to do that, so you're best bet is to use the Arg<T>.Matches() constraint, which takes a function that, given the stub inputs, returns true if the runtime input are a match.
Unfortunately, looking at the contents of the Func<T> delegate is not straightforward. (Take a look at https://stackoverflow.com/a/17673890/682840)
var myArgChecker = new Function<Expression<Func<Form, bool>>, bool>(input =>
{
//inspect input to determine if it's a match
return true; //or false if you don't want to apply this stub
});
_mockRepo.Stub(
r =>
r.GetOne<Form>(
Arg<Expression<Func<Form, bool>>>.Matches(input => myArgChecker(input)),
Arg<string>.Is.Equal("FormTemplate, Event")))
.Return(form);
I'm building a repository framework with NHibernate. Part of my requirements include limiting the number of results returned- or forcing paging. In order for NHibernate to successfully parse the query from the IQueryable expression, it must perform OrderBy or OrderByDescending operations before any .Skip().Take() operation.
This is how my current signature is called.
var actualExerciseJournals = repo.GetAll( pageIndex: pageNum++, recordsPerPage: 250, orderByFirst: exerciseJournal => exerciseJournal.JOURNALDATE, orderBySecond: exerciseJournal => exerciseJournal.MEMBERID);
This is the interface signature.
IEnumerable<TEntity> GetAll(int pageIndex, int recordsPerPage, Expression<Func<TEntity, object>> orderByFirst, Expression<Func<TEntity, object>> orderBySecond, Expression<Func<TEntity, object>> orderByThird, Expression<Func<TEntity, object>> orderByFourth, params Expression<Func<TEntity, object>>[] include);
(The include argument is not relevant for the question)
Is it possible to have a signature that would accept something like this as the parameter?
var actualExerciseJournals = repo.GetAll(pageIndex: 0, recordsPerPage: 250, collectionToOrder => collectionToOrder.OrderBy(x => x.JOURNALDATE).ThenBy(y => y.MEMBERID));
Then I would be able to apply this expression within my repository and I would no longer have any limits on the number of orderbys or whether it's orderbydescending or what not.
Thanks
The lambda expression is just a function. So you should be able to use Func<IEnumerable<TEntity>,IEnumerable<TEntity>>. You are effectivly wanting to pass in a function that takes in an IEnumerable and give out a different IEnumerable.
How does one use Type.GetMethod() to get a method with lambda parameters? I'm trying to get the Queryable.Any method for a parameter of something like Func, using this:
typeof(Queryable).GetMethod("Any", new Type[]{typeof(Func<ObjType, bool>)})
but it keeps returning null.
There are four things wrong:
There's no such thing as a "lambda parameter". Lambda expressions are often used to provide arguments to methods, but they're converted into delegates or expression trees
You've missed off the first parameter of Queryable.Any - the IQueryable<T>
You're using Func<ObjType, bool> which is a delegate type, instead of Expression<Func<ObjType, bool>> which is an expression tree type
You can't get at generic methods in quite that way, unfortunately.
You want:
var generic = typeof(Queryable).GetMethods()
.Where(m => m.Name == "Any")
.Where(m => m.GetParameters().Length == 2)
.Single();
var constructed = generic.MakeGenericMethod(typeof(ObjType));
That should get you the relevant Any method. It's not clear what you're then going to do with it.
I have an XElement with values for mock data.
I have an expression to query the xml:
Expression<Func<XElement, bool>> simpleXmlFunction =
b => int.Parse(b.Element("FooId").Value) == 12;
used in:
var simpleXml = xml.Elements("Foo").Where(simpleXmlFunction).First();
The design time error is:
The type arguments for method 'System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly'
The delegate supplied to Where should take in an XElement and return a bool, marking if the item matches the query, I am not sure how to add anything more to the delegate or the where clause to mark the type.
Also, the parallel method for the real function against the Entity Framework does not have this issue. What is not correct with the LINQ-to-XML version?
Don't make simpleXmlFunction an Expression<Func<XElement, bool>>. Make it a Func<XElement, bool>. That's what's expected as a delegate of .Where.
Func<XElement, bool> simpleXmlFunction =
new Func<XElement, bool>(b => int.Parse(b.Element("FooId").Value) == 12);
I think the full answer includes the previous answer, David Morton's comment, and an updated code snippet:
The .Where implementation for IQueryable is different than the .Where implementation for IEnumerable. IEnumerable.Where expects a:
Func<XElement, bool> predicate
You can compile the function from the expression you have by doing:
Expression<Func<XElement, bool>> simpleXmlExpression =
b => int.Parse(b.Element("FooId").Value) == 12;
Func<XElement, bool> simpleXmlFunction = simpleXmlExpression.Compile();
var simpleXml = xml.Elements("Foo").Where(simpleXmlFunction).First();
This will allow you to look at the expression tree generated and to use the compiled form to query the xml collection.