0

I have the following method

     public List<ServicesLogModel> Paging(Func<ServicesLogModel, bool> condition, string columnOrder, bool? orderDescending, int? pageIndex, int? pageSize, out int total)
    {
         return _mongoRepository.Paging(condition, order => order.Message, orderDescending.Value, pageIndex.Value, pageSize.Value, out total);
    }

The columnOrder parameter is a string as lambda expression (ex: order => order.Message) that I must cast to Func<T, object>

I'm trying with Expression.Parameter

 var parm = Expression.Parameter(typeof(ServicesLogModel), "order");

        var propName = Expression.Property(parm, columnOrder);

        Expression predicateBody = Expression.Assign(parm, propName);


        var test=Expression.Lambda<Func<ServicesLogModel, object>>(predicateBody, parm);

it doesn't work Error :You can not use an expression of type 'System.String' for an assignment to type 'ServicesLogModel'

Edit :Method Signature

public List<T> Paging(Func<T, bool> condition, Func<T, object> order, bool orderDescending, int pageIndex, int pageSize,out int total) 

Call method

    [HttpGet]
    [Route("Admin/GetReaderConnectorLog/{Apikey}/{SecretKey}/{index}/{pagesize}/{orderAsc}/{columnOrder}")]
    public IActionResult GetReaderConnectorLog(string Apikey, string SecretKey, int? index, int? pagesize, bool? orderAsc, string columnOrder)
    {
        try
        {
            _userService.BeginTransaction();
            //  _webApiHelper.ValidateApiKey(Apikey, SecretKey, Context, _userService, true);
            int total;
            //TEST
            var listModel = _connectorLogService.Paging(_ => true, $"order => order.{columnOrder}", orderAsc, index, pagesize, out total);
            _userService.Commit();
            return _webApiHelper.OkResponse($"{_appSettings.Options.UserTag}[Send List User]", Context, new PaginationModel<ServicesLogModel> { ListData = listModel, Total = total, Apikey = Apikey, SecretKey = SecretKey });
        }
        catch (Exception e)
        {
            _userService.Rollback();
            return _webApiHelper.ResolveException(Context, e);
        }
    }

Regards

