Преобразование анонимного типа в новый тип кортежей С# 7

Появилась новая версия С# с полезной новой функцией Типы Tuple:

public IQueryable<T> Query<T>();

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => new {
            id = o.Id,
            name = o.Name,
        })
        .First();

    return (id: obj.id, name: obj.name);
}

Есть ли способ преобразовать объект анонимного типа obj в кортеж, который я хочу вернуть без свойства сопоставления по свойству (при условии, что имена свойств совпадают)?

Контекст находится в ORM, мой объект SomeType имеет много других свойств и сопоставляется с таблицей с большим количеством столбцов. Я хочу сделать запрос, который приносит только ID и NAME, поэтому мне нужно преобразовать анонимный тип в кортеж, или мне нужно, чтобы поставщик ORM Linq знал, как понимать кортеж, и помещал связанные с ним столбцы в предложение SQL select.

Ответ 1

Короткий ответ - нет, в текущей форме С# 7 нет встроенного способа выполнения ваших целей дословно, так как вы хотите выполнить:

  • Linq к лицам
  • Отображение подмножества столбцов
  • Предотвращение свойства путем сопоставления свойств из пользовательского или анонимного типа в кортеж С# 7 путем прямого сопоставления с кортежем С# 7.

Поскольку Query<SomeType> предоставляет IQueryable, в дерево выражений .Select(x => new {}) должен быть сделан любой вид проекции.

Для добавления этой поддержки существует open roslyn issue, но она еще не существует.

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


В то время как это ограничение в настоящее время запекается в Linq-to-Entities из-за отсутствия поддержки и невозможности использования параметризованных конструкторов в проекции .Select(), как Linq-to-NHibernate, так и Linq-to-Sql позволяют взломать в виде создания System.Tuple в проекции .Select(), а затем вернуть значение ValueTuple с помощью . Метод расширения ToValueTuple()

public IQueryable<T> Query<T>();

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => new System.Tuple<int, string>(o.Id, o.Name))
        .First();

    return obj.ToValueTuple();
}

Так как System.Tuple можно сопоставить с выражением, вы можете вернуть подмножество данных из своей таблицы и разрешить фреймворку обрабатывать сопоставление с вашим кортежем С# 7. Затем вы можете деконструировать аргументы с помощью любого соглашения об именах, которое вы выберете:

(int id, string customName) info = GetSomeInfo();
Console.Write(info.customName);

Ответ 2

Конечно, создав кортеж из выражения LINQ:

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .Select(o => (o.Id,o.Name))
        .First();

    return obj;
}

В соответствии с другим ответом относительно кортежей, предшествующих С# 7, вы можете использовать AsEnumerable(), чтобы предотвратить перезагрузку EF. (У меня мало опыта работы с EF, но это должно делать:)

public (int id, string name) GetSomeInfo() {
    var obj = Query<SomeType>()
        .AsEnumerable()
        .Select(o => (o.Id,o.Name))
        .First();

    return obj;
}

Ответ 3

Хотя литералы кортежей в настоящее время не поддерживаются в деревьях выражений, это не означает, что тип ValueTuple не поддерживается. Просто создайте это явно.

public (int id, string name) GetSomeInfo() =>
    Query<SomeType>()
        .Select(o => ValueTuple.Create(o.Id, o.Name))
        .First();

Ответ 4

Примечание для тех, кто работает в более низкой версии .NET: если вы используете более низкую версию .NET, чем 4.7.2 или .NET Core, вы должны использовать Nuget Package Manager для установки System.ValueTuple в ваш проект.

Затем, вот пример получения кортежа из запроса Linq to SQL:

var myListOfTuples = (from record1 in myTable.Query()
                     join record2 in myTable2.Query() on record1.Id = record2.someForeignKey
                     select new {record1, record2}).AsEnumerable()
                     .select(o => (o.record1, o.record2))
                     .ToList()

Это бежало для меня, однако, после регистрации, я получил сбой сборки... читать дальше.

Для еще большего удовольствия и игр у меня, к сожалению, по какой-то причине была более ранняя версия С# на моем сервере сборки. Поэтому мне пришлось вернуться назад, потому что он не распознал новый формат кортежа в строке .select(o => (o.record1, o.record2)) (в частности, что это будет кортеж из-за скобок вокруг o. record1 и o.record2). Итак, я должен был вернуться назад и немного больше узнать об этом:

var myListOfAnonymousObjects = (from record1 in myTable.Query()
                     join record2 in myTable2.Query() on record1.Id = record2.someForeignKey
                     select new {record1, record2}).ToList()

var myTuples = new List<Tuple<Record1sClass, Record2sClass>>();
foreach (var pair in myListOfAnonymousObjects)
{
    myTuples.Add(pair.record1, pair.record2);
}
return myTuples;

Ответ 5

    public IList<(int DictionaryId, string CompanyId)> All(string DictionaryType)
    {
        var result = testEntities.dictionary.Where(p => p.Catalog == DictionaryType).ToList();
        var resultTuple = result.Select(p => (DictionaryId: p.ID, CompanyId: p.CompanyId));
        return resultTuple.ToList();
    }

Этот метод вы можете назвать свой элемент кортежа в linq select