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

EF6 and EF Core version controversy #102

Closed
leotsarev opened this issue Oct 4, 2019 · 23 comments
Closed

EF6 and EF Core version controversy #102

leotsarev opened this issue Oct 4, 2019 · 23 comments
Labels

Comments

@leotsarev
Copy link
Contributor

leotsarev commented Oct 4, 2019

Consider following scenario.
Project A references both LinqKit and EF6 and has following code:

Loader.cs
...
using EntityFramework;
using LinqKit;
...
Foo[] GetFoos() =>
  ef6Context.Foos.AsExpandable().Where(f => fooPredicate.Invoke(f)).ToArrayAsync();

Now we need adding new context with Bar data to our application, and choosing to use new brand shine EF Core.
We need to add references (may be transitional) to EF Core and LinqKit.Microsoft.EntityFrameworkCore. Than we try to add following code to Loader.cs

using EntityFramework;
using LinqKit;
using Microsoft.EntityFrameworkCore;
...
Bar[] GeBars() =>
  efCoreContext.Bars.AsExpandable().Where(f => barPredicate.Invoke(f)).ToArrayAsync();

Oops, we get compile error about not able to resolve AsExpandable, Invoke and ToArrayAsync. Latter is pretty easy to fix - we can rename namespace or just refactor loading bar to BarLoader.cs

BarLoader.cs
...
//  REMOVED using EntityFramework;
using LinqKit;
using Microsoft.EntityFrameworkCore;
...
Bar[] GeBars() =>
  efCoreContext.Bars.AsExpandable().Where(f => barPredicate.Invoke(f)).ToArrayAsync();

Now compiler could figure ToArrayAsync, but still fails to figure LinqKit types between LinqKit.EntityFramework and LinqKit.Microsoft.EntityFrameworkCore.

Note that for this to fail it enough to have transitive dependency to LinqKit somewhere in your tree.
There is two problems that lead to that:

  • Our architecture was not clean/layered enough (if all LinqKit uses was isolated in project like FooRepository.csproj, we could introduced BarRepository.csproj). But we actively using LinqKit/EF directly in our service layer, and as result have hard dependency
  • Same namespaces for incompatible versions LinqKit.

We explored trying to wrap LinqKit in our helper lib with different namespaces, but failed.
It's ok to wrap AsExpandable() to AsExpandable6() and AsExpandableCore(), but wrapping Invoke failed (as expected, because it checked on visiting expression tree).