ngonzalezromero
  • 103
  • 1
  • 10
  • 2
    Define "it isn't work" – Clint Sep 22 '15 at 17:03
  • 1
    Just for the record, you can use Predicate instead of Func when your return type is boolean. – msmolcic Sep 22 '15 at 17:05
  • 2
    @msmolcic `Predicate` predates `Func`, it is perfectly acceptable to use the more general purpose `Func`. See for example LINQ's [Where clause](https://msdn.microsoft.com/en-us/library/bb534803(v=vs.100).aspx) `public static IEnumerable Where( this IEnumerable source, Func predicate )`. All of LINQ uses `Func` and does not anywhere use `Predicate`. – Scott Chamberlain Sep 22 '15 at 17:09
  • @ScottChamberlain I just said he can use it, did not say it's a must or whatever. – msmolcic Sep 22 '15 at 17:11
  • @msmolcic Actually he must use Func, if `Paging(` takes in a `Func` as the first parameter and you pass in a `Predicate` you will get a compiler error *"Argument 1: cannot convert from 'System.Predicate' to 'System.Func`"* because delegates are not implicitly convertable even if they have the same signature. – Scott Chamberlain Sep 22 '15 at 17:14
  • 1
    @ScottChamberlain Actually he can change paging and use Predicate – msmolcic Sep 22 '15 at 17:15
  • so the string `columnOrder` parameter column holds the name of the column that you want to use to order the data? e.g. "Message"? – Yacoub Massad Sep 22 '15 at 17:18
  • 2
    possible duplicate of [Parse string to C# lambda Func](http://stackoverflow.com/questions/1707854/parse-string-to-c-sharp-lambda-func) – pquest Sep 22 '15 at 17:20
  • yes `code`public sealed class ServicesLogModel { public string Id { get; set; } public string Message { get; set; } public DateTime Date { get; set; } public string Level { get; set; } public string Exception { get; set; } }`code` – ngonzalezromero Sep 22 '15 at 17:20

2 Answers2

3

Well , the final solution was this

public Func<T, object> GetLambda<T>(string property)
    {
        var param = Expression.Parameter(typeof(T), "p");

        Expression parent = Expression.Property(param, property);

        if (!parent.Type.IsValueType)
        {
            return Expression.Lambda<Func<T, object>>(parent, param).Compile();
        }
        var convert = Expression.Convert(parent, typeof(object));
        return Expression.Lambda<Func<T, object>>(convert, param).Compile();
    }
ngonzalezromero
  • 103
  • 1
  • 10
  • Your solution should be better in terms of performance since it uses expressions to create a function which is faster than using reflection. By the way, I am curious to know if the solution I provided works or not. – Yacoub Massad Sep 23 '15 at 15:42
  • Hi Yacoub Massad , finally your solution doesn't work – ngonzalezromero Sep 23 '15 at 18:27
1

Since your method requires Func<T,object> and not Expression<Func<T,object>>, a simple solution is to use reflection:

public Func<T, object> GetPropertyFunc<T>(string property_name)
{
    return t => typeof (T).GetProperty(property_name).GetMethod.Invoke(t, new object[] {});
}

This method takes the name of the property, and it returns the required function.

And here is how you can test it:

ServicesLogModel model = new ServicesLogModel()
{
    Message = "my message"
};

Func<ServicesLogModel, object> func = GetPropertyFunc < ServicesLogModel>("Message"); //I am assuming the property name is "Message", but you can pass any string here

var message = func(model) as string;
Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
  • it doesn't work , because not order the list by "message" property ...this way work (_mongoRepository.Paging(condition, p=> p.Message, orderDescending.Value, pageIndex.Value, pageSize.Value, out total) ) , but this way not ( return _mongoRepository.Paging(condition, func, orderDescending.Value, pageIndex.Value, pageSize.Value, out total) ) – ngonzalezromero Sep 22 '15 at 17:38
  • Are you sure that your Paging method accepts a `Func order` and not `Expression order>`? – Yacoub Massad Sep 22 '15 at 17:44
  • yes ..... (public List Paging(Func condition, Func order, bool orderDescending, int pageIndex, int pageSize,out int total) – ngonzalezromero Sep 22 '15 at 17:48
  • So, if this is not working, what exactly is not working? is it giving you wrong results? or is it throwing an exception? – Yacoub Massad Sep 22 '15 at 17:50
  • Does the columnOrder variable equal to `"Message"` or `"order => order.Message"`? – Yacoub Massad Sep 22 '15 at 17:53
  • i need that works this way "order => order.Message" not by the content of "message" variable.. – ngonzalezromero Sep 22 '15 at 17:55
  • Can you please provide in the question an example of how you want to invoke the first Paging method? – Yacoub Massad Sep 22 '15 at 17:58
  • Since you are specifying the value of the argument, why not simply change it to `"Message"` instead of `"order => order.Message"`? are you planning to support more complex selectors like `"order => order.Customer.Name"`? – Yacoub Massad Sep 22 '15 at 18:13
  • in the future , i will sort by foreach property of ServicesLogModel model ( order => order.Date, order => order.Exception ) etc.. – ngonzalezromero Sep 22 '15 at 18:22
  • Why do you want columnOrder to be a string, why not make it `Func` or `Expression>`? I am starting to get confused. Can you please explain your intention in the question? – Yacoub Massad Sep 22 '15 at 18:32
  • because I receive this parameter from a web application, i'm building a rest api, so this parameter is dynamic , for that, i must recieve as string – ngonzalezromero Sep 22 '15 at 18:37
  • And do you receive a string like `"order => order.Message"`? Can we assume that it will always be in the format `"order => order.XXXXXX"`? if so, you can simply parse this string to get the `"XXXXXX"` piece (which is the property name) and then use the function I provided to obtain a Func. Or are you expecting complex expressions like `"order => order.ZZZZZZ.XXXXXX"`? – Yacoub Massad Sep 22 '15 at 18:39
  • no.. i receive the just the object property ("Message") and i concat the rest of the string ( $"order => order.{columnOrder}") – ngonzalezromero Sep 22 '15 at 18:43
  • how do it that??? ( you can simply parse this string to get the "XXXXXX" piece (which is the property name) and then use the function I provided to obtain a Func) – ngonzalezromero Sep 22 '15 at 18:43
  • That my function should be a perfect fit. Just pass "Message" (the property name) to my function and it should work – Yacoub Massad Sep 22 '15 at 18:44
  • Since you receive the property name, you don't have to parse any thing, just pass the property name as is to the function I provided (`GetPropertyFunc`) – Yacoub Massad Sep 22 '15 at 18:45
  • works !!!! but this way ServicesLogModel model = new ServicesLogModel(); Func func = GetPropertyFunc(model, "Message"); return _mongoRepository.Paging(condition, order => order.Exception, orderDescending.Value, pageIndex.Value, pageSize.Value, out total); Thank you very much – ngonzalezromero Sep 22 '15 at 19:00
  • The GetPropertyFunc receives one parameter, not two. When I answered the question they were two (I did a mistake), but immediately I changed it to one. I cannot see how you are using the func variable. Please review your last comment. – Yacoub Massad Sep 22 '15 at 19:03
  • change this (ServicesLogModel model = new ServicesLogModel() { Message = "my message" }; ) for this ( ( ServicesLogModel model = new ServicesLogModel();) – ngonzalezromero Sep 22 '15 at 19:15
  • Why would this change any behavior? Are you using the GetPropertyFunc method that receives one or two arguments? – Yacoub Massad Sep 22 '15 at 19:19
  • two arguments, the same method that you programming – ngonzalezromero Sep 22 '15 at 19:25
  • I updated my answer immediately after writing it. It seems you have the old answer. Sorry for that. Please refresh your browser. Currently, the method has only one argument. `public Func GetPropertyFunc(string property_name)` – Yacoub Massad Sep 22 '15 at 19:30