Use if statement in Action C# - c#

I have a bool variable _settings.Value.UsePostgreSQL and method
AddConfiguration(this IServiceCollection builder, Action<DbContextOptionsBuilder> dbContextOptionsAction = null)
Is it possible to use if condition in method with Action as a parameter? Something like this:
_settings.Value.UsePostgreSQL = true;
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddConfiguration(builder =>
_settings.Value.UsePostgreSQL ?
builder.UseSqlServer(_settings.Value.ConnectionString, options =>
options.MigrationsAssembly(migrationsAssembly)) :
builder.UseNpgsql(_settings.Value.ConnectionString, options =>
options.MigrationsAssembly(migrationsAssembly)));
While I'm trying to implement this I have an error:
Only assignment, call, increment, decrement, and new object
expressions can be used as a statement

In C#, this is an expression, not a statement. As a complete statement, it won't compile. It would be fine in Perl, JavaScript, and probably other languages, but syntax is arbitraryish so you can't reliably generalize from one language to another.
a ? b : c;
It isn't an "if statement"; it's a conditional expression. This is your code with an if statement; I expect that this version will compile for you, but let me know.
services.AddConfiguration(builder =>
{
if (_settings.Value.UsePostgreSQL) {
builder.UseSqlServer(_settings.Value.ConnectionString,
options => options.MigrationsAssembly(migrationsAssembly));
} else {
builder.UseNpgsql(_settings.Value.ConnectionString,
options => options.MigrationsAssembly(migrationsAssembly));
}
});
It looks like services.AddConfiguration() expects an Action<T>, not a Func<T>, so the compiler will require the body of the lambda to be a statement, not an expression.
This is a statement containing a conditional expression, and it will compile:
var d = a ? b : c;
That's what the conditional operator is for, but it's not what you were trying to do.

Related

Combining expression trees

I have the following expression:
public Expression<Func<T, bool>> UserAccessCheckExpression<T>(int userId) where T : class
{
return x => (IsAdmin || userId == CurrentUserId || userId == 0);
}
Then I want to apply this filter to several collections (IQueryable) like this one:
return tasks
.Where(t => t.TaskUsers
.Any(x => UserAccessCheckExpression<TaskUser>(x.User) && x.SomeBool == true));
I'm getting the following error while doing so:
Error 40 Cannot implicitly convert type System.Linq.Expressions.Expression<System.Func<TaskUser,bool>> to bool
I can't use workaround with interface inheritance (like TaskUser inherits interface with int UserId property (where T : IHasUserId)) since I want to combine logic.
The problem is that your UserAccessCheckExpression() method is returning an Expression while the Any() method is expecting a boolean.
Now, you can get your code to compile by compiling the Expression and invoking the method (using UserAccessCheckExpression<TaskUser>(x.User).Compile().Invoke(x.User)) but that would obviously fail on runtime because Linq-to-Entities wouldn't be able to translate your Any() to a store query as it no longer contains an Expression.
LinqKit is aiming to solve this problem using its own Invoke extension method that while letting your code compile, will make sure your Expression will get replaced back to its original form using another extension method named AsExpandable() that is extending the entity set.
Try this:
using LinqKit.Extensions;
return tasks
.AsExpandable()
.Where(t => t.TaskUsers.Any(
x => UserAccessCheckExpression<TaskUser>(x.User).Invoke(x)
&& x.SomeBool == true));
More on LinqKit
Yeah, so, you can't do that. There's a difference between an Expression<> and a Func<>. You're trying to use the UserAccessCheckExpression as a func. I'm not sure what you're trying to do, but you can compile it to a func and then use it sorta like you are:
var expr = UserAccessCheckExpression<TaskUser>(x.User);
var func = expr.Compile();
// Later use it like ...
var result = func();
But I expect you're using this with EF or Linq2Sql? That being the case you'll need to rewrite the expression. It can be done by hand (not easy) or, better, use a tool like PredicateBuilder.

Ternary operator and Func, Action, Predicate [duplicate]

