Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

select anonymous delegate with linq

Tags:

c#

linq

delegates

I know there's a way to do this but I've been banging my head against a wall trying to figure it out. This works fine:

private GenericRecord CreateGeneric(GenericRecord g, Member m)
{
    g.Member = m;
    return g;
}

public IList<GenericRecord> ReportFromDatabase(DateTime startDate, DateTime endDate)
{
    List<GenericRecord> returnRecords = new List<GenericRecord>();

    returnRecords.AddRange(from r in pjRepository.Records
                           join m in memberRepository.Members on r.SID equals m.MemberId.ToString()
                           where r.TransactionDate >= startDate && r.TransactionDate <= endDate
                           select CreateGeneric((GenericRecord)r, m));

    return returnRecords;
}

But I know there's a way to do it without the CreateGeneric function. How do I select a delegate function inline?

returnRecords.AddRange(from r in pjRepository.Records
                       join m in memberRepository.Members on r.SID equals m.MemberId.ToString()
                       where r.TransactionDate >= startDate && r.TransactionDate <= endDate
                       select (delegate
                       {
                           GenericRecord g = (GenericRecord)r;
                           g.Member = m;
                           return g;
                       }));

That gives me this exception:

The type of the expression in the select clause is incorrect. Type inference failed in the call to 'Select'.

Edit: Another failed attempt

returnRecords.AddRange((from r in pjRepository.Records
                        join m in memberRepository.Members on r.SID equals m.MemberId.ToString()
                        where r.TransactionDate >= startDate && r.TransactionDate <= endDate
                        select new { r, m }).Select(x =>
                        {
                            GenericRecord g = (GenericRecord)x.r;
                            g.Member = x.m;
                            return g;
                        }));

This gives me:

A lambda expression with a statement body cannot be converted to an expression tree.

like image 241
Nick Spiers Avatar asked Dec 10 '22 11:12

Nick Spiers


1 Answers

Try:

returnRecords.AddRange((from r in pjRepository.Records
                            join m in memberRepository.Members on r.SID equals m.MemberId.ToString()
                            where r.TransactionDate >= startDate && r.TransactionDate <= endDate
                            select new { r, m }).AsEnumerable().Select(x =>
                            {
                                GenericRecord g = (GenericRecord)x.r;
                                g.Member = x.m;
                                return g;
                            }));

The key different is that AsEnumerable() function. That takes an IQueryable and returns an IEnumerable, which behind the scenes forces the evaluation of the expression tree by the Linq provider. This prevents the Linq library from attempting to convert the second Select's lambda to part of the expression tree (which it says it can't); the second Select will instead perform its transformation on the actual im-memory collection produced by evaluating the IQueryable expression tree. Since you require the query to be evaluated before the statement ends anyway (so all the elements can be added to returnRecords), there should be no significant performance difference.

like image 148
KeithS Avatar answered Dec 26 '22 15:12

KeithS