Динамический linq: есть ли способ получить доступ к данным объекта по индексу? - PullRequest
3 голосов
/ 28 января 2012

Мне нужна фильтрация в памяти с помощью Dynamic Linq.У моих объектов есть только индексатор:

public  object this[int index] { }

Доступ к моим данным такой: объект [0], объект [1], ...

Так мой запрос выглядит так:

// get FilterText from user at runtime
// eg. filterText can be: [0] > 100 and [1] = "wpf"
collection.AsQueryable().where(filterText);

Есть ли способ сделать это?

Ответы [ 3 ]

2 голосов
/ 28 января 2012

У меня есть для вас новости.

Это действительно возможно ... НО! (Да, но есть).

Я предполагаю, что вы используете динамическую библиотеку Linq. Затем вы должны использовать ключевое слово «it», чтобы иметь возможность указать текущий объект, к которому вы хотите применить операцию индексирования.

Однако ... Тип возвращаемого значения вашего индексатора - объект, поэтому вы не сможете записать [0]> 100 и [1] = "wpf" из-за несоответствия типов данных.

Кроме того, даже если вы наследуете DynamicObject и добавляете свойства во время выполнения, эти свойства не будут разрешены во время выполнения динамической библиотекой linq в ее текущем состоянии. Вы бы просто получили поле или свойство не существует в типе xxx.

Существует несколько решений, некоторые из которых вы можете принять за решение.

  • Одно уродливое решение, если у вас ограниченное количество типов данных, скажем, n (где n <количество типов в .NET), вы можете использовать n индексаторы с сопоставлением параметров и получить нужный тип данных. За Например, если у вас есть в основном целые числа и несколько строк: </p>

    it[0] > 100 AND it[1, "dummy"] = "wpf" //The dummy parameter allows you to specify return type.
    
  • Другое уродливое решение, Dynamic Linq поддерживает использование ToString () и Convert-методы, так что вы можете, например, написать тот же запрос, что и выше:

    Convert.ToDouble(it[0]) > 100 AND it[1].ToString() = "wpf".
    
  • Третье уродливое решение, вы могли бы использовать соглашение, где вы заверните заявления таким образом, который говорит вам, как преобразовать данные. За Например, вы можете написать:

    it[0] > 100 AND it{1} = "wpf"
    

    И заменить "it [" на "Convert.ToDouble (it [" и т. Д. *

Если я прав, я думаю, что вы не можете использовать универсальный индексатор и с библиотекой. И Convert.ChangeType не приносит вам пользы в этом случае, так как тип возвращаемого значения все еще является объектом.

Может быть, я или кто-нибудь еще перепишу библиотеку, чтобы поддержать такие вещи, но у меня нет времени сделать это в ближайшем будущем (несколько недель).

Что ж, извините, но я должен быть где-то через 15 минут, так что, надеюсь, мы должны принять более удачные решения позже!

телепортируясь на собрание

UPDATE:

Думаю, я нашел решение вашей (и моей) проблемы!

В библиотеке DLINQ вы можете добавить члена в интерфейс (ы) IxxxSignatures для типа, с которым вы хотели бы работать.

Итак, я добавил (например) в IEqualitySignatures:

void F(Object x, Int32 y);

И изменил (в данном случае) метод ParseComparison в блоке else следующим образом.

left = Expression.Convert(left, right.Type);

И, хотите верьте, хотите нет, это сработало:)

Я не проверял все виды операций, так как я не добавил сигнатуры для других типов и операций, но это должно быть довольно просто сделать!

UPDATE

Обновлены некоторые мелочи выше ..

Я немного поэкспериментирую с этим, и хотя это может быть не самым привлекательным решением, вы можете сделать что-то подобное (DataObject - это просто DynamicObject с индексатором):

    [TestMethod]
    public void DynamicTest()
    {
        List<DataObject> dataObjects = new List<DataObject>();

        dynamic firstObject = new DataObject();
        dynamic secondObject = new DataObject();

        firstObject.dblProp = 10.0;
        firstObject.intProp = 8;
        firstObject.strProp = "Hello";

        secondObject.dblProp = 8.0;
        secondObject.intProp = 8;
        secondObject.strProp = "World";

        dataObjects.Add(firstObject);
        dataObjects.Add(secondObject);

        /* Notice the different types */
        string newQuery = FormatQuery("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'");

        var result = dataObjects.Where(newQuery);

        Assert.AreEqual(result.Count(), 1);
        Assert.AreEqual(result.First(), firstObject);
    }

И какой-то метод форматирования, например (представьте, что я написал полный метод):

    public string FormatQuery(string query)
    {
        query = query.Replace('\'', '\"');

        string[] operators = new string[] { "<", ">", "!=", "<=", ">=", "<>", "=" };

        string[] parts = query.Split();

        for (int i = 0; i < parts.Length; i++)
        {
            if (operators.Contains(parts[i]))
            {
                parts[i - 1] = "it[\"" + parts[i - 1] + "\"]";
            }
        }

        return String.Join(" ", parts);
    }

Вместо этого мы могли бы использовать метод расширения.

Или ... Мы могли бы поместить метод в библиотеку DLINQ, используя что-то вроде (не говоря, что это хорошая идея):

if (typeof(T).GetInterface("IDynamicMetaObjectProvider", true) != null)
    whereClause = whereClause.FormatQuery();

И, конечно, проверьте, реализует ли тип строковый индексатор, что-то вроде (игнорируя атрибут IndexerName здесь):

if (t.GetType().GetProperty("Item") != null)

, что позволило бы "обычным пользователям" написать:

data.Where("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'")

Ну, может быть, нехорошо иметь там такие вещи, но вы понимаете. Вы могли бы иметь! :)

1 голос
/ 28 января 2012

Вам просто нужно добавить в индексатор it, что эквивалентно в языке DynamicLinq к this в C #.

Итак, вам просто нужно it[1] == "wpf".

Однако есть еще некоторые сложности из-за того, что ваш индексатор возвращает object. Для типов классов (включая string) у вас все в порядке, DLinq будет продвигать все по мере необходимости.

Однако для типов значений, таких как int s, вам придется выполнить Int32(it[0]) > 10.

1 голос
/ 28 января 2012

Вы либо используете DynamicLinq http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx, либо строите деревья выражений с помощью отражения. Многие люди предпочитают для этого динамическую библиотеку Linq; Я никогда не использовал это сам, я использовал подход отражения.

...