The method 'Where' cannot follow the metho

2019-01-20 14:22发布

问题:

Why am I getting:

The method 'Where' cannot follow the method 'Select' or is not supported. Try writing the query in terms of supported methods or call the 'AsEnumerable' or 'ToList' method before calling unsupported methods.

...when using the WHERE clause, like when calling:

XrmServiceContext.CreateQuery<Contact>().Project().To<Person>().Where(p => p.FirstName == "John").First();

?

This works:

XrmServiceContext.CreateQuery<Contact>().Project().To<Person>().First();

Also this works:

XrmServiceContext.CreateQuery<Contact>().Where(p => p.FirstName == "John").First();

I'm using AutoMapper QueryableExtension.

Additional info:

  • I don't want to call ToList() before the Where clause. I know it will works that way.
  • CreateQuery<TEntity>() returns IQueryable<TEntity>.

回答1:

It's because whatever query provider you are using isn't able to handle this. It's not invalid in the general case; in fact most query providers do support filtering after projecting. Certain query providers simply aren't as robust as others, or they are representing a query model that is less flexible/powerful than the LINQ interface (or both). As a result, LINQ operations that are correct from the C# compiler's point of view might still not be translatable by the query provider, so the best it can do is throw an exception at runtime.



回答2:

Why don't you just move the where so it is before the projection? It will result in a single query being executed which filters and projects:

XrmServiceContext.CreateQuery<Contact>().Where(p => p.FirstName == "John").Project().To<Person>().First();


回答3:

Looking at AutoMapper's instructions for the QueryableExtensions it has an example showing the Where clause before the projection. You need to refactor your code to support this model, as opposed to placing the Where clause after the projection.

public List GetLinesForOrder(int orderId)
{
  Mapper.CreateMap()
    .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name);

  using (var context = new orderEntities())
  {
    return context.OrderLines.Where(ol => ol.OrderId == orderId)
             .Project().To().ToList();
  }
}

Given the limitations of Dynamic CRM's LINQ provider you should not expect AutoMapper to necessarily get the LINQ query correct.

There is actually a logic behind this design. As the developer you create a working Where clause. You then let AutoMapper's Project().To() define the select statement. Since CRM's LINQ provider has support for anonymous types in it should work correctly. The purpose of projection in AutoMapper is to limit the data retrieved from each class to only that needed for the projected to class. It is not intended to write a Where clause based on the projected to class.