3

Say I have this view model:

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; }
}

I stuck the projection function in there for convenience, so now I can say something like:

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

Awesome. But what do I do if I wanted to add to this query? Say I wanted to also pull a Description column from the table. Normally, I could just do a select new { } and put Description in there, but I can't quite do that because I can only put one projection function in `.Select() .

I was hoping I could do something like this:

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

But I get an exception:

System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.FieldExpression' to type 'System.Linq.Expressions.LambdaExpression'.

Or could I at least have all these queries done in one round trip somehow? I know TransactionScope works for making changes, but I don't think it causes queries to be done all at once.

Rei Miyasaka
  • 7,007
  • 6
  • 42
  • 69
  • I'm not sure what the problem is you're having. Why don't you add the `Description` column to the `SeriesLinkViewModel` or make a new ViewModel class that includes `Description`? – Steven Nov 15 '10 at 13:02
  • It's a simplistic example. The problem is when I have more combinations of queries to do. Then I can either do really broad-stroke queries that retrieve a ton of stuff, or I have a proliferation of ViewModels for no particularly good reason other than that I couldn't write an astute query. – Rei Miyasaka Nov 15 '10 at 13:22

4 Answers4

6

Solved it with 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
    };
Rei Miyasaka
  • 7,007
  • 6
  • 42
  • 69
2

This is an addition to Rei's answer (which I marked as correct). If you want to remove the dependency on the local variable, take a look at this answer from Dan Abramov on how to fix LinqKit.

Community
  • 1
  • 1
Josh Mouch
  • 3,480
  • 1
  • 37
  • 34
0

I know this is not exactly what you are looking for, but one possible work around is to create a method like this

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

Then when you want to use the projection, run your query through it.

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

Not ideal because you aren't creating a reusable expression that can be combined with others, but at least you would have just one mapping function that all similar queries run through.

Peter Willis
  • 926
  • 5
  • 9
  • I think I'm missing something -- how does that differ from just making an expression to pass to `.Select()`? – Rei Miyasaka Nov 15 '10 at 13:28
  • I don't think it should differ either.... but something in the implementation of linq means that it can't incorporate a projection expression into the query. – Peter Willis Nov 15 '10 at 14:01
0

This is in my opinion a nicer solution.

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);

The "Merge" extension method will return a projection that is the result of merging both projection expressions, thus containing the three columns: Name, Slug and Description.

The actual implementation can be found in this link: https://coding.abel.nu/2013/01/merging-expression-trees-to-reuse-in-linq-queries/

hernant
  • 341
  • 3
  • 8