1

I want to define some lambda expression that represent updates of properties of instances of a class.

I try to write that as below:

Expression<Action<User>> update = user => user.Name = "Joe Foo";

But I have a compilation error:

Error CS0832 An expression tree may not contain an assignment operator.

How to represent this update with a lambda.

EDIT

My goal is for a business service to send updates to a generic repository. This repository could analyze the expression of the lambdas to build query to send to the database engine.

An example of a business service could be:

public void DoStuff(String userId, ...)
{
  // Business logic here
  // ...

  // Persist updates determined above
  this.repository.Update(
    // First parameter is the filter of entities to updates
    x => x.Id == userId,
    // Next parameters are updates to apply
    x => x.FirstName = "John",
    x => x.LastName = "Foo",
    ...);
}
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
gentiane
  • 6,715
  • 3
  • 23
  • 34
  • 1
    Why do you want to do this, as obviously you cant? however, if we can work out the why we might be able to give you something else – TheGeneral Mar 07 '19 at 00:08
  • 1
    Assuming you are asking about `Expression` because you want to transform it yourself you can use `==` workaround similar to [Moq approach](https://stackoverflow.com/a/16818415/477420) - `Mock.Of( m => m.Name == "wahtever" && m.Email == "some@example.com");`... but it is not clear from the question what your actual goal is - so not an answer (also based on comment to Eric's answer it is likely what you want) – Alexei Levenkov Mar 07 '19 at 00:49
  • 1
    I changed the title; the operator that assigns a value to a variable or property is the *assignment* operator. An *assignation* is a secret meeting between lovers having an affair. :-) – Eric Lippert Mar 09 '19 at 17:20
  • Your update makes it more clear what's going on here. LINQ was not designed to represent "update" queries that mutate the database. It was only designed to represent queries that extract data: selection, grouping, joining, filtering, sorting, and so on. – Eric Lippert Mar 13 '19 at 16:09

3 Answers3

7

I want to define some lambda expression that represent updates of properties of instances of a class.

You can't always get what you want.

We designed expression-tree lambdas to represent non-mutating operations, so using =, +=, ++ and so on in an expression-tree lambda is illegal.

How to represent this update with a lambda?

Delegate lambdas have no such restriction; you can say

Action<User> update = user => user.Name = "Joe Foo";

Can you say more about why you need this? There might be a better way to achieve your goal. You may be asking an XY question. That is a question where you have a problem, you have a bad solution, and now you are asking a question about the bad solution instead of about the problem. What's the problem you're trying to solve?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    I have a business service that wants to sends updates of an entity to a generic repository. So I wanted that service pass updates as lambda expressions to the repository. An example of repository call from a service could be like : `repository.Update(x => x.Id == userid, x => x.FirstName = "John", x => x.LastName = "Foo")`. The first parameter is the filter of entities to update, the next are updates to apply. The repository could translate into query to the database engine by analyzing expressions. – gentiane Mar 07 '19 at 00:32
  • There is an [Expression.Assign(Expression, Expression) Method](https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.expression.assign?view=netframework-4.7.2). So this seems to be a C# restriction and not an `Expression` restriction. – Olivier Jacot-Descombes Mar 07 '19 at 01:53
  • 3
    @OlivierJacot-Descombes: That's correct. We created expression trees for LINQ, but needed to add more nodes to it for the DLR, but never added support for the new nodes to the C# compiler. – Eric Lippert Mar 07 '19 at 06:27
  • 1
    "You can't always get what you want." But if you try sometimes, you might find you get what you need. – D Stanley Mar 13 '19 at 16:04
2

As others have said, C# does not allow assignment expressions in expression trees.

An alternative approach would be to use a new expression. This would allow to express the update with a single lambda:

repository.Update(
    x => x.Id == userId, // Where clause
    x => new User{ // Update expression
        Modified = DateTime.Now,
        ModifiedBy = "John",
        Amount = x.Amount + 10
    }
);

where x represents the old state of the entity in the update expression.

void Update(Expression<Func<T, bool>> filter, Expression<Func<T, T>> update)

If you don't need to reference the old value, you can use this simplified signature (according to your comment):

void Update(Expression<Func<T, bool>> filter, Expression<Func<T>> update)
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • 1
    I tried workaround used by Moq replacing assignment `=`by a equlity `==`. But it causes many problems (enum are casted..). So I switched to your suggestion, but with a simplified version that does not use the old state. It works fine. The method signature is `void Update(Expression> filter, Expression> update)` – gentiane Mar 09 '19 at 23:11
0

It is hard to guess how you want to use your Expression. If you want to dive deep into reflections, maybe this snippet will help:

var user = new User();

var paramExpr = Expression.Parameter(typeof(User), "user");
var propertyExpression = Expression.Property(paramExpr, "Name");
Expression.Lambda<Action<User>>(
    Expression.Assign(
        propertyExpression,
        Expression.Constant("Joe Foo")
    ),
    new List<ParameterExpression>() { paramExpr }
).Compile()(user);

Console.WriteLine(user.Name); //Joe Foo

In there you:

1) Define the ParameterExpression - your input;

2) Set up how to get to the property of the class that you are interested (.Name)

3) Finally, define the expression (and compile-call it) to do stuff wqith a test user variable

JleruOHeP
  • 10,106
  • 3
  • 45
  • 71