AutoMapper / AutoMapper.Extensions.OData

Creates LINQ expressions from ODataQueryOptions and executes the query.
MIT License
140 stars 38 forks source link

Feature request: Detect missing search binder and disable $search when not using OData routing #139

Closed npbenjohnson closed 2 years ago

npbenjohnson commented 2 years ago

I have a couple use cases for this request:

It looks like OData code handles it here: https://github.com/OData/AspNetCoreOData/blob/9225d1eba1601bf1470b7e611cdd595fa479dbfc/src/Microsoft.AspNetCore.OData/Query/ODataQueryOptions.cs#L380 or here https://github.com/OData/AspNetCoreOData/blob/9225d1eba1601bf1470b7e611cdd595fa479dbfc/src/Microsoft.AspNetCore.OData/Query/Query/SearchQueryOption.cs#L136

I found this workaround for now but it's ugly: Search binder that always returns true (any actual filtering must be done manually in controller):

    public class NoopSearchBinder : ISearchBinder
    {
        public Expression BindSearch(SearchClause searchClause, QueryBinderContext context)
        {
            return Expression.Lambda(Expression.Constant(true), Expression.Parameter(context.CurrentParameter.Type, "_"));
        }

        public Expression<Func<T, bool>> NoopExpression<T>()
        {
            return x => true;
        }
    }

Registration:

services.AddOData(
      (options) =>
      {
          options.AddRouteComponents("", new ODataModelBuilder().GetEdmModel(), services =>
          {
              services.AddSingleton<ISearchBinder, NoopSearchBinder>();
          });
      }
   )

``` c#
public virtual async Task<IQueryable<TReadModel>> QueryModels<TEntity, TReadModel>(IQueryable<TEntity> dataSource, ODataQueryOptions<TReadModel> options)
        where TReadModel : class
    {
   // ...

        // Set route prefix and copy options to force no-op search binder to load in constructor: https://github.com/OData/AspNetCoreOData/blob/9225d1eba1601bf1470b7e611cdd595fa479dbfc/src/Microsoft.AspNetCore.OData/Extensions/HttpRequestExtensions.cs#L275
        Request.ODataFeature().RoutePrefix = "";
        options = new ODataQueryOptions<TReadModel>(new ODataQueryContext(options.Context.Model, options.Context.ElementClrType, options.Context.Path), Request);

        return await dataSource.GetQueryAsync(Mapper, options, querySettings);
    }
BlaiseD commented 2 years ago

Not clear. Your query needs to provide a $search without a search binder?

I think it makes sense to check for a binder here. PRs welcome.