0

I have a string stored in my db:

"Users.ElementAt(1).LastName"

I then have an object like so:

        MyClass myclass = new MyClass ()
        {
            Users = new List<User>()
            {
                new User()
                {
                    LastName = "LastName1"
                },
                new User()
                {
                    LastName = "LastName2"
                },
            }
        };  

Is there a way to either parse/evaluate/run the given string against my object to get each users last name?

I've been using the DynamicLinq library, but I am running into an issue with ElementAt(1) with the error message:

"No applicable aggregate method 'ElementAt' exists"

Can anyone provide some guidance here? Will I have to resort to writing my own parser and using reflection?

Steve Stokes
  • 1,200
  • 18
  • 36
  • Are you wanting to iterate through each `User` in the `Users` list and get their last name? Or do you want to pull out just the individual `User` last name at index 1? – Nick DeVore Sep 26 '13 at 22:23
  • Similar questions: http://stackoverflow.com/questions/11479525/how-to-turn-a-string-into-a-linq-expression http://stackoverflow.com/questions/714799/is-there-an-easy-way-to-parse-a-lambda-expression-string-into-an-action-delega http://stackoverflow.com/questions/10114841/how-to-create-dynamic-lambda-based-linq-expression-from-a-string-in-c This might help too: http://blogs.msdn.com/b/marcinon/archive/2010/01/14/building-custom-linq-expressions-made-easy-with-dynamicqueryable_2e00_.aspx – Anderson Matos Sep 26 '13 at 22:46
  • @NickDeVore I just want the second users last name. For some reason my quotes did not save. This is not an object, this is a string in a database. – Steve Stokes Sep 27 '13 at 02:27

3 Answers3

0

I am not certain if you really need dynamic linq to do what you want. Just regular System.Linq should handle finding indexes of position and also the element at should work just fine if you are using C# .NET 4.5 or 4.0 in my experience. Here is a simple console app that should work fine:

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


public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}


class Program
{
    public static List<User> Users;

    static void Main(string[] args)
    {
            Users = new List<User>
            {
                new User
                    {
                        FirstName = "Brett",
                        LastName = "M"
                    },
                new User
                    {
                        FirstName = "John",
                        LastName = "Doe"
                    },
            new User
                {
                    FirstName = "Sam",
                    LastName = "Dilat"
                }
            };

        Console.WriteLine("Should work as long as object is defined and instantiated");
        Console.WriteLine(Users.ElementAt(1).FirstName);


        Console.WriteLine("\nIterate Through List with Linq\n I use a string you can add to another list or whatever\n\n");

        string s = "";

        Users.ForEach(n => s += "Position: " + Users.IndexOf(n) + " " + n.LastName + Environment.NewLine);
        Console.WriteLine(s);

        Console.ReadLine();
    }

}

And if you just want to find a particular Object in a collection by a property of that collection you can use the 'FindIndex' method:

Console.WriteLine("Index of Dilat: " + Users.FindIndex(n => n.LastName.Equals("Dilat")));

EDIT 9-27-13 for string to split.

If you want to take a string and split it up into a nice enurable list to iterate through you can do that too. You merely need to find the seperator. If you do not have one you are going to have to use some regex magic which is more complicated.

Keep everything the same but substituting my MAIN method:

string split = "Brett,Same,John,Frank";

        List<string> stringList = split.Split(',').ToList();

        string s = "";

        stringList.ForEach(n => s += n + Environment.NewLine);

        s += "\n\nFor Observing Element at\n";

        s += "John is at: " + stringList.FindIndex(n => n.Equals("John"));

        s += "\n\nGetting Poco Objects assigned and listed\n";

        var list = stringList.Select(u => new User
            {
                FirstName = u,
                LastName = "Something"
            }).ToList();

        list.ForEach(n => s += "User: " + n.FirstName + " " + n.LastName + "At: " + list.IndexOf(n) + Environment.NewLine);
