nhibernate / nhibernate-core

NHibernate Object Relational Mapper
https://nhibernate.info
GNU Lesser General Public License v2.1
2.13k stars 925 forks source link

OData NotSupportedException MemberInit on base class member #2380

Closed fairking closed 4 years ago

fairking commented 4 years ago

I encounter the following error while trying to filter by base class member. Filtering by own members is working fine.

Please see the repository: https://github.com/fairking/NhOdataTest

Error happens if the following url hit: https://localhost:5001/odata/weatherforecast?$filter=town eq 'London'

However own member works fine: https://localhost:5001/odata/weatherforecast?$filter=town2 eq 'London'

public class BaseWeatherForecastVm
{
    public string Town { get; set; }
}

public class WeatherForecastVm : BaseWeatherForecastVm
{
    public Guid Id { get; set; }
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF { get; set; }
    public string Summary { get; set; }
    public string Town2 { get; set; }
}

public class WeatherForecastVmAutoMapperProfile : Profile
{
    public WeatherForecastVmAutoMapperProfile()
    {
        CreateMap<WeatherForecast, WeatherForecastVm>()
            .ForMember(d => d.TemperatureF, map => map.MapFrom(s => 32 + (int)(s.TemperatureC / 0.5556)))
            .ForMember(d => d.Town, map => map.MapFrom(s => s.Town.Name))
            .ForMember(d => d.Town2, map => map.MapFrom(s => s.Town.Name));
    }
}

[EnableQuery]
public IActionResult Get()
{
    return Ok(_mapper.ProjectTo<WeatherForecastVm>(_session.Query<WeatherForecast>().Fetch(x => x.Town)));
}

Error:

NHibernate: select weatherfor0_.date as col_0_0_, weatherfor0_.id as col_1_0_, weatherfor0_.summary as col_2_0_, weatherfor0_.temperature_c as col_3_0_, cast(weatherfor0_.temperature_c as DOUBLE) as col_4_0_, town1_.name as col_5_0_ from weather_forecast weatherfor0_ left outer join town town1_ on weatherfor0_.town_id=town1_.id
fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HLVOSTLSK9K2", Request id "0HLVOSTLSK9K2:00000011": An unhandled exception was thrown by the application.
System.NotSupportedException: MemberInit
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.VisitExpression(Expression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.VisitMemberExpression(MemberExpression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.VisitExpression(Expression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.TranslateEqualityComparison(BinaryExpression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.VisitBinaryExpression(BinaryExpression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.VisitExpression(Expression expression)
   at NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.Visit(Expression expression, VisitorParameters parameters)
   at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
   at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
   at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
   at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
   at NHibernate.Linq.Visitors.QueryModelVisitor.Visit()
   at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root, Nullable`1 rootReturnType)
   at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory, Boolean filter)
   at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryExpressionPlan.CreateTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryExpressionPlan..ctor(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
   at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
   at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
   at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query)
   at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
   at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
   at Remotion.Linq.QueryableBase`1.System.Collections.IEnumerable.GetEnumerator()
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteResourceSet(IEnumerable enumerable, IEdmTypeReference resourceSetType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObjectInline(Object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.Serialization.ODataResourceSetSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
   at Microsoft.AspNet.OData.Formatter.ODataOutputFormatterHelper.WriteToStream(Type type, Object value, IEdmModel model, ODataVersion version, Uri baseAddress, MediaTypeHeaderValue contentType, IWebApiUrlHelper internaUrlHelper, IWebApiRequestMessage internalRequest, IWebApiHeaders internalRequestHeaders, Func`2 getODataMessageWrapper, Func`2 getEdmTypeSerializer, Func`2 getODataPayloadSerializer, Func`1 getODataSerializerContext)
   at Microsoft.AspNet.OData.Formatter.ODataOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
maca88 commented 4 years ago

This issue is similar to the issue encountered in #2322, where Town property in the Select method is not equal to the Town in the Where method, as ReflectedType is not the same, which is an OData specific. Unfortunately, Relinq uses their TransparentIdentifierRemovingExpressionVisitor in the parsing stage which we cannot replace by our modified version (we modified it with #2322) as ExpressionResolver is not meant to be replaced and used in a lot of places internally. The workaround is to use our modified TransparentIdentifierRemovingExpressionVisitor after the parsing is done, which is not ideal as we have to traverse the query model one more time. On re-motion, there is already an opened issue for this, where I suggested to add a way to override the default behavior.

fairking commented 4 years ago

Thank you guys 👍