-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Client vs. Server evaluation can sometimes call a DbFunction's static method template #9725
Comments
Above is the query model generated for the query. |
It's known that a DbFunction may evaluate client side. As you have found out if the query is not completely translatable the function may be run clientside, depending on what part of the query it is in. In your case you are ordering on a property that the database doesn't know about, so EF breaks the query into server and client side. As @smitpatel said to work around this you can either sort on a column in the database, or ToList the select and then apply the order by. Alternatively you can provide a client side implementation of the DbFunction and it will be run correctly. Not all functions are implementable client side, but some are, and in those cases it will work. |
Thanks for the input. As I added to my first comment - I found this type of error quite hard to diagnose, as it isn't obvious what is happening. I didn't even think that a DbFunction would be evaluated client side, but now I know. I think I will change the Exception in say "Called in Client vs. Server evaluation" (and update the book to say that too). Thanks for clarifying this. |
While it may seem bit difficult to figure out what happened there is enough of logging happening for that.
The line where exception is happening is from DbFunction defined. It is System.Exception because that is what is written in code. Throwing
This capture that the function will be called on client explicitly. |
Hi @pmiddleton, I have been looking at the idea that DbFunction methods could be called by client vs. server evaluation, and therefore could have real code inside the DbFunction method body. My first try was to look at adding code into the static method, but there is no access to the current DbContext (well it could be, but only via some static approach, which is horrible), so that was a non-starter. I did try defining a non-static method inside the DbContext, which would have allowed the method access to the current DbContext, but I get the error:
I therefore content that creating a a software version is pretty difficult, as the UDF is likely to take the minimum of information as an input parameter and it cannot access the DbContext. My Am I missing something here? If you want to allow client vs. server evaluation is it possible to allow the DbFunction method to be non-static?? I would appreciate your thoughts on this. PS. Personally I think the DbFunction is the easiest way to include custom SQL in a query and I want to understand this to convey this to others. |
Currently you can't define an instance method as a dbFunction. That is a limitation due to time constraint in order to get things done for 2.0. I am actually working on this feature now - see #9213 I had not considered the use case which you are proposing for that feature. I had always thought it as just a shorthand to avoid some typing. I had not considered making database calls from it in cases of client side evaluation. IDK if that would work. You would be making one or more database calls in the middle of another parent call. It may work, or it may blow up. @smitpatel @anpete - care to put forward any guesses what would happen in that use case? I will give it a try once I can get instance methods working and see what happens. At a minimum it seems very bad performance wise as you will would end up executing N+1 queries. I always envisioned that the ability to make that client side call would be for simple functions, and actually something you would actively try to avoid in favor of server side execution. |
It should work (it's conceptually the same as any other mixed eval query) but we may need to address any wrinkles that crop up. Some advantages of providing a client implementation are:
|
I have a working copy of instance method support up if you want to play with it. This is based off of the latest dev branch. I would be curious if it works for the scenario you are envisioning. |
Hi @pmiddleton, I read your comment and I thought I would give you my view on how/why DbFunctions will be used. My comments about developer usage scenarios, but hopefully they may help you in your thinking.
I hope my ramblings are helpful. I'm pleased to see DbFunctions are going to evolve in the future. I love them. |
I ran a test for your use case. I tried making another database call from a dbFunction which was being client side evaluated. As I thought EF Core won't let this call happen as there is another call in progress. The internal concurrency detection prevents the second call from completing. Client side evaluation for DB Functions is going to have to stay limited to very simple functions which can be implemented without making any database calls. In regards to item 3. That is only ever going to be a Sqlite feature. Sqlite UDFs are implemented in C not in SQL. Interop is then allowing .Net code to replace the C function in @bricelam example. For other providers to support this we would need some kind of C# -> Sql translation which is just not possible. |
@pmiddleton You are hitting #7375 - we need to fix it. |
I will keep an eye on that issue and retest if/when it is resolved. @JonPSmith even if the re-entrance issue is resolved I still would recommend against coding a method this way as it will still have the N+1 issue. Finding a way to rework the query to run server side is a much better option. |
@pmiddleton, I am quite happy with DbFunction not running with client vs. server, and I think others will be happy too. One of the reasons I found it hard to diagnose the problem I couldn't even envisage that a SQL UDF would run client side. |
Note from triage: Since using the context inside client eval of the function is blocked by #7375, and this is in turn blocked by #8864, @anpete is going to investigate #8864 again to see whether we can do something for 2.1. However, #8864 looks quite hard, so we will cycle back again after the investigation and decide how to proceed. |
I had the exception inside one of my
[DbFunction]
static method template being triggered in a complex query. I stripped everything away, and I found that an outerOrderBy
was deemed to need Client vs. Server evaluation, which dragged theDbFunction
method into the client side evaluation, with the obvious result of exception in the static method being called.I found this type of error quite hard to diagnose, as it isn't obvious what is happening. I really didn't think a DbFunction would be be called in Client vs. Server evaluation, but now I know.
I have isolated a simple example as shown below
The logging output from my unit test is
Steps to reproduce
Below I list my unit test. The significant thing in this unit test is that the
ReviewsAverageVotes
value is not set, which seems to trigger the Client vs. Server evaluation (My query was a lot more complex that that, but I proved it triggered the same issue).Notes:
var options = this.ClassUniqueDatabaseSeeded4Books();
produces a uniquely named database and seeds it with four test books.context.ExecuteScriptFileInTransaction(filepath);
adds the UDFs to the database.My entity classes (which you must be sick of seeing:)) are:
Linking Table is
I haven't provided the
DbContext
with its setup, but I don't think you need it. If you do then I can set it, but all the code can be found the open-source project on GitHub in branchChapter13-Part1
- see https://github.com/JonPSmith/EfCoreInAction/tree/Chapter13-Part1. You can find the actual unit test at https://github.com/JonPSmith/EfCoreInAction/blob/Chapter13-Part1/Test/UnitTests/ServiceLayer/Ch13_BookListDtoImproved.cs#L83-L117 .Further technical details
EF Core version: EF Core 2.0.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 15.3
The text was updated successfully, but these errors were encountered: