Почему Select () не преобразует IEnumerable <dynamic>в IEnumerable <StrongType>? - PullRequest
5 голосов
/ 30 августа 2011

Я пытаюсь использовать Dapper просто для сопоставления моих таблиц базы данных с типами в C #, однако некоторые из моих типов нуждаются в дополнительных элементах, которых нет в таблице.Для этого я использую фабрику, которая может принимать значения столбцов и устанавливать соответствующие свойства.

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => _myTypeFactory.Create(o));
}

В настоящее время это приводит к возвращению, генерирующему ошибку:

Невозможно преобразоватьтип выражения 'System.Collections.Generic.IEnumerable<dynamic>' для возврата типа 'System.Collections.Generic.IEnumerable<IMyType>'

Мой фабричный класс выглядит примерно так:

public class MyTypeFactory {
  public IMyType Create(dynamic o) {
    return Create((String) o.Code, (Int32) o.KeyID);
  }
  public IMyType Create(String code, Int32 keyID) {
    return new MyType(code, Cache.Lookup(keyID));
  }
}

Почему метод Select() не возвращает IEnumerable<IMyType>?Что мне нужно сделать, чтобы сделать эту работу?Это просто неправильный подход и есть лучший способ?

Ответы [ 2 ]

9 голосов
/ 30 августа 2011

Самое простое решение - просто использовать оператор Cast<> LINQ:

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => _myTypeFactory.Create(o))
              .Cast<IMyType>();
}

Кроме того, вы можете разыграть каждый элемент:

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => (IMyType) _myTypeFactory.Create(o));
}

В настоящее время это не работает, потому что просто невозможно неявное преобразование между IEnumerable<dynamic> и IEnumerable<IMyType>. IEnumerable<dynamic> может быть реализовано любым количеством способов, и учитывая, что каждый элемент будет сгенерирован динамически нет оснований предполагать, что значение результата будет реализовано IEnumerable<IMyType>.

Я согласен, что выглядит как будто вторая форма фактически ничего не добавляет, но компилятор не проверяет все возможные возвращаемые типы _myTypeFactory.Create(o) - он обрабатывает это целое выражение как динамическое значение, т.е. выражение имеет тип dynamic. Поэтому результат Select все еще имеет тип IEnumerable<dynamic>.

Другой вариант - указать аргумент универсального типа для Select.

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select<IMyType>(o => _myTypeFactory.Create(o));
}

Это пытается заставить лямбда-выражение к Func<dynamic, IMyType> - я верю , что будет работать ...

РЕДАКТИРОВАТЬ: Как отмечено в комментариях, принудительное разрешение вызова метода во время компиляции также исправит это. В основном это зависит от того, что вы считаете наиболее читабельным.

1 голос
/ 30 августа 2011

Лучшим решением, вероятно, является удаление динамического вызова из оператора select, тогда вы получите ожидаемый статический тип IEnumerable<IMyType>.

public IEnumerable<IMyType> All() {
  var query = _connection.Query("SELECT * FROM [table]");
  return query.Select(o => _myTypeFactory.Create((Object)o)); //cast dynamic type to Object
}

ИЛИ

public IEnumerable<IMyType> All() {
      IEnumerable<object> query = _connection.Query("SELECT * FROM [table]"); //IEnumerable<dynamic> is the same as IEnumerable<object>
      return query.Select(o => _myTypeFactory.Create(o)); 
}
...