Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.Interactive.Async call is ambiguous error when IX-Async is referenced #18124

Closed
shravan2x opened this issue Sep 29, 2019 · 66 comments
Closed
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@shravan2x
Copy link

I'm using .NET Core 3.0 with EF Core and System.Interactive.Async. Using methods like FirstOrDefaultAsync and Where results in compilation errors like

The call is ambiguous between the following methods or properties: 'Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync<TSource>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Func<TSource, bool>>, System.Threading.CancellationToken)' and 'System.Linq.AsyncEnumerable.FirstOrDefaultAsync<TSource>(System.Collections.Generic.IAsyncEnumerable<TSource>, System.Func<TSource, bool>, System.Threading.CancellationToken)'
The call is ambiguous between the following methods or properties: 'System.Linq.Queryable.Where<TSource>(System.Linq.IQueryable<TSource>, System.Linq.Expressions.Expression<System.Func<TSource, bool>>)' and 'System.Linq.AsyncEnumerable.Where<TSource>(System.Collections.Generic.IAsyncEnumerable<TSource>, System.Func<TSource, bool>)'

Steps to reproduce

  1. Reference EFCore.
  2. Reference System.Interactive.Async
public class FruitDb : DbContext
{
    public DbSet<Apple> Apples { get; set; }

    public async Task Seeds5Async()
    {
        Apple apple = await Apples.FirstOrDefaultAsync(x => x.NumSeeds == 5);
        // ... use it
    }
}

Further technical details

EF Core version: 3.0.0
Database provider: Sqlite
Target framework: .NET Core 3.0
Operating system: Windows 10
IDE: Visual Studio 2019 16.3

@smitpatel smitpatel self-assigned this Sep 30, 2019
@smitpatel
Copy link
Contributor

@shravan2x - Ways to deal with this

  • Don't reference both libraries unless you require it in your project
  • Both references are in different namespaces. If you have using for only one namespace it would work
  • If you cannot use any of above then you need to use static method as static form rather than extension form with full type specification. Like this

Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync(Apples, x => x.NumSeeds == 5)

@smitpatel smitpatel removed their assignment Sep 30, 2019
@smitpatel smitpatel added closed-no-further-action The issue is closed and no further action is planned. and removed type-bug labels Sep 30, 2019
@carlreinke
Copy link
Contributor

All of those solutions are basically non-starters.

I worked around this by defining some extension methods:

internal static class DbSetExtensions
{
    public static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this DbSet<T> dbSet)
        where T : class
    {
        return dbSet;
    }

    public static IQueryable<T> AsQueryable<T>(this DbSet<T> dbSet)
        where T : class
    {
        return dbSet;
    }
}

DbSet<T> was nicer to use when it had AsAsyncEnumerable() rather that implementing IAsyncEnumerable<T>.

@shravan2x
Copy link
Author

shravan2x commented Oct 2, 2019

@smitpatel Your answer is not helpful at all.

  • I definitely require both libraries.
  • I can't have a using for only one library because I definitely use both libraries in my code.
  • Using a full type specification beats the point of using extension methods.

Additionally, you didn't mention that using namespace aliases allows you to only specify the full type specification of one of the libraries, in case it is used much more often than the other.

But i'm already aware of all these solutions. I posted this issue to engage constructive conversation on how to improve our API surface and find a permanent solution, especially since async LINQ methods are very useful.

Since your solution doesn't address my problem, please reopen the issue.

@smitpatel
Copy link
Contributor

@shravan2x - Ambiguity in method resolution arises in compiler. There can be more ways to work-around the issue. I am not sure how can we improve API surface for some issue which is compiler issue rather than EF. We do not want to change Name FirstOrDefaultAsync or change interfaces implemented by DbSet. If you have any other idea, do tell us.

@smitpatel smitpatel reopened this Oct 2, 2019
@shravan2x
Copy link
Author

shravan2x commented Oct 2, 2019

@smitpatel I notice that this issue only occurs when the Microsoft.EntityFrameworkCore.Sqlite package version is upgraded from 2.2.6 to 3.0.0. With 2.2.6, both packages are installed and work fine. Do you know why that may be?

If you prefer, I can prepare a repro.

@carlreinke Your solution works great!

@smitpatel
Copy link
Contributor

EF Core 2.2 used IX-Async package for async enumerables. In 3.0 EF Core does not depend on IX-Async rather uses IAsyncEnumerable from compiler directly.

@shravan2x
Copy link
Author

shravan2x commented Oct 3, 2019

I've updated my code to use @carlreinke 's solution. I'm fairly happy with it. Overall, you're right that this problem is hard to solve without changing the interfaces implemented by DbSet.

However, (if I understand correctly), this problem occurs because DbSet implements IEnumerable. When iterating over it using IEnumerable, all rows in the DB have to be fetched. However, fetching all rows is not a very common use case (at least for users like me). Perhaps we could add an alternative to DbSet that doesn't implement IEnumerable (maybe a base class)?

@smitpatel
Copy link
Contributor

fetching all rows is not a very common use case

Exactly. It is not common to use both libraries in same project, where both method namespaces needs to be imported in same file where you are using raw DbSet to apply an async operation. Hence there is very little value to add a base class and not confuse other users which are using DbSet in normal way. Work-arounds posted above etc are good way to handle the corner case where customers run into it.

@carlreinke
Copy link
Contributor

It is not common to use both libraries in same project, where both method namespaces needs to be imported in same file where you are using raw DbSet to apply an async operation.

I don't know why you think that. I'm using async-LINQ to finish up the parts of queries that can't be translated to SQL.

People use AsEnumerable() on their query and then follow that up with LINQ — in fact, this is prescribed in your own documentation. Why are they not going to want to do the same in async scenarios?

@NicolasDorier
Copy link

NicolasDorier commented Oct 4, 2019

I found out that this is worse than that, forget System.Interactive.Linq. If you use both System.Linq and Microsoft.EntityFrameworkCore, you can't even use Where clause on the DbSet...

#18220

I hope I am wrong and I messed up somewhere, because I can't believe such a simple use case can be broken.

@NicolasDorier
Copy link

NicolasDorier commented Oct 4, 2019

Ok so for those stuck, I ended up adding several extensions method at the root namespace of my project. This work because the compiler prefer extension methods in the current or parent namespace before looking the usings.

public static IAsyncEnumerable<TEntity> AsAsyncEnumerable<TEntity>(this Microsoft.EntityFrameworkCore.DbSet<TEntity> obj) where TEntity : class
{
        return Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.AsAsyncEnumerable(obj);
}
public static IQueryable<TEntity> Where<TEntity>(this Microsoft.EntityFrameworkCore.DbSet<TEntity> obj, System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate) where TEntity : class
{
    return System.Linq.Queryable.Where(obj, predicate);
}

Please tell me this is not the only workaround, and I fucked up somewhere, how could something like that could have been missed?

@smitpatel
Copy link
Contributor

I tested it even further,
Issues arises only when applying async operator on DbSet. Further, if you want to use EF method, you can call AsQueryable on DbSet and call EF method afterwards and it will work fine. Like this
await db.Blogs.AsQueryable().FirstOrDefaultAsync();

AsAsyncEnumerable, which we suggest as using work-around does not work either since it also has ambiguity. The root cause is, IX-async has put methods in System.Linq namespace. IAsyncEnumerable does not have methods defined in bcl which everyone can use. So multiple libraries have to define these methods. And since it being in System.Linq, it causes clashes on normal occasion too.

@ajcvickers
Copy link
Contributor

After discussing again and re-investigating there doesn't seem to be anything we can do here. It would be a "normal" conflicting methods in two different namespaces problem, except that since, as @smitpatel says, IX-async decided to put their methods into System.Linq which then invalidates many of the normal mechanisms provided by the compiler for dealing with such issues.

@ajcvickers ajcvickers added closed-external and removed closed-no-further-action The issue is closed and no further action is planned. labels Oct 14, 2019
@NicolasDorier
Copy link

@smitpatel actually I remember I saw a way to remove ambiguity of namespace conflicts in C#.... trying to find again/

@NicolasDorier
Copy link

NicolasDorier commented Oct 18, 2019

So for those stuck on AsAsyncEnumerable conflict, you can use extern alias

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extern-alias
with the :: operator https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/namespace-alias-qualifier

When I will fix it in my own code later, I will publish a clean solution here.

@ajcvickers @smitpatel I understand why you close this issue, but this is a big design mistake you did here. Those namespace conflicts will create countless of issues to noobs trying to figure out what is happening. Even me, coding in C# for more than 10 years non stop, I got confused...

Because you probably don't want to make breaking change again, you will probably not change this decision, but still, ef did a huge design mistake.

You are making EF impossible to use with other libraries without using an obscure feature of the C# language that yourself don't even know, and causing endless of conflict of Select/Where.

You should better remove interfaces from dbset, just coding classic method AsQueryable() and AsAsyncEnumerable() within the type itself so it can't possibly have conflicts.

@smitpatel
Copy link
Contributor

@NicolasDorier -
It is a huge design mistake on IX-Async part to add their methods inside System.Linq namespace.
Any library like EF which works with Queryable which can also be enumerated async.

  • Either define method to enumerate async and clash with IX-Async.
  • Not define such method and customers who are not using IX-Async cannot enumerate async anymore.

Such methods if defined in BCL which everyone would be using by default make sense but not in external libraries.

@ajcvickers
Copy link
Contributor

@NicolasDorier If there's something we can do in EF Core to make this better, then please suggest. We will consider it, even if it is a breaking change, based on how much help it would be and based on how much it would impact other experiences.

@NicolasDorier
Copy link

@ajcvickers this should never happen #18220 it happens whenever you reference namepace System.Linq and EF in the same file.

You can solve it by not implementing IQueryable on DbSet and instead put a AsQueryable non extension method. It would make clear to the user how to use it properly.

@smitpatel
Copy link
Contributor

If you were using async interactive before, why not keep the reference in 3.0

  • Because we don't need to use it.
  • People who are not using Ix-Async don't need to get additional dependencies they don't use.

@MilesAdamson
Copy link

I don't know why you think that. I'm using async-LINQ to finish up the parts of queries that can't be translated to SQL.

Same, why is this issue closed?

@roji

This comment has been minimized.

@Webreaper

This comment has been minimized.

roji added a commit to dotnet/EntityFramework.Docs that referenced this issue Sep 2, 2020
* Merge query and saving async pages
* Move under fundamentals
* Document ambiguous invocation issues with System.Interactive.Async
  and show workaround.

Closes #1722
Related to #1332
Related to dotnet/efcore#18124
roji added a commit to dotnet/EntityFramework.Docs that referenced this issue Sep 2, 2020
* Merge query and saving async pages
* Move under fundamentals
* Document ambiguous invocation issues with System.Interactive.Async
  and show workaround.

Closes #1722
Related to #1332
Related to dotnet/efcore#18124
roji added a commit to dotnet/EntityFramework.Docs that referenced this issue Sep 2, 2020
* Merge query and saving async pages
* Move under fundamentals
* Document ambiguous invocation issues with System.Interactive.Async
  and show workaround.

Closes #1722
Related to #1332
Related to dotnet/efcore#18124
roji added a commit to dotnet/EntityFramework.Docs that referenced this issue Sep 2, 2020
* Merge query and saving async pages
* Move under fundamentals
* Document ambiguous invocation issues with System.Interactive.Async
  and show workaround.

Closes #1722
Related to #1332
Related to dotnet/efcore#18124
roji added a commit to dotnet/EntityFramework.Docs that referenced this issue Sep 3, 2020
* Refactor and improve async documentation

* Merge query and saving async pages
* Move under fundamentals
* Document ambiguous invocation issues with System.Interactive.Async
  and show workaround.

Closes #1722
Related to #1332
Related to dotnet/efcore#18124
@NetMage
Copy link

NetMage commented Sep 30, 2020

That means neither Enumerable nor AsyncEnumerable extensions need to be called on a query used with EF Core.

That's really funny coming from the same team that recommends converting GroupJoin and GroupBy that EF Core can't handle by using AsEnumerable() in the query.

@waf
Copy link

waf commented Nov 27, 2020

It would be good to re-open this, at least for tracking purposes. The Reactive team considers this to be a design issue in EF Core:

@gojanpaolo
Copy link

I like how MoreLinq can handle conflicts with other libraries: https://github.com/morelinq/MoreLINQ#usage
I also think that this whole issue is caused largely by IX-Async decision to put their extensions in System.Linq.

@gandhis1
Copy link

gandhis1 commented Nov 27, 2020

I like how MoreLinq can handle conflicts with other libraries:

The way MoreLinq handles it is terrible and there is a whole thread of people complaining about it here:

morelinq/MoreLINQ#565

@roji
Copy link
Member

roji commented Feb 18, 2021

FYI, for EF Core 6.0, DbSet will no longer be implementing IAsyncEnumerable, removing the ambiguity and the need to call AsQueryable - see #24041 for more details and background.

@omerozkan1
Copy link

shravan2x

Try the following and see it works. It worked successfully for me.

Instead of:

using System.Linq;

you just need:

using static System.Linq.Queryable;
using static System.Linq.Enumerable; // You only need this if you're using LINQ to Objects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
Development

No branches or pull requests