3

So this happens when I try to get data from my Web Api:

ExceptionMessage:

Only parameterless constructors and initializers are supported in LINQ to Entities.

This is my service:

public async Task<ICollection<TicketDto>> GetAllUnansweredTicketsAsync()
{
        return await _context.Tickets.Include(m => m.Message)
                        .Include(u=>u.User)
                        .OrderByDescending(d=>d.Message.Date)
                        .Select(t => new TicketDto(t)).ToListAsync();

This is how my DTO looks like:

public class TicketDto
{
    public int ID { get; set; }
    public string Username { get; set; }
    public string Issue { get; set; }
    public DateTime Date { get; set; }

    public TicketDto(Ticket ticket)
    {
        ID = ticket.ID;
        Username = ticket.User.UserName;
        Issue = ticket.Message.Title;
        Date = ticket.Message.Date;
    }
}

Can someone explain me how to fix this?

Simon Karlsson
  • 4,090
  • 22
  • 39
IvanD
  • 157
  • 4
  • 12

2 Answers2

5

In Linq to Entities your code is executed in server side, but constructors can't be translated to SQL by the provider.

You can solve the problem adding an empty constructor and setting the properties:

...Select(t => new TicketDto
{
    ID = t.ID,
    Username = t.User.UserName,
    Issue = t.Message.Title,
    Date = t.Message.Date,
});

public class TicketDto
{
    public TicketDto()
    {
    }
    ...

But If you want to keep only your actual constructor you can switch to Linq to Objects calling AsEnumerable():

...
.AsEnumerable()
.Select(t => new TicketDto(t))
.ToListAsync();
Arturo Menchaca
  • 15,783
  • 1
  • 29
  • 53
1

If you cannot or don't want add a default contructor to the TicketDto class, I suggest that you materialize the query before mapping it to the DTO object :

public async Task<ICollection<TicketDto>> GetAllUnansweredTicketsAsync()
{
    return (await _context.Tickets.Include(m => m.Message)
        .Include(u=>u.User)
        .OrderByDescending(d=>d.Message.Date)
        .ToListAsync())
        .Select(t => new TicketDto(t))
        .ToList();
}

After the call to ToListAsync you will use Linq to Object rather than Linq to Entities since the collection will be materialized in memory.

Fabien ESCOFFIER
  • 4,751
  • 1
  • 20
  • 32