OData / AspNetCoreOData

ASP.NET Core OData: A server library built upon ODataLib and ASP.NET Core
Other
457 stars 158 forks source link

Apply/GroupBy/Aggregate in .NET 5 / Odata v8preview3 / EFCore 5 doesn't work #76

Open fabiocbr75 opened 3 years ago

fabiocbr75 commented 3 years ago

Hello,

I have problems with Apply/GroupBy/Aggregate in .NET 5 / Odata v8preview3 / EFCore 5.

I'm not able to use simple expression like $apply=aggregate($count as OrderCount).

I receive the following error:

System.InvalidOperationException: Processing of the LINQ expression 'GroupByShaperExpression:
      KeySelector: new NoGroupByWrapper(),
      ElementSelector:EntityShaperExpression:
          EntityType: OperationExecution
          ValueBufferExpression:
              ProjectionBindingExpression: EmptyProjectionMember
          IsNullable: False
      ' by 'RelationalProjectionBindingExpressionVisitor' failed. 
This may indicate either a bug or a limitation in Entity Framework. 

Can you help me to resolve this problem?

Obviously for me, it's not a solution to use ToList() and aggregate client side.

Thanks

Fabio

QualitasGit commented 3 years ago

Hello. I'm getting a similar error when trying to use simple groupby:

?$apply=groupby((Title))

An unhandled exception occurred while processing the request. InvalidOperationException: The converter specified on 'Microsoft.AspNetCore.OData.Query.Wrapper.GroupByWrapper' is not compatible with the type 'Microsoft.AspNetCore.OData.Query.Wrapper.GroupByWrapper'. System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializationConverterOnAttributeNotCompatible(Type classTypeAttributeIsOn, PropertyInfo propertyInfo, Type typeToConvert)

I managed to track the resulting query in MySql db and the result was pretty weird

SELECT 'Title' AS 'Name', 'f'.'title' AS 'Value' FROM 'table' AS 'f' GROUP BY 'f'.'title'

That was not expected, 'cause I still want all other fields and the query force select new fields with some weird names. The query run on my db, but the odata always return this error

JanKotschenreuther commented 3 years ago

Our APIs produce similar errors for aggregates:

{{baseUrl}}/Article?$apply=aggregate(CurrentPP with sum as mySum) {{baseUrl}}/OrderDetail?$apply=filter(OrderPositionId eq 2462)/aggregate(Quantity with sum as TotalQuantity)

System.InvalidOperationException: Processing of the LINQ expression 'GroupByShaperExpression:
KeySelector: new NoGroupByWrapper(), 
ElementSelector:EntityShaperExpression: 
    EntityType: Article
    ValueBufferExpression: 
        ProjectionBindingExpression: EmptyProjectionMember
    IsNullable: False
' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in Entity Framework. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitUnary(UnaryExpression unaryExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitUnary(UnaryExpression unaryExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](Object value)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, Object asyncEnumerable, Func`2 reader)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|21_0(ResourceInvoker invoker, IActionResult result)
   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 ---
   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__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

We also had problems with groupby using Newtonsoft. We were able to solve that problem opting out of Newtonsoft, by removing .AddNewtonsoftJson(). Here are the cases we had problems with which now work without Newtonsoft: {{baseUrl}}/Article?$apply=groupby((Brand/Name))&$top=10&$skip=30&$orderby=Brand/Name {{baseUrl}}/Article?$apply=groupby((Brand/Id))&$top=10&$skip=30&$orderby=Brand/Id

leonardochaia commented 3 years ago

As of v8.0.0.0-rc2 this is still not working. Is this being tracked someplace else?

It does work on the sample since it's using the In Memory Database, as soon as you switch to Sqlite or SqlServer it stops working.

To reproduce, make the following changes to the sample:

// Program.cs
public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            // keep a connection open for the duration of the process so it's not deleted.
            using var conn = new SqliteConnection($"Data Source = TestApp;" +
                                                  $" Mode = Memory; Cache = Shared");
            conn.Open();;

            using var scope = host.Services.CreateScope();
            scope.ServiceProvider.GetRequiredService<MyDataContext>().Database.EnsureCreated();

            host.Run();
            conn.Close();
        }
// Startup.cs
 services.AddDbContext<MyDataContext>(opt => 
                opt.UseLazyLoadingProxies()
                    .UseSqlite($"Data Source = TestApp;" +
                               $" Mode = Memory; Cache = Shared")
                    // .UseInMemoryDatabase("MyDataContextList")
                );

