Как получить значение из строкового выражения

У меня есть строка, хранящаяся в моей БД:

"Users.ElementAt(1).LastName"

Затем у меня есть такой объект:

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

Есть ли способ проанализировать/оценить/запустить данную строку против моего объекта, чтобы получить фамилию каждого пользователя?

Я использую библиотеку DynamicLinq, но у меня возникла проблема с ElementAt(1) с сообщением об ошибке:

«Не существует применимого агрегированного метода «ElementAt»»

Может ли кто-нибудь дать некоторые рекомендации здесь? Придется ли мне прибегать к написанию собственного парсера и использованию рефлексии?


person Steve Stokes    schedule 26.09.2013    source источник
comment
Вы хотите пройтись по каждому User в списке Users и получить их фамилию? Или вы хотите вывести только индивидуальную фамилию User в индексе 1?   -  person Nick DeVore    schedule 27.09.2013
comment
Похожие вопросы: stackoverflow.com/questions/11479525/ stackoverflow.com/questions/714799/ stackoverflow.com/questions/10114841/ Это тоже может помочь: blogs.msdn.com/b/marcinon/archive/2010/01/14/   -  person Anderson Matos    schedule 27.09.2013
comment
@NickDeVore Мне просто нужна фамилия второго пользователя. Мои цитаты почему-то не сохранились. Это не объект, это строка в базе данных.   -  person Steve Stokes    schedule 27.09.2013


Ответы (3)


Похоже, что анализатор выражений DynamicLinq поддерживает только подмножество стандартных методов оператора LINQ:

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);
}

Как видите, ElementAt среди них нет. Хорошей новостью является то, что простое добавление void ElementAt(int index); к IEnumerableSignatures работает просто отлично. Это, конечно, при условии, что используемый вами поставщик запросов LINQ будет обрабатывать оператор ElementAt; вполне возможно, что и не будет.

Но если изменение исходного кода DynamicLinq невозможно, то нет, в ядре .NET Framework или LINQ нет средств, которые будут анализировать и оценивать такие виды выражений. Есть и другие варианты (см. ScriptCS, Roslyn), но я подозреваю, что вы склоняетесь к «избыточному» решению любой реальной проблемы, которую пытаетесь решить.

person Mike Strobel    schedule 10.10.2013
comment
Я добавил ElementAt в коллекцию выше, прежде чем публиковать это, и это не сработало, возможно, мне придется попробовать еще раз. Исходная коллекция представляет собой список, в котором есть ElementAt. Я не думаю, что то, для чего мы это используем, является излишним. Мы преодолели это с другим решением, но мы все еще изучаем это. Спасибо за помощь, пока это лучший ответ, поэтому я отмечу его как правильный. - person Steve Stokes; 10.10.2013
comment
IQueryable также не объявляет ElementAt(). DynamicLinq использует свой внутренний интерфейс IEnumerableSignatures как своего рода справочную таблицу для сопоставления известных методов расширения. Однако название вводит в заблуждение, поскольку я считаю, что оно будет искать совпадения только в том случае, если целевой тип реализует IQueryable (а не только в том случае, если он реализует IEnumerable). Если вы сделаете исходную коллекцию типом, который реализует IQueryable (или предоставите свойство-оболочку, которое вызывает AsQueryable() в списке), это должно работать. Я предположил, что вы уже делаете это, основываясь на тексте ошибки в вашем сообщении. Моя ошибка. - person Mike Strobel; 11.10.2013

var allLastNamesInList= Users.Select(x=> x.LastName).toList();

Этот оператор будет перебирать список и получать все фамилии. Вы даже можете использовать порядок, чтобы вы могли получить фамилии по порядку.

Это требование, которое вы ищете, или что-то другое?

person Antony Benito    schedule 27.09.2013
comment
Да, это строка, а не объект, я ошибся при публикации и изменил ее, пожалуйста, посмотрите разницу. - person Steve Stokes; 27.09.2013
comment
да. приведенный выше оператор даст вам все фамилии из списка List<string> allLastNamesInList= Users.Select(x=> x.LastName).toList(); Даже если вы поместите var, он автоматически наберет cast. Оба будут работать - person Antony Benito; 29.09.2013

Я не уверен, действительно ли вам нужен динамический linq, чтобы делать то, что вы хотите. Просто обычный System.Linq должен обрабатывать поиск индексов позиции, а также элемент at должен работать нормально, если вы используете C # .NET 4.5 или 4.0, по моему опыту. Вот простое консольное приложение, которое должно работать нормально:

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();
    }

}

И если вы просто хотите найти определенный объект в коллекции по свойству этой коллекции, вы можете использовать метод FindIndex:

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

РЕДАКТИРОВАТЬ 9-27-13 для разделения строки.

Если вы хотите взять строку и разбить ее на красивый список для повторения, вы тоже можете это сделать. Вам просто нужно найти разделитель. Если у вас его нет, вам придется использовать некоторую магию регулярных выражений, которая является более сложной.

Сохраните все то же самое, но замените мой метод MAIN:

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);
person djangojazz    schedule 26.09.2013
comment
Извините, в моем вопросе не было кавычек вокруг выражения, которое я там отредактировал. У меня нет экземпляра, это строка, которую мне нужно применить к объекту. - person Steve Stokes; 27.09.2013
comment
Разделите строку на новый список и выполните итерацию по нему, см. обновленный ответ. - person djangojazz; 27.09.2013
comment
Это обратное тому, что я хочу. У меня нет имен пользователей, я хочу их от объекта с помощью строки. Я бы разделил его на . и назвал бы каждый выражением. У меня возникла проблема с ElementAt() - person Steve Stokes; 27.09.2013
comment
Вы сказали: это строка, которую мне нужно применить к объекту. Я показал это: Console.WriteLine(Index of Dilat: + Users.FindIndex(n => n.LastName.Equals(Dilat))); чтобы получить член по определенному индексу, зная имя. Я показал, как добавить новый объект из списка строк или из строки, разделяющей его. Я понятия не имею, чего вы на самом деле хотите в данный момент. - person djangojazz; 27.09.2013
comment
У меня нет такого имени пользователя, как Дилат. Я хочу динамически вызывать FindIndex, который анализируется из строки - person Steve Stokes; 27.09.2013
comment
public static int GiveMeIndex(List‹User› aList, string aName) { return aList.FindIndex(n => n.LastName.Equals(aName)); } - person djangojazz; 27.09.2013