djangojazz
  • 14,131
  • 10
  • 56
  • 94
  • Sorry my question did not include quotes around the expression, which I have edited in there. I do not have an instance, it's a string which I need to apply to the object. – Steve Stokes Sep 27 '13 at 02:28
  • Do a string split into a new list and iterate through that, see updated answer. – djangojazz Sep 27 '13 at 15:45
  • This is backwards of what I want. I do not have the users names, I want them from the object using the string. I would split it on the .'s and call each with an expression. I'm running into an issue with ElementAt() – Steve Stokes Sep 27 '13 at 17:25
  • You said: "it's a string which I need to apply to the object." I showed this: Console.WriteLine("Index of Dilat: " + Users.FindIndex(n => n.LastName.Equals("Dilat"))); to get a member at a certain index knowing a name. I showed how to add a new object from a string list, or from a string splitting it out. I have no idea what you actually want at this point. – djangojazz Sep 27 '13 at 17:42
  • I have no users name like "Dilat". I want to dynamically call FindIndex which is parsed from the string – Steve Stokes Sep 27 '13 at 18:13
  • public static int GiveMeIndex(List aList, string aName) { return aList.FindIndex(n => n.LastName.Equals(aName)); } – djangojazz Sep 27 '13 at 18:42
0
var allLastNamesInList= Users.Select(x=> x.LastName).toList();

This statement will iterate through the list and get all last names. You can even use order by as well so that you will get last names in order.

Is this is the requirement you are looking for or something different ?

  • Yeah it's a string, not an object, I made a mistake when posting and changed it, please see the difference. – Steve Stokes Sep 27 '13 at 02:28
  • Yes. the above statement will give you all last names from the list `List allLastNamesInList= Users.Select(x=> x.LastName).toList();` Even if you put var then it will automatically type cast. Both will work – Antony Benito Sep 29 '13 at 08:22
0

It looks like DynamicLinq's expression parser only supports a subset of the standard LINQ operator methods:

interface IEnumerableSignatures
{
    void Where(bool predicate);
    void Any();
    void Any(bool predicate);
    void All(bool predicate);
    void Count();
    void Count(bool predicate);
    void Min(object selector);
    void Max(object selector);
    void Sum(int selector);
    void Sum(int? selector);
    void Sum(long selector);
    void Sum(long? selector);
    void Sum(float selector);
    void Sum(float? selector);
    void Sum(double selector);
    void Sum(double? selector);
    void Sum(decimal selector);
    void Sum(decimal? selector);
    void Average(int selector);
    void Average(int? selector);
    void Average(long selector);
    void Average(long? selector);
    void Average(float selector);
    void Average(float? selector);
    void Average(double selector);
    void Average(double? selector);
    void Average(decimal selector);
    void Average(decimal? selector);
    void Take(int count);
    void Union(IQueryable right);
    void OrderBy(LambdaExpression exp);
    void OrderByDescending(LambdaExpression exp);
}

As you can see, ElementAt is not among them. The good news is that simply adding void ElementAt(int index); to IEnumerableSignatures appears to work just fine. That is, of course, assuming the LINQ query provider you are using will handle the ElementAt operator; it is entirely possible that it will not.

But if modifying the DynamicLinq source isn't an option, then no, there are no facilities within the core .NET Framework or LINQ which will parse and evaluate those kinds of expressions. There are other options (see ScriptCS, Roslyn), but I suspect you are drifting towards an "overkill" solution to whatever actual problem you are trying to solve.

Mike Strobel
  • 25,075
  • 57
  • 69
  • I added ElementAt to the collection above previous to me posting this and it did not work, I may have to try again. The source collection is a List, which does have ElementAt. I don't think what we're using this for is overkill. We did get past this with a different solution, but we're still looking into this. Thanks for the help, the is the best answer so far so I will mark as correct. – Steve Stokes Oct 10 '13 at 19:47
  • `IQueryable` does not declare `ElementAt()` either. DynamicLinq uses its internal `IEnumerableSignatures` interface as a sort of reference chart for matching up known extension methods. The name is misleading, though, as I believe it will only look for matches if the target type implements `IQueryable` (and not if it only implements `IEnumerable`). If you make source collection a type that implements IQueryable (or provide a wrapper property that calls `AsQueryable()` on the list), it should work. I'd assumed you were already doing this based on the error text in your post. My mistake. – Mike Strobel Oct 10 '13 at 20:23