Build expression from object


Question

I have the following object received from Angular client application in ASP.NET Core:

public class ModelFromClient
{
  public string name {get;set;}      //Database field is Name
  public int qunatity {get;set;}     //Database field is Quantity
}

And I have a EF Table class:

[Table("MyTable")]
public class MyRow
{
  public int Id {get;set;}  
  public string Name {get;set;}
  public int Qunatity {get;set;}
}

Now I need to create expression from ModelFromClient to Expression<Func<MyRow, MyRow>> and I need it with generic. Without generics solution would be:

public Expression<Func<MyRow, MyRow>> ToExpression(ModelFromClient Model)
{
    Expression<Func<MyRow, MyRow>> result = (t) => new MyRow()
    {
        Name = Model.name, 
        Quantity = Model.qunatity
    };
    return result;
}

But I would like something like that:

public Expression<Func<T, T>> ToExpression<T>(object Model) where T: new()
{
    Expression<Func<T, T>> result = (t) => new T();
    foreach(var prop in Model.GetType().GetProperties()) 
    {
       //compile error Fields does not exists.
       result.Body.Fields.Add(prop.Name.Capitalize(), prop.GetValue(Model, null));  //capitalize returns Name from input name
    }
    return result;
}

I need expression to pass it to Update extension method of EntityFramework-Plus.

Accepted Answer

Disclaimer: I'm the owner of the project Entity Framework Plus

Here is a fiddle to get you started: https://dotnetfiddle.net/JY0wzw

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

public class Program
{
    public class MyRow
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Qunatity { get; set; }
    }

    public static void Main()
    {
        var type = typeof(MyRow);
        var constructorInfo = type.GetConstructor(new Type[0]);
        var newExpression = Expression.New(constructorInfo);

        var memberInits = new List<MemberAssignment>();
        foreach (var prop in type.GetProperties())
        {
            if (prop.Name == "Id")
            {
                memberInits.Add(Expression.Bind(prop, Expression.Constant(1)));
            }
            else if (prop.Name == "Name")
            {
                memberInits.Add(Expression.Bind(prop, Expression.Constant("Z_Name")));
            }
            else if (prop.Name == "Qunatity")
            {
                memberInits.Add(Expression.Bind(prop, Expression.Constant(2)));
            }
        }

        var expression = Expression.MemberInit(newExpression, memberInits);

        // FOR testing purpose
        var compiledExpression = Expression.Lambda<Func<MyRow>>(expression).Compile();
        var myRow = compiledExpression();

        Console.WriteLine(myRow.Id);
        Console.WriteLine(myRow.Name);
        Console.WriteLine(myRow.Qunatity);
    }
}

Disclaimer: I'm the owner of the project Eval-Expression.NET

This library is not free but allows you to create code dynamically at runtime. Once you get familiar, you can do pretty much anything you want way more easily than with the previous solution.

// Register your type
EvalManager.DefaultContext.RegisterType(typeof(MyRow));

// Register extension methods once from Z.EntityFramework.Plus
EvalManager.DefaultContext.RegisterExtensionMethod(typeof(BatchUpdate));

Eval.Execute("query.Update(x => new MyRow() { Id = 1, Name = 'Z_Name', Qunatity = 2});", new {query});

Popular Answer

As @Jonathan Magnan said (thank you Jonathan), his answer point me to right direction:

public static Expression<Func<T, T>> ToExpressionGeneric<T>(this object Model) where T : new()
{
    var type = typeof(T);
    var constructorinfo = type.GetConstructor(new Type[0]);
    var newExpression = Expression.New(constructorinfo);

    var memberInits = new List<MemberAssignment>();

    var modelProperties = Model.GetType().GetProperties();
    foreach (var prop in type.GetProperties())
    {
        var modelProperty = modelProperties.Where(t => t.Name == prop.Name.FirstLetterToLowerCase()).SingleOrDefault();
        if (modelProperty != null)
            memberInits.Add(Expression.Bind(prop, Expression.Constant(modelProperty.GetValue(Model, null))));
    }

    var expression = Expression.MemberInit(newExpression, memberInits);

    var p = Expression.Parameter(typeof(T), "p");

    return Expression.Lambda<Func<T, T>>(expression, p);
}

public static string FirstLetterToLowerCase(this string s)
{
    if (string.IsNullOrEmpty(s))
        throw new ArgumentException("There is no first letter");

    char[] a = s.ToCharArray();
    a[0] = char.ToLower(a[0]);
    return new string(a);
}

Just in case if someone will search for same solution.





Licensed under: CC-BY-SA
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why