I have the following code:
return Expression.Call(
typeof(System.Linq.Enumerable),
"Like",
new Type[] { typeof(string) },
Expression.Constant(filter.Value),
Expression.Coalesce(member, Expression.Constant(string.Empty))
);
Alright, so instead of using "Contains", I'd like to create my own extension method called "Like", and then do something specific. I went off and created an extension method like normal:
public static bool Like<TSource>(this IEnumerable<TSource> source, TSource value)
{
return true;
}
I'm returning true for now to try to get things to work. The error I get is that "Like" is not a method of Enumerable. I can use that new extension method on it's own but not from within the expression.call.
Any ideas what might be the issue? I mean, the "Contains" is fine if I want exact text searches but I really want what the method implies, "LIKE" searches.
Thanks,
David
The first code is calling the static Enumerable.Contains<T>(Ienumerable<T>,T) - it is not triggered as an extension method, but as a regular static method call.
For example, if you have:
public static class StringExtensions
{
public static bool Like<TSource>(this IEnumerable<TSource> source, TSource value)
{
return true;
}
}
You should write:
Expression.Call(
typeof(StringExtensions), // the class that contains the extension method
"Like",
new Type[] { typeof(string) },
Expression.Constant(array), // this is the IEnumerable<string>
Expression.Constant(String.Empty)
);
Related
I need to change a function that accepts one Expression with one property inside and give it the ability to work with 2 properties at least.
I have the following base class that contains nested ElementHelper class
public class DomainObjectViewModel<TModel> where TModel : DomainObject
{
public class ElementHelper
{
public static void Create<T1>(TModel model, Expression<Func<TModel, T1>> expression)
{
var getPropertyInfo = GetPropertyInfo(expression);
//Do cool stuff
}
private static PropertyInfo GetPropertyInfo<T1>(Expression<Func<TModel, T1>> propertyExpression)
{
return (PropertyInfo)((MemberExpression)propertyExpression.Body).Member;
}
}
}
-ElementHelper class contains a Create function that gets the propertyInfo of the expression and only works if you pass one property in the expression.
Then I have the following inherited class that uses the helper function in the constructor.
public class ProductViewModel : DomainObjectViewModel<ProductEditViewModel>
{
public ProductViewModel(ProductEditViewModel model)
{
//It works for one property in the Expression
ElementHelper.Create(model, x => x.LaunchDate);
//Give the ability to pass multiple paramenters in the expression
ElementHelper.Create(model, x => new { x.LaunchDate, x.ApplyLaunchDateChanges });
}
}
I think I can use NewExpression (new { x.LaunchDate, x.ApplyLaunchDateChanges }) in order to pass it a collection of properties, but I cannot make it work.
Would you use same approach?
How you can split the passed Expression so you can get the propertyinfo of each properties found in the NewExpression object?
Well, since ElementHelper.GetPropertyInfo is your own method, you can decide what is allowed to pass, and then handle it appropriately inside that method.
Currently you handle only MemberExpression, so that's why it works only with single property accessor. If you want to be able to pass new { ... }, you need to add support for NewExpression like this:
private static IEnumerable<PropertyInfo> GetPropertyInfo<T1>(Expression<Func<TModel, T1>> propertyExpression)
{
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression != null)
return Enumerable.Repeat((PropertyInfo)memberExpression.Member, 1);
var newExpression = propertyExpression.Body as NewExpression;
if (newExpression != null)
return newExpression.Arguments.Select(item => (PropertyInfo)((MemberExpression)item).Member);
return Enumerable.Empty<PropertyInfo>(); // or throw exception
}
I have a test method with the following signature:
public void TheBigTest(MyClass data, decimal result)
{
And I'd like to run this in XUnit 2.1. I've got my CalculationData class all set up and that works if I remove the second parameter. But when I try to pass in the expected result as a second parameter by doing:
[Theory, ClassData(typeof(CalculationData)), InlineData(8893)]
It doesn't work. The test fails with a:
The test method expected 2 parameter values, but 1 parameter value was
provided.
Any ideas?
The class specified in the ClassData attribute needs to be an enumerable class that returns all of the parameters for the test method, not just the first one.
So, in your example, you would need something like:
public class CalculationData : IEnumerable<object[]>
{
IEnumerable<object[]> parameters = new List<object[]>()
{
new object[] { new MyClass(), 8893.0m },
new object[] { new MyClass(), 1234.0m },
// ... other data...
};
public IEnumerator<object[]> GetEnumerator()
{
return parameters.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
You can then add parameters to your MyClass class to enhance your test data.
I'm trying to extract the filter expression from ODataQueryOptions so that I can use it in my business logic class.
public PageResult<Poco> Get(ODataQueryOptions odataQueryOptions)
{
Expression<Func<Poco, bool>> myExpression = ... // what do i do here?
var result = _myBusinessLogic.Search(myExpression);
return new PageResult<Poco>(result, null, null);
}
I took a look at the blog describing translating the query into HQL here and I think (at least I hope) that's an overkill for what I'm trying to do.
I basically need to get the filter expression in the Expression<Func<Poco, bool>> form. I tried playing with ApplyTo() but I can't quite get it. Any help appreciated.
We have a FilterBinder class that suits your needs but is internal unfortunately. Nevertheless you could do a simple trick to get hold of the $filter expression,
public static class ODataQueryOptionsExtensions
{
public static Expression ToExpression<TElement>(this FilterQueryOption filter)
{
IQueryable queryable = Enumerable.Empty<TElement>().AsQueryable();
queryable = filter.ApplyTo(queryable, new ODataQuerySettings());
return queryable.Expression;
}
}
In your case, you can just do,
public PageResult<Poco> Get(ODataQueryOptions odataQueryOptions)
{
Expression<Func<Poco, bool>> myExpression = odataQueryOptions.Filter.ToExpression<Poco>();
var result = _myBusinessLogic.Search(myExpression);
return new PageResult<Poco>(result, null, null);
}
Notice that the expression contains looks more like this,
SOTests.Customer[].Where($it => conditional-expression). So, you might have to extract that conditional expression from the lambda.
In more recent versions of OData, FilterBinder isn't internal anymore. You can do the following directly:
public static class ODataQueryOptionsExtensions
{
public static Expression ToExpression<TElement>(this FilterQueryOption filter)
{
var binderContext = new QueryBinderContext(model, new ODataQuerySettings(), typeof(TElement));
var expression = new FilterBinder().BindFilter(filter.FilterClause, binderContext);
}
}
I've implemented an $count action that uses OData $filter to query my EF Database. It's based on #raghuram-nadiminti answer. Thank you!
[HttpGet("$count")]
public virtual async Task<int> Count(ODataQueryOptions<MyBookEntity> odataQueryOptions)
{
var queryable = this.dataContext.MyBooks;
return await odataQueryOptions.Filter
.ApplyTo(queryable, new ODataQuerySettings())
.Cast<MyBookEntity>()
.CountAsync();
}
The website that I'm working on is heavily depending on ajax/json and knockout.js.
I would like to have a lot of my Controllers return view-tailored 'json objects', without wrapping them in a JsonResult when returning the method.
This would mean I could easily composite multiple calls into one parent object, but still be able to call the Actions separately too.
Simplified example:
public object Main(int groupId)
{
var viewModel = new
{
Persons = Employees(groupId),
Messages = AllMessages()
};
return viewModel;
}
public object Employees(int groupId)
{
return DatabaseContext.Employees.Where(e => e.GroupId == groupId).ToList();
}
public object AllMessages()
{
return DatabaseContext.Messages.ToList();
}
I was hoping I could capture the returned object in OnActionExecuted and at that point wrap the whole result up in a final JsonResult.
The result is already converted to a string and captured in a ContentResult though.
Any ideas? :) Thanks,
A good approach on this is to create helper methods for your entity calls. Or if you have those methods already somewhere, they can actually serve as the helper methods. In that manner you can return a list of strongly-typed Messages and Employees as well as returning your desired parent object. You can then have individual controller methods that returns json objects. In addition, you can extend the parent viewmodel to return additional fields.
The Parent ViewModel
public class ParentModel {
public Employee Persons {get;set;}
public Message Messages {get;set;}
}
The Helper Methods
The beauty of using helper methods similar to what is defined here is that you can apply a few more logic to your query, and more, and you don't have to change anything in your controller methods.
public ParentModel GetMain(int groupId)
{
var viewModel = new ParentModel
{
Persons = Employees(groupId),
Messages = AllMessages()
};
return viewModel;
}
public IEnumerable<Employee> Employees(int groupId)
{
return DatabaseContext.Employees.Where(e => e.GroupId == groupId).ToList();
}
public IEnumerable<Message> AllMessages()
{
return DatabaseContext.Messages.ToList();
}
The Controller Methods
public ActionResult GetParent(int groupId){
return Json(helperinstance.GetMain());
}
public ActionResult GetEmployees(int groupId){
return Json(helperinstance.Employees());
}
public ActionResult GetMessages(int groupId){
return Json(helperinstance.AllMessages());
}
Thanks for the answer. I'm not going for the solution of von v. because I like to keep the boilerplate as small as possible.
In the end I am trying out the following approach. It seems to work pretty well for now, but I still have to test it in real production.
If anyone has some (security) concerns with this, I'm happy to hear them in the comments.
// BaseController
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var returnType = ((ReflectedActionDescriptor)filterContext.ActionDescriptor).MethodInfo.ReturnType;
// is the returnType not deriving from ActionResult? Automatically wrap it in a JsonResult
if ( !typeof(ActionResult).IsAssignableFrom(returnType) )
{
var result = filterContext.ActionDescriptor.Execute(filterContext, filterContext.ActionParameters);
filterContext.Result = Json( result );
}
}
I'm trying and failing to use an ExpressionVisitor to modify an expression that calls a method. I have a SearchService that encapsulates the search logic and want to be able to amend the search arguments passed.
The class in which the SearchFunc should be modified and run:
public class SearchService
{
public Expression<Func<string, string, List<int>>> SearchFunc { get; set; }
public void Run()
{
SearchModifier modifier = new SearchModifier();
Expression<Func<string, string, List<int>>> newFunc = (Expression<Func<string, string, List<int>>>)modifier.Modify(SearchFunc);
}
}
SearchModifier is defined as:
public class SearchModifier : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
Debug.Print(string.Format("VisitMethodCall: {0}", node.ToString()));
//VisitMethodCall: value(ExpressionTree_test.MainWindow)._adminRepository.SearchUsers("orig val", "orig val2")
//trying to use the Update method to create an amended MethodCallExpression
List<ConstantExpression> newargs = new List<ConstantExpression>();
newargs.Add(Expression.Constant("my new arg 1", typeof(string)));
newargs.Add(Expression.Constant("my new arg 2", typeof(string)));
MethodCallExpression methodCallExpression = node.Update(node, newargs);
//causes exception
//Method 'System.Collections.Generic.List`1[System.Int32] SearchUsers(System.String, System.String)' declared
//on type 'ExpressionTree_test.AdminRepository' cannot be called
//with instance of type 'System.Collections.Generic.List`1[System.Int32]'
Debug.Print(string.Format("Amended VisitMethodCall: {0}", methodCallExpression.ToString()));
return base.VisitMethodCall(node);
}
The Run method is called like this:
_searchService = new SearchService();
_searchService.SearchFunc = (t, s) => _adminRepository.SearchUsers("orig val", "orig val2");
I can't find much information on using the MethodCallExpression.Update method so am not sure I'm doing this correctly. How to I change the values of the arguments in the method?
Of course there may be a better way of doing this and any suggestions gratefully received...
You're not using the result of the Update method. You should pass it to base.VisitMethodCall instead of node:
return base.VisitMethodCall(methodCallExpression);
EDIT
Sorry, I misread the question... The first argument to Update is not the expression node being visited, it's the instance on which the method is called. So the code should be:
node.Update(node.Object, newargs);