Then execute an $apply: http://localhost:5000/products?$apply=aggregate($count as OrderCount)

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: Processing of the LINQ expression 'GroupByShaperExpression:
      KeySelector: new NoGroupByWrapper(), 
      ElementSelector:EntityShaperExpression: 
          EntityType: Product
          ValueBufferExpression: 
              ProjectionBindingExpression: EmptyProjectionMember
          IsNullable: False
      ' by 'RelationalProjectionBindingExpressionVisitor' failed. This may indicate either a bug or a limitation in Entity Framework. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitExtension(Expression extensionExpression)
         at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitUnary(UnaryExpression unaryExpression)
         at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitUnary(UnaryExpression unaryExpression)
         at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
         at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
         at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
         at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
         at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
         at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
         at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
         at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
         at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
         at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
         at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](Object value)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext context, ObjectResult result, Object asyncEnumerable, Func`2 reader)
         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 ---
         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 Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
         at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
         at Microsoft.AspNetCore.OData.Batch.ODataBatchMiddleware.Invoke(HttpContext context) in /home/lchaia/dev/AspNetCoreOData/src/Microsoft.AspNetCore.OData/Batch/ODataBatchMiddleware.cs:line 66
         at Microsoft.AspNetCore.OData.Query.ODataQueryRequestMiddleware.Invoke(HttpContext context) in /home/lchaia/dev/AspNetCoreOData/src/Microsoft.AspNetCore.OData/Query/ODataQueryRequestMiddleware.cs:line 64
         at Microsoft.AspNetCore.OData.Routing.ODataRouteDebugMiddleware.Invoke(HttpContext context) in /home/lchaia/dev/AspNetCoreOData/src/Microsoft.AspNetCore.OData/Routing/ODataRouteDebugMiddleware.cs:line 82
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
JanKotschenreuther commented 3 years ago

Still no change on 8.0.0-rc3.

JanKotschenreuther commented 3 years ago

I have got a temporary workaround that might be of interest until this issue is solved.

The following example to use aggregates will materialize the whole dataset before applying the query. Which means the whole dataset will be loaded into memory of the API before filters and so on apply. At least the whole dataset has not to be returned to the client. So be aware of that when making use of the following example.

  1. Create a generic ApiController with an Action that should execute the Aggregate and materializes the dataset before applying the query:

    [ApiController]
    public class AggregateController<TEntity> : ControllerBase
        {
                private readonly MyContext _myContext;
    
        public AggregateController(MyContext myContext)
        {
            _myContext = myContext;
        }
    
        [HttpGet("")]
        public async Task<IActionResult> Aggregate(ODataQueryOptions<TEntity> queryOptions)
        {
            var dbSet = _myContext.Set<TEntity>();
                        var dataArray = dbSet.ToArray();
    
            IQueryable query = queryOptions.ApplyTo(dataArray().AsQueryable());
    
            return Ok(query);
        }
        }
  2. Create a GenericControllerFeatureProvider which is required to provide a generic controller for each available Entity:

    public class GenericControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
    {
        public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
        {
            var contextType = typeof(MyContext);
            var entityTypes = contextType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                .Where(x => x.PropertyType.IsGenericType)
                .Where(x => x.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
                .Select(x => x.PropertyType.GetGenericArguments().FirstOrDefault())
                .Where(x => x != null)
                .ToArray();
            foreach (var entityType in entityTypes)
            {
                feature.Controllers.Add(typeof(AggregateController<>).MakeGenericType(entityType).GetTypeInfo());
            }
        }
    }
    
    public static class GenericControllerFeatureProviderExtensions
    {
        public static IMvcBuilder AddGenericControllerFeatureProvider(this IMvcBuilder builder)
        {
            return builder.ConfigureApplicationPartManager(x => x.FeatureProviders.Add(new GenericControllerFeatureProvider()));
        }
    }
  3. Create a GenericControllerConvention which adds the routes for each entity to a generic controller:

    public class GenericControllerConvention : Attribute, IControllerModelConvention
    {
        public void Apply(ControllerModel controller)
        {
            if (controller.ControllerType.IsGenericType)
            {
                var entityType = controller.ControllerType.GenericTypeArguments.FirstOrDefault();
                if (entityType != null)
                {                   
                    if (controller.ControllerType.AsType() == typeof(AggregateController<>).MakeGenericType(entityType))
                    {
                        controller.Selectors.Add(new SelectorModel()
                        {
                            AttributeRouteModel = new AttributeRouteModel(new RouteAttribute($"api/Aggregate/{entityType.Name}"))
                        });
                    }
                }
            }
        }
    }
  4. Wire everything up in Startup.cs:

            services.AddControllers(options =>
            {
                options.Conventions.Add(new GenericControllerConvention());
            }).AddGenericControllerFeatureProvider();
JeanRessouche commented 3 years ago

Same on 8.0.1, simple groupBy do work, not with aggregate (makes the groupBy useless in most situations).

Works ?$apply=groupby((Field))

Doesn't ?$apply=groupby((Field),aggregate(Title with countdistinct as total)))

I know that a lot of effort is made every day on this great piece of software, but can this feature benefit from a higher priority ?

TehWardy commented 3 years ago

I'm starting to become convinced that this has never worked as i've been asking about it for some time.

... it doesn't seem to have a home where someone will actually look at it either.

MohammadMobasher commented 3 years ago

any progress here ?

spaasis commented 3 years ago

Seems like this is dead in the water? The fallback seems to be making custom functions for the required functionality

TehWardy commented 3 years ago

Yeh I've been doing that or supporting non odata based params that do the same thing ... tricky though if the returned object set changes shape.

zdzislaw commented 3 years ago

I'm having the same problem when I try to use $expand.

PackageReference Include="Microsoft.AspNetCore.OData" Version="8.0.2"

cubriq commented 3 years ago

I have been following this topic since .Net Core 2.0 and it is still not fixed. I have an application that is dependent on apply/groupby and this is the only reason why I am still on .Net Framework for this app. Very annoying. Starting to doubt that it will ever be fixed. Considering to learn node or graphql...

Grauenwolf commented 3 years ago

Are there are alternative ORMs that do support this?

Airex commented 3 years ago

This small hack works for me and allow to use $apply

<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.3.1" />
 var query = _context.Set<TEntity>().AsNoTracking();
         if (Request.Query.ContainsKey("$apply"))
            query = query.ToLinqToDB();

         return query;
cubriq commented 3 years ago

Thanx Airex. I got groupby working in .Net core 2.0 by using Linq2db, but it was broken in .net 3.1 and could not get it working. Is your example tested on .Net Core 3.1? Thx / Richard

Airex commented 3 years ago

Thanx Airex. I got groupby working in .Net core 2.0 by using Linq2db, but it was broken in .net 3.1 and could not get it working. Is your example tested on .Net Core 3.1? Thx / Richard

I am using this approach in .net 5.0 and EF 5.0.7. For 3.1 may be good idea to try different versions of Linq2db

cubriq commented 3 years ago

Thanx Airex. I got groupby working in .Net core 2.0 by using Linq2db, but it was broken in .net 3.1 and could not get it working. Is your example tested on .Net Core 3.1? Thx / Richard

I am using this approach in .net 5.0 and EF 5.0.7. For 3.1 may be good idea to try different versions of Linq2db

Ok, thanx! Will try it out!

Grauenwolf commented 3 years ago

Would it be possible to just use Linq2db for all of the OData calls and remove EF entirely?

Grauenwolf commented 3 years ago

This seems to be fixed in .NET 6.

    <PackageReference Include="Microsoft.AspNetCore.OData" Version="8.0.3" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
cubriq commented 2 years ago

Yes! I can confirm it has finally been fixed for .Net core in .Net 6. Have tested group by with different aggregates and it is working fine now, without the Linq2db workaround. Som hero at Microsoft has finally got it working after 5 years (!) since .Net core initial release. Now I can finally ugrade my core 2.0 apps with the Linq2db workaraound and also convert my other apps from .Net Framework 4.x (that has been working all along)...

TehWardy commented 2 years ago

Same old story, force another framework version on us that's broken in 100 other ways. Sigh ...

I'm getting whiplash with this once a year having to completely rebuild my apps from scratch because of breaking changes between framework versions.

Will Microsoft ever stop this BS so I can stabilise my codebase for 5 minutes?

I say this because the title literally states the version of frameworks that are broken and as always the answer is "don't use this, use another version" ... in the past i've been in the situation where multiple teams at Microsoft told me to both upgrade and downgrade my framework version in the same week.

Is there no way this fix can be pulled in to .Net 5 at least so I can avoid using an unstable .Net version?

Grauenwolf commented 2 years ago

The long term solution is to avoid Entity Framework entirely. I think EF Core 6 was the first time I wasn't burned by a new version breaking something I'm using. The team just can't be trusted to maintain stability.

TehWardy commented 2 years ago

Yeh I can't find a decent replacement for it though ... When you use features like OData it handles stuff like wiring up related entities in expands for you. I'm open to replacing it though, but I do recall the dapper guys not even understanding the question when I asked them.

EF is both a major pain in some ways but a god send in others ... you're right though, the constant breaking changes are a very bad running joke.

Grauenwolf commented 2 years ago

Dapper is definitely not an option unless you want to create your own SQL generator from scratch.

Presumably any ORM that supports IQueryable would work, but I never looked into it deeply.

TehWardy commented 2 years ago

Exactly my thoughts