Как я могу объединить несколько рефакторинговых выражений Select в Linq (в EF или SQL)?

Скажем, у меня есть эта модель представления:

public class SeriesLinkViewModel
{
    public static Expression<Func<Series, SeriesLinkViewModel>> FromSeries =
        s => new SeriesLinkViewModel
        {
            Name = s.Name,
            Slug = s.Slug,
        };

    public string Name { get; set; }
    public string Slug { get; set; }
}

Я вставил туда функцию проекции для удобства, так что теперь я могу сказать что-то вроде:

var links = dc.Series.Select(SeriesLinkViewModel.FromSeries);

Потрясающий. Но что мне делать, если я хочу добавить к этому запросу? Скажем, я хотел также извлечь столбец Description из таблицы. Обычно я мог бы просто сделать select new { } и поместить туда Description, но я не могу этого сделать, потому что я могу поместить только одну проекционную функцию в `.Select() .

Я надеялся, что смогу сделать что-то вроде этого:

q = from s in dc.Series
    select new
    {
        Series = SeriesLinkViewModel.FromSeries.Compile()(s),
        Description = s.Description
    };

Но я получаю исключение:

System.InvalidCastException: невозможно привести объект типа «System.Linq.Expressions.FieldExpression» к типу «System.Linq.Expressions.LambdaExpression».

Или я мог бы, по крайней мере, сделать все эти запросы за один раз? Я знаю, что TransactionScope работает для внесения изменений, но я не думаю, что это заставляет запросы выполняться сразу.


person Rei Miyasaka    schedule 15.11.2010    source источник
comment
Я не уверен, в чем проблема. Почему бы вам не добавить столбец Description в столбец SeriesLinkViewModel или не создать новый класс ViewModel, включающий Description?   -  person Steven    schedule 15.11.2010
comment
Это упрощенный пример. Проблема в том, что мне нужно выполнить больше комбинаций запросов. Затем я могу либо выполнять действительно широкие запросы, которые извлекают массу материала, либо у меня есть распространение ViewModels без какой-либо особенно веской причины, кроме того, что я не мог написать проницательный запрос.   -  person Rei Miyasaka    schedule 15.11.2010


Ответы (4)


Решил с помощью LinqKit:

var fs = SeriesLinkViewModel.FromSeries; //needs to be local for some reason
q = from s in dc.Series.AsExpandable() //enables LinqKit to do its magic
    select new
    {
        Series = fs.Invoke(s), //and voila!
        Description = s.Description
    };
person Rei Miyasaka    schedule 15.11.2010

Это дополнение к ответу Рей (которое я отметил как правильное). Если вы хотите удалить зависимость от локальной переменной, взгляните на этот ответ Дэна Абрамова о том, как исправить LinqKit.

person Josh Mouch    schedule 12.05.2012

Я знаю, что это не совсем то, что вы ищете, но одним из возможных обходных путей является создание такого метода.

private IQueryable<SeriesLinkViewModel> FromSeries(IQueryable<Series> seriesQuery)
{
    return from s in seriesQuery
           select new SeriesLinkViewModel
           {
                Name = s.Name,
                Slug = s.Slug
           };
}

Затем, когда вы хотите использовать проекцию, запустите через нее свой запрос.

return FromSeries(from s in Series
                  where s.Name == "foo"
                  select s);

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

person Peter Willis    schedule 15.11.2010
comment
Я думаю, что я что-то упускаю - чем это отличается от простого создания выражения для передачи в .Select()? - person Rei Miyasaka; 15.11.2010
comment
Я не думаю, что он тоже должен отличаться... но что-то в реализации linq означает, что он не может включать выражение проекции в запрос. - person Peter Willis; 15.11.2010

На мой взгляд, это более красивое решение.

public class SeriesLinkViewModel
{
    public static Expression<Func<Series, SeriesLinkViewModel>> FromSeries =
        s => new SeriesLinkViewModel
        {
            Name = s.Name,
            Slug = s.Slug,
        };

    public string Name { get; set; }
    public string Slug { get; set; }
}

public class SeriesLinkExtendedViewModel: SeriesLinkViewModel
{
    public new static Expression<Func<Series, SeriesLinkExtendedViewModel>> FromSeries = 
        SeriesLinkViewModel.FromSeries.Merge(s => new SeriesLinkExtendedViewModel
        {
            Description = s.Description
        });

    public string Description { get; set; }
}

// Somewhere else...
var q = from s in dc.Series.Select(SeriesLinkExtendedViewModel.FromSeries);

Метод расширения «Объединить» вернет проекцию, которая является результатом слияния обоих выражений проекций, таким образом, содержащую три столбца: Имя, Slug и Описание.

Фактическую реализацию можно найти по этой ссылке: https://coding.abel.nu/2013/01/merging-expression-trees-to-reuse-in-linq-queries/

person hernant    schedule 28.11.2017