Duplicate
I can do this:
Func<CategorySummary, decimal> orderByFunc;
if (orderBy == OrderProductsByProperty.Speed)
orderByFunc = x => x.Speed;
else
orderByFunc = x => x.Price;
Why can't I do this:
Func<CategorySummary, decimal> orderByFunc = (orderBy == OrderProductsByProperty.Speed) ? x => x.Speed : x => x.Price;
The 'type inference' on the conditional operator is not quite good enough, I get a message like
Type of conditional expression cannot
be determined because there is no
implicit conversion between 'lambda
expression' and 'lambda expression'
you can always just be explicit on the right-hand-side, a la
var o = true ? new Func<int,int>(x => 0) : new Func<int,int>(x => 1);
In any case it's just a minor annoyance regarding how the types of lambdas, type inference, and the conditional operator interact.
Just cast the lambda's to Func<CategorySummary, decimal> and it will work
An alternative to the suggestions so far - move the conditional inside the lambda expression:
Func<CategorySummary, decimal> orderByFunc =
x => (orderBy == OrderProductsByProperty.Speed) ? x.Speed : x.Price;
It may not be suitable in all situations (and it does mean that the check is performed on every invocation) but sometimes it could be useful.
EDIT: As Eric points out, the two are not equivalent. Here's a quick example of how they differ (using explicit casting to get the conditional to work where the operands are lambdas):
using System;
class Test
{
static void Main()
{
bool likesCheese = false;
Action outerConditional = likesCheese
? (Action) (() => Console.WriteLine("Outer: I like cheese"))
: (Action) (() => Console.WriteLine("Outer: I hate cheese"));
Action innerConditional = () =>
Console.WriteLine (likesCheese ? "Inner: I like cheese"
: "Inner: I hate cheese");
Console.WriteLine("Before change...");
outerConditional();
innerConditional();
likesCheese = true;
Console.WriteLine("After change...");
outerConditional();
innerConditional();
}
}
Results:
Before change...
Outer: I hate cheese
Inner: I hate cheese
After change...
Outer: I hate cheese
Inner: I like cheese
As you can see, the change to the value of likesCheese only affects the version which has the conditional operator inside the lambda expression. Sometimes this is desirable, sometimes not... but you definitely need to be aware of it.
It's enough to cast only one resulting operand to a target type:
Action showResult = true
? (Action)(() => Console.Write("Hello!"))
: () => Console.Write("");
It was a little unexpected, but this is how compiler works. And when I think about it, it makes sense now.

Operator '??' cannot be applied to operands of type IQueryContainer and lambda expression

I am trying to create a method to process a certain query. I follow an example posted on the Nest repository (line 60), but still the MatchAll is not recognized by the compiler and if I try to build the solution, the error that shows is:
Operator '??' cannot be applied to operands of type IQueryContainer and lambda expression
This is my method so far:
public void ProcessQuery(IQueryContainer query = null)
{
var searchResult = this._client.Search<T>(
s => s
.Index(MyIndex)
.AllTypes()
.From(0)
.Take(10)
.Query(query ?? (q => q.MatchAll())) // Not valid
.SearchType(SearchType.Scan)
.Scroll("2m")
);
}
The type of a lambda expression can either be converted to an Expression or to some delegate type, but most likely not to IQueryContainer. Lambda expressions themselves do not have a type and need specific context for that automatic conversion, which you can give e.g. by using an appropriate delegate type constructor. But again: I don't believe an interface on one side of ?? and a lambda expression on the other makes any kind of sense.
Thanks to the comment of #Mrinal Kamboj and the answer of #Wormbo, I found my own answer:
I changed the argument type to QueryContainer and if the argument is null, a new QueryMatchAll query is created, this works for me:
public void ProcessQuery(QueryContainer query = null)
{
var searchResult = this._client.Search<T>(
s => s
.Index(MyIndex)
.AllTypes()
.From(0)
.Take(10)
.Query(query ?? new MatchAllQuery()) // Now works
.SearchType(SearchType.Scan)
.Scroll("2m")
);
}

