System.Linq.Динамический.Ядро не может сравнить тип объекта

#c# #linq #dynamic-linq

#c# #linq #динамический-linq

Вопрос:

Я пытаюсь выполнить динамический запрос с моделью, как показано ниже:

 public class Index
{
    public long Position { get; set; }

    public object[] Values { get; set; }
}
  

И с таким образцом данных, как этот:

 var indexes = new Dictionary<long, Index>();
indexes.Add(1, new Index { Position = 1000, Values = new object[3] { "Welly", "Chandra", 26 } });
indexes.Add(2, new Index { Position = 1001, Values = new object[3] { "Darma", "Angelo", 25 } });
indexes.Add(3, new Index { Position = 1002, Values = new object[3] { "Abby", "Yeremia", 28 } });
indexes.Add(4, new Index { Position = 1003, Values = new object[3] { "Yonathan", "Gunawan", 22 } });
indexes.Add(5, new Index { Position = 1004, Values = new object[3] { "Aldy", "Santoso", 24 } });
var queryable = indexes.Values.AsQueryable();
var result = queryable.Where("Values[1] = "Yeremia" || Values[2] = 24").ToList();
  

Но он всегда выдает исключение:

 System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Object' and 'System.Int32'.
  

Разве здесь невозможно использовать object тип данных?

Ответ №1:

Это возможно, если вы используете .Equals() , который сначала проверит, идентичны ли типы, и, если это не так, выполнит приведение к типу времени выполнения.

 var result = queryable.Where(
    "Values[1].Equals("Yeremia") || Values[2].Equals(24)").ToList();
  

Как отмечалось в комментариях, при сравнении объектов возникают проблемы. В случае == , вы не можете сравнить Int32 с object без приведения. Аналогично, при использовании .Equals() вы не можете иметь значение слева равным null, иначе вы выдадите исключение NullReferenceException .

Если возможно, гораздо лучшим подходом было бы что-то вроде ValueTuple, который строго типизирован и ссылается по имени, а не по индексу. Это не только безопасно от NullReferenceExceptions, но и позволяет избежать операций упаковки и приведения.

 public class Index
{
    public long Position { get; set; }
    public (string, string, int) Values { get; set; }
}
  
 var indexes = new Dictionary<long, Index>();
    indexes.Add(1, new Index { Position = 1000, Values = ("Welly", "Chandra", 26) });
    indexes.Add(2, new Index { Position = 1001, Values = ("Darma", "Angelo", 25) });
    indexes.Add(3, new Index { Position = 1002, Values = ("Abby", "Yeremia", 28) });
    indexes.Add(4, new Index { Position = 1003, Values = ("Yonathan", "Gunawan", 22) });
    indexes.Add(5, new Index { Position = 1004, Values = ("Aldy", "Santoso", 24) });
    
var queryable = indexes.Values.AsQueryable();
var result = queryable.Where(
    "Values.Item2 == "Yeremia" || Values.Item3 == 24").ToList();
  

Комментарии:

1. Спасибо, это работает. Итак, лучше ли для всех сравнений использовать .Equals() вместо = operator ?

2. So, is it better for all comparisons to use .Equals() instead of = operator? Нет, если левая сторона может быть null .

3. @JustinusHermawan как всегда бывает в подобных случаях, ответ «это зависит». mjwillsвыражает прекрасный пример того, когда это было бы не так advisable…in случаи, когда вы можете вызвать исключение NullReferenceException, например.

4. О, я вижу, я думаю, что могу гарантировать object[] значения, они всегда будут иметь not null значение.

5. Это может быть возможно с ValueTuple , но поскольку моя Values длина не определена и имеет динамический тип, я думаю, что мне нужно использовать Array вместо этого.