Dynamically build lambda expression from a collection of objects? - c#

I have a list of sorts stored in this format:
public class ReportSort
{
public ListSortDirection SortDirection { get; set; }
public string Member { get; set; }
}
And I need to turn it into a lambda expression of type Action<DataSourceSortDescriptorFactory<TModel>>
So assuming I have the following collection of Report Sorts as:
new ReportSort(ListSortDirection.Ascending, "LastName"),
new ReportSort(ListSortDirection.Ascending, "FirstName"),
I would need to transform it into such a statement to be used like so:
.Sort(sort => {
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
})
And the sort method signature is:
public virtual TDataSourceBuilder Sort(Action<DataSourceSortDescriptorFactory<TModel>> configurator)
So I have some method right now:
public static Action<DataSourceSortDescriptorFactory<TModel>> ToGridSortsFromReportSorts<TModel>(List<ReportSort> sorts) where TModel : class
{
Action<DataSourceSortDescriptorFactory<TModel>> expression;
//stuff I don't know how to do
return expression;
}
...and I have no idea what to do here.
EDIT: Answer is:
var expression = new Action<DataSourceSortDescriptorFactory<TModel>>(x =>
{
foreach (var sort in sorts)
{
if (sort.SortDirection == System.ComponentModel.ListSortDirection.Ascending)
{
x.Add(sort.Member).Ascending();
}
else
{
x.Add(sort.Member).Descending();
}
}
});
I was thinking at first I had to dynamically build a lambda expression from scratch using the Expression class. Luckily that wasn't the case.

...and I have no idea what to do here.
Well, reason it out.
What have you got in hand? A List<ReportSort> called sorts.
What do you need? An Action<Whatever>.
You've already taken the first step: you've make a method that takes the thing you have and returns the thing you need. Great first step.
Action<DataSourceSortDescriptorFactory<TModel>> expression;
//stuff I don't know how to do
return expression;
And you've called out what you don't know how to do -- yet. This is a good technique.
Start by filling in something that compiles but doesn't work properly.
Action<DataSourceSortDescriptorFactory<TModel>> expression =
sort => {
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
};
return expression;
Excellent. Now you have a compiling program which means you can run your tests and verify that if this case is expected, the test passes, and if anything else is expected, the test fails.
Now think, what have I got in hand? I've got a list of stuff, and I'm doing an Action. That means that a side effect is happening, probably involving every item on the list. So there's probably a foreach in there somewhere:
Action<DataSourceSortDescriptorFactory<TModel>> expression =
sort => {
sort.Add("LastName").Ascending();
sort.Add("FirstName").Ascending();
foreach(var sort in sorts) {
// Do something
}
};
return expression;
Compile it. It fails. Ah, we have confused the sort we are adding to with the new sort we are adding. Fix the problem.
Action<DataSourceSortDescriptorFactory<TModel>> expression =
existingSort => {
existingSort.Add("LastName").Ascending();
existingSort.Add("FirstName").Ascending();
foreach(var newSort in sorts) {
// Do something
}
};
return expression;
Great, now again we are in a position to compile and run tests.
The pattern here should be clear. Keep it compiling, keep running tests, gradually make your program more and more correct, reason about the operations you can perform on the values that you have in hand.
Can you finish it off?

You could use the following lambda expression that you could assign to the Action<T> delegate. In that lambda expression, capture the List<T> variable and loop over it:
public static Action<DataSourceSortDescriptorFactory<TModel>> ToGridSortsFromReportSorts<TModel>(List<ReportSort> sorts) where TModel : class
{
Action<DataSourceSortDescriptorFactory<TModel>> expression =
result =>
{
foreach (var sort in sorts)
{
if (sort.SortDirection == ListSortDirection.Ascending)
result.Add(sort.Member).Ascending();
else // or whatever other methods you want to handle here
result.Add(sort.Member).Descending();
}
};
return expression;
}

Related

Parsing expression with unknown number of parameters in DynamicExpreso

Due to performance reasons GitHub page suggests "If you need to run the same expression multiple times with different parameters I suggest to parse it one time and then invoke the parsed expression multiple times."
I have a class like this:
public class Conditional : TemplateElement
{
public string Condition { get => _condition; set => _condition = value; }
public Lambda Parsed { get => ...; private set => ... }
public Conditional(string condition)
{
Condition = condition;
...
ParseExpression();
}
private void ParseExpression()
{
var target = new Interpreter();
Lambda = target.Parse(Condition, ???);
}
}
The 'Condition' string can be in form:
item["CreatedDate"] <= DateTime.Today.AddDays(-2)
Now, at the moment of instantiation of Conditional class I don't know what the 'item' contains, I want it to be parsed so I can use it later. Lambda should resolve to the Condition to boolean.
I'm not exactly sure how to achieve this, documentation doesn't help me much. Should I define 'item' as specific type in Parameters array?

Generic Query With PredicateBuilder in Linqkit

I've been using LinqKit to create generic queries for quite some time.
One thing that has always bothered me is the fact that you always have to test whether the value sent in the filter is valid.
For example: Suppose I have a string filter. Conditions can be Equal, StartsWith, EndsWith and Contains.
My method would look something like this:
public List<MyModel> Get(MyModelFilter filter)
{
if (string.IsNullOrEmpty(filter.prop))
{
predicate = predicate.And(_myModel => myModel.Prop.Contains(filter.prop));
}
// Plus a giant amount of if's with multiple filters
return DbSet.AsExpandable()
.Where(predicate)
.ToList();
}
To end this bunch of If's, I decided to create a generic method to apply the filter to the properties.
My idea is to pass the property where the filter will be applied, and the filter definition, and encapsulate the Expression creation logic
It would be something of the type:
public List<MyModel> Get(MyModelFilter filter)
{
predicate = predicate.And(_myModel => myModel.Prop, filter.PropFilterDefinition);
// Goodnye If's, Only others filter impl
return DbSet.AsExpandable()
.Where(predicate)
.ToList();
}
For this, I've created some extension methods to handle this
public static Expression<Func<TPredicate, bool>> And<TPredicate>(
this ExpressionStarter<TPredicate> predicate,
Func<TPredicate, string> property, StringFilterDefinition filter,
bool ignoreNull = true)
{
if (InvalidStringFilter(filter, ignoreNull))
{
return predicate;
}
// This is LinqKit's And Extension Method
return predicate.And(BuildPredicate(property, filter));
}
private static Expression<Func<TPredicate, bool>> BuildPredicate<TPredicate>(
Func<TPredicate, string> property,
StringFilterDefinition filter)
{
if (filter.Filter == StringFilterComparators.Equal)
{
return x => property.Invoke(x) == filter.Value;
}
if (filter.Filter == StringFilterComparators.BeginsWith)
{
return x => property.Invoke(x).StartsWith(filter.Value);
}
if (filter.Filter == StringFilterComparators.EndsWith)
{
return x => property.Invoke(x).EndsWith(filter.Value);
}
return x => property.Invoke(x).Contains(filter.Value);
}
private static bool InvalidStringFilter(
StringFilterDefinition filter,
bool ignoreNullValue = true)
{
if (filter?.Filter == null)
{
return true;
}
return ignoreNullValue && string.IsNullOrEmpty(filter.Value);
}
The problem is that the filter is not applied, and the answer is in Invoke right up there. EF can not translate the above expression to SQL.
The EF error is
Microsoft.EntityFrameworkCore.Query.Internal.SqlServerQueryCompilationContextFactory[8]
The LINQ expression '(__property_0.Invoke([x]) == __filter_Value_1)'
could not be translated and will be evaluated locally. To configure
this warning use the DbContextOptionsBuilder.ConfigureWarnings API
(event id 'RelationalEventId.QueryClientEvaluationWarning').
ConfigureWarnings can be used when overriding the
DbContext.OnConfiguring method or using AddDbContext on the
application service provider.
The question is:
How can I make this construction work?
Also, any suggestions on how best this?
You seem to forgot that besides the PredicateBuilder, the really useful feature provided by LINQKit AsExpandable, Expand and Invoke custom extension methods is to be able to correctly embed expressions inside the expression tree.
In order to utilize that feature, you should use Expression<Func<...>> instead of Func<...>. In the posted code, replace all occurrences of Func<TPredicate, string> with Expression<Func<TPredicate, string>> and the issue should be solved.

How to simulate method overloading based on generic return type in c#

I have a read model as IQueryable<CustomType>, I use this inside my Web application. A lot of time I need to extract from this read model different View Model.
I use to write extension method like:
public static ViewModelA AsViewModelA(this IQueryable<CustomType> query)
{
var vm = view
.Select(x => new ViewModelA
{
Something = x.Something
}).FirstOrDefault();
return vm;
}
public static ViewModelB AsViewModelB(this IQueryable<CustomType> query)
{
var vm = view
.Select(x => new ViewModelB
{
SomethingElse = x.SomethingElse
}).FirstOrDefault();
return vm;
}
This do the job but I don't like the mess generated with method names; a more generic way, something like this would be preferable:
query.AsViewModel<ViewModelA>()
I know that return type is not intended as method signature (so no overload applies) and I know that generic type is not sufficient to make an overload.
What I would is a mechanism to just simulate overloading based on generic type. This mechanism should avoid a main method with cascading if/then/else. There is a way? Maybe with dynamics?
One option is to have a map from the type to a conversion of CustomType to that type. So it would look something like:
private static readonly Dictionary<Type, Expression> Mappings =
new Dictionary<Type, Expression>
{
{ typeof(ViewModelA),
Helper<ViewModelA>(x => new ViewModelA { Something = x.Something }) },
{ typeof(ViewModelB),
Helper<ViewModelB>(x => new ViewModelB { SomethingElse = x.SomethingElse }) },
...
}
// This method just helps avoid casting all over the place.
// In C# 6 you could use an expression-bodied member - or add a
private static Expression<Func<CustomType, T>> Helper<T>
(Expression<Func<CustomType, T>> expression)
{
return expression;
}
public static T AsViewModel<T>(this IQueryable<CustomType> query)
{
Expression rawMapping;
if (!Mappings.TryGetValue(typeof(T), out rawMapping))
{
throw new InvalidOperationException("Or another exception...");
}
// This will always be valid if we've set up the dictionary properly
var mapping = (Expression<Func<CustomType, T>>) rawMapping;
return view.Select(mapping).FirstOrDefault();
}
You can make the dictionary construction a bit cleaner with a bit more up-front code.
Well, yes, you can use dynamic:
private static ViewModelA AsViewModelInternal(this IQueryable<CustomType> query,
ViewModelA dummy) { ... }
private static ViewModelB AsViewModelInternal(this IQueryable<CustomType> query,
ViewModelB dummy) { ... }
public static T AsViewModel<T>(this IQueryable<CustomType> query)
{
return (T)query.AsViewModelInternal(default(T));
}
Make sure to handle a non-existing overload, of course :) The easiest way is to add an overload that takes object as the last argument, so that you basically have a "fallback overload".
However, I wouldn't recommend that. One of the great benefits of generics is you get great compile-time checks. This generic method pretends to accept all possible T's, but it actually doesn't. It's the equivalent of taking object instead of ViewModelA/ViewModelB.
It's not like there's a world's difference between
query.AsViewModelB()
and
query.AsViewModel<ViewModelB>()
I'd only use the alternative if you often find yourself having to use a generic type argument when calling AsViewModel, i.e. when you don¨t know the specific 'type in advance.

PreprocessQuery event in LightSwitch fails

In LightSwitch I have a PreprocessQuery event as follows:
partial void ValidOrders_PreprocessQuery(ref IQueryable<Order> query)
{
query = query.Where(order => OrderIsValid(order));
}
public bool OrderIsValid(Order order)
{
return true;
}
This fails with a message (on the HTMLClient side !) "method cannot be null".
But this works fine:
partial void ValidOrders_PreprocessQuery(ref IQueryable<Order> query)
{
query = query.Where(order => true);
}
Can someone see why?
Thanks,
Paul
The query provider is only shown the method OrderIsValid, and as that method has already been compiled down to IL it can no longer "look into it" to see it's implementation, as it would need to to create Expression objects to represent it.
There are a number of options that you have, ranging from inlining the method as you did yourself, or having the method itself return an expression, rather than doing the work:
public Expression<Func<Order, bool>> OrderIsValid()
{
return order => true;
}
This would let you write:
partial IQueryable<Order> ValidOrders_PreprocessQuery(IQueryable<Order> query)
{
return query.Where(OrderIsValid());
}
As a side note, I would strongly advise you to not pass the query by reference, but rather return a new query instead; that would be the more idiomatic approach.

How much of a performane hit will i take from casting when trying to make this code mistake proof?

I have the following code:
public class CrudModel<T> : ICrudModel<T> where T : DomainBase
{
public IQueryable<T> GetAll()
{
return Repository.Query<T>();
}
}
the issue is that some of the objects (T) I need to do an extra filter so I created a separate method like this:
public IEnumerable<TR> GetAllwithinOrg<TR>() where TR : DomainBase, IFilterable
{
var items = Repository.Query<TR>();
return FilterwithinOrg(items);
}
where filter method looks like this:
public IEnumerable<TResult> FilterwithinOrg<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
this all works fine but the issue is that I have to remember to call method 1 or method 2 (based on if the object supports the IFilterable interface
On this question, I got the suggestion to do this:
public IQueryable<T> GetAll()
{
var items = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
items = FilterwithinOrg(items.Cast<IFilterable>()).Cast<T>().AsQueryable();
}
return items;
}
so I can support both use cases in one method. This seems to work but I am trying to understand what type of performance hit that I am going to take by doing this
items.Cast<IFilterable>()).Cast<T>().AsQueryable()
If it's bad then I will deal with remembering to call 2 separate methods from the outside but obvious it would be convenient to just have one. Any suggestions?
I think I will leave it in just as a backup if I forget to call the second method but wanted to again see if I can keep it to just one if possible to make it simpler for the outside caller.
How about having another method with where clause in the CrudModel class.
public IEnumerable<T> GetAll<T>(Func<T, bool> whereClause) where T : DomainBase
{
var items = Repository.Query<T>();
return items.Where(whereClause);
}
And call using
List<int> intList = new List<int>() { 1 };
intList.GetAll<int>((i) => sampledict.ContainsKey(i));
I felt it is not proper to make things complex by having logic cramped into one single GetAll method and since CrudModel seems to be generic, better to have generic method that accepts any condition.
First, I think it is a bit strange that you have a function GetAll, but for certain types, you start filtering, resulting in not getting all :)
Besides that, I do not think you have big performance loss... In essense you do one extra check inside you GetAll-method: typeof(IFilterable).IsAssignableFrom(typeof(T)) is like an ordinary cast. You will hardly feel it.
Perhapse the filter itself could be improved. You create a dictionary. Does the dictionary have the same values every call, or does it change. And why a dictionary if you only use the keys, and not the values? What about a HashSet<T>?
Casting time can be neglected comparing to the time spent for database query. However, you are querying the whole table and filter in-memory according to your code here:
public IEnumerable<TResult> FilterwithinOrg<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
Remember that you need to filter query, not list, so you should change the method to accept and return IQueryable<TResult> instead like:
public IQueryable<TResult> FilterwithinOrg<TResult>(IQueryable<TResult> linked) where TResult : IFilterable
{
var dictKeys = GetDict().Keys.ToList();
return linked.Where(r => dictKeys.Contains(r.Id));
}
Noted that the filter expression has to have equivalent SQL expression or runtime-error will occur.

Categories

Resources