Is there a way to inline external functions into an EF Linq query?

Let say I have a function like this:
var filterValue = GetCurrentFilter(state);
And then an EF query:
var result = context.EntitySet.Where(x=> x.column > filterValue);
this works, but as soon as I try to inline that:
var result = context.EntitySet.Where(x=> x.column > GetCurrentFilter(state));
It does not because EF Linq tried to parse GetCurrentFilter into expression tree and is unable to do that. This is all quite understandable.
My question is, is there a way to let EF Linq know that in needs to execute the GetCurrentFilter function when it builds the tree and use its result in the tree?
Something like
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)));
Since GetCurrentFilter does not have parameters that is a part of the query this should be technically possible to do that if EF Linq can support it that is. I'm suspecting that I'm just missing the correct syntax for that.
Make GetCurrentFilter a (read only) property instead of a method. EF will evaluate properties to their values, rather than trying to translate them into SQL, unlike methods.
The only other road that you have is to traverse the entire expression tree, search for usage of your ResultOf method, evaluate its parameter to a value, and then inline that value where the ResultOf call once was, rebuiding the query around that value.
In order for this to work it means you need to not only wrap the code you want to inline in a call to EfUtil.ResultOf, but it also means calling a method on the query itself to force it to go back and evaluate it:
public class EfUtil
{
public static T ResultOf<T>(T value)
{
return value;
}
}
//Note this could probably use a better name
public static IQueryable<T> EvaluateResults<T>(this IQueryable<T> query)
{
return query.Provider.CreateQuery<T>(
new ExpressionEvaluator().Visit(query.Expression));
}
internal class ExpressionEvaluator : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "ResultOf" && m.Method.DeclaringType == typeof(EfUtil))
{
Expression target = m.Arguments[0];
object result = Expression.Lambda(target)
.Compile()
.DynamicInvoke();
return Expression.Constant(result, target.Type);
}
else
return base.VisitMethodCall(m);
}
}
This would allow you to write:
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)))
.EvaluateResults();
It would then evaluate GetCurrentFilter(state) on the client side and inline the result as a constant into the query.
As a slightly simpler test, we can write the following:
var query = new[] { 1, 2, 3 }
.AsQueryable()
.Where(x => x > EfUtil.ResultOf(Math.Max(1, 2)))
.EvaluateResults();
Console.WriteLine(query.ToString());
And it will print out:
System.Int32[].Where(x => (x > 2))
Which is exactly what we want.
Note that the use of the lambda's parameter (x in these examples) cannot be used anywhere within the call to EfUtil.ResultOf or the code won't work, and couldn't possibly be made to work (although we could generate a better error message if we cared enough).

LINQ Dynamic Assignment Expressions

I have the following code that I would rather have defined dynamically.
var candidates = People.Where(x=> (x.Age >18 && x.Age < 25)) .AsQueryable().Select( c=> { c.Category = "F190 MISSING" ; return c; });
What I would however want is to have the above assignment defined as a string to be executed at runtime like so
String Conditional= "c=> { c.Category = #0; return c; }";
String[] ExpressionParameters = new [] {"F190 MISSING"};
var candidates = People.Where(x=> (x.Age >18 && x.Age < 25)).AsQueryable().Select( Conditional,ExpressionParameters);
So far, with my attempts, I have been getting either a Expression expected error message or a NO PROPERTY C EXISTS IN PERSON. How can I better define my assignment expression dynamically? Thanks in advance.
The first one does not work, because c# compiler is not able to transform multi-line lambda into Expression Tree (and IQueryable.Select expects expression).
The C# and Visual Basic compilers can generate expression trees only
from expression lambdas (or single-line lambdas). It cannot parse
statement lambdas (or multi-line lambdas). For more information about
lambda expressions in C#, see Lambda Expressions (C# Programming
Guide); (...).
The second one can be done using libraries like DynamicLINQ.

Categories

Resources