Proposed solutions:

  • Introduce version of LinqKit that have different namespaces.
  • Try to have changed names (so, for example LinqKit.Microsoft.EntityFrameworkCore will have both AsExpandable() and AsExpandableCore()

I'm willing to contribute, but of course I need approval first.

@StefH
Copy link
Collaborator

StefH commented Oct 4, 2019

I don't understand:
Now compiler could figure ToArrayAsync, but still fails to figure LinqKit types between LinqKit.EntityFramework and LinqKit.Microsoft.EntityFrameworkCore.

What do you mean by LinqKit types?

And you can just use #if / #else compiler switches to make a common library which uses EF6 or EFCore.

@StefH
Copy link
Collaborator

StefH commented Oct 4, 2019

Maybe you can provide an example project ?

@leotsarev
Copy link
Contributor Author

leotsarev commented Oct 4, 2019

@leotsarev
Copy link
Contributor Author

No, I can't use compile switches, because I need EF6 and EF Core both working :-)

@StefH
Copy link
Collaborator

StefH commented Oct 4, 2019

So you want one project where you want to use EF6.3 and EFCore???

@leotsarev
Copy link
Contributor Author

Basically yes.

@StefH
Copy link
Collaborator

StefH commented Oct 7, 2019

@leotsarev
Copy link
Contributor Author

Thanks for help! Do you think that having workaround in terms of AsExpandable6/AsExpandableCore and Invoke6/InvokeCore in LinqKit directly won't hit cost/benefit bar?

@StefH
Copy link
Collaborator

StefH commented Oct 7, 2019

Adding these workaround methods is also possible.

  • AsExpandableEF
  • AsExpandableEFCore
    &
  • InvokeEF
  • InvokeEFCore

I'll check if this can be done easily, or that some complex changes are needed.

@StefH
Copy link
Collaborator

StefH commented Oct 15, 2019

@leotsarev
A new version (preview-03) can be downloaded from MyGet and can be used like:

var ef6Ctx = new Ef6Context();
var foo = await ef6Ctx.FooSet.AsExpandableEF().ToListAsync();

var coreCtx = new CoreContext();
var bar = await coreCtx.BarSet.AsExpandableEFCore().ToListAsync();

@StefH
Copy link
Collaborator

StefH commented Oct 23, 2019

@leotsarev Could you test this?

@leotsarev
Copy link
Contributor Author

Yep, we'll try to

@StefH
Copy link
Collaborator

StefH commented Oct 29, 2019

@leotsarev Any progress on testing this?

@leotsarev
Copy link
Contributor Author

Sorry for delay. Seems that InvokeEF/InvokeEFCore are missing

            Expression<Func<Foo, bool >> predicate = f => true;
            var foo = await ef6Ctx.FooSet.AsExpandableEF().Where(x => predicate.Invoke(x)).ToListAsync();

@StefH
Copy link
Collaborator

StefH commented Oct 30, 2019

Preview-04 should be available on MyGet

@ynauls
Copy link

ynauls commented Nov 7, 2019

@StefH, any way you can upload the preview to Nuget.org?

@StefH
Copy link
Collaborator

StefH commented Nov 8, 2019

@leotsarev Can you please verify?

@ynauls I'll upload the preview-04 also to NuGet in some time.

@StefH
Copy link
Collaborator

StefH commented Nov 8, 2019

@ynauls version 1.1.17-preview-04 is uploaded to NuGet

@leotsarev
Copy link
Contributor Author

leotsarev commented Nov 12, 2019

Sorry again for slow replies.

No, Invoke is not working for us.
Following code snippet:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using LinqKit;
using MyLinqKit;
using MyLinqKitCore;

 

namespace EntityPlusEntityCore
{
    class Program
    {
        static async Task Main()
        {
            Expression<Func<EfModel, bool>> idExpr = x => x.Id == 5;

 

            var context = new EfContext();
            var x1 = await context.EfModels
                                  .AsExpandableEF()
                                  //.MyWhere(idExpr)
                                  .Where(x => idExpr.InvokeEF(x))
                                  .MyCountAsync();

 


            Expression<Func<EfModelCore, bool>> idExprCore = x => x.Id == 5;

 

            var contextCore = new EfContextCore();
            var x2 = await contextCore.EfModels
                                      .AsExpandableEFCore()
                                      //.MyWhere(idExprCore)
                                      .Where(x => idExprCore.InvokeEFCore(x))
                                      .MyCountCoreAsync();
        }
    }
}

Neither version are working:

EF6 version: NotSupportedException: LINQ to Entities does not recognize the method Boolean InvokeEF[EfModel, Boolean]
EF Core version: Fallback to client side execution, no WHERE in resulting SQL.

@StefH
Copy link
Collaborator

StefH commented Nov 12, 2019

Does the code work when you don't call invoke manually? Like my answer in this issue #105

@leotsarev
Copy link
Contributor Author

yes

@StefH
Copy link
Collaborator

StefH commented Nov 26, 2019

#103

@StefH StefH closed this as completed Nov 26, 2019
@StefH StefH added the feature label Nov 26, 2019
@Bert-Proesmans
Copy link

Hi @StefH,

This works for Where() methods but not within Select() (projections) where it is required to manually call Invoke on the Func expressions.

I'm in the same situation where i'm working with both EF and EFCore for the foreseeable future because my supplier only provides an adapter for EF currently.
Calling InvokeEF throws the exception below at runtime. I'm simply calling this;

[..]
.Select(x => new ArticleContract()
{
deliveryOrderBook = bookConverter.InvokeEF(x.order.BESTELBKCODE),
})
[..]
System.NotSupportedException: LINQ to Entities does not recognize the method 'Datatypes.Briljant.EF.Semantics.BestellingBoek InvokeEF[String,BestellingBoek](System.Linq.Expressions.Expression`1[System.Func`2[System.String,Datatypes.Briljant.EF.Semantics.BestellingBoek]], System.String)' method, and this method cannot be translated into a store expression.
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.UnaryTranslator.TypedTranslate(ExpressionConverter parent, UnaryExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberInitTranslator.TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberInitTranslator.TypedTranslate(ExpressionConverter parent, MemberInitExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SelectTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass43_0.<GetResultsAsync>b__1()
   at System.Data.Entity.Core.Objects.ObjectContext.<ExecuteInTransactionAsync>d__156`1.MoveNext()
[Omitted for brevity]

Is there intention to fix this?
In the meantime i'll look at assembly aliasing as suggested above, but i dislike having to go into the project file and make manual tweaks in general.

Thank you in advance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants