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

Document difference between First and FirstOrDefault() #323

Closed
Mackiovello opened this issue Nov 23, 2017 · 8 comments
Closed

Document difference between First and FirstOrDefault() #323

Mackiovello opened this issue Nov 23, 2017 · 8 comments
Assignees

Comments

@Mackiovello
Copy link
Contributor

The class QueryResultRows has a property First. This property was deprecated in 2.3.1 in favor of Linq's FirstOrDefault method.

After checking out the Starcounter HelloWorld app and changing First to FirstOrDefault() on line 14 I get an exception when going to localhost:8080/HelloWorld:

System.Exception: ScErrDataBindingForJsonException (SCERR14012): An exception occurred when a databinding for a property in json was used. Property 'CurrentBalance' in 'PersonJson'.
Version: 2.3.1.7870.
Help page: https://docs.starcounter.io/v/2.3.1/?q=SCERR14012.
   at lambda_method(Closure , Json )
   at Starcounter.Templates.Property`1.GetAndCacheValue(Json parent) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Templates\Foundation\Property.cs:line 47
   at Starcounter.XSON.NewtonSoftSerializer.GetValue[T](Property`1 template, Json parent, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 575
   at Starcounter.XSON.NewtonSoftSerializer.SerializeLong(Json json, Template template, JsonWriter writer, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 172
   at Starcounter.XSON.NewtonSoftSerializer.SerializeObject(Json json, JsonWriter writer, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 241
   at Starcounter.Transaction.Scope(Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\Transaction.cs:line 216
   at Starcounter.Advanced.XSON.JsonExtension.Scope(Json json, Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Advanced\JsonExtension.cs:line 67
   at Starcounter.XSON.NewtonSoftSerializer.<>c__DisplayClass10_1.<Serialize>b__0() in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 79
   at Starcounter.Internal.TransactionManager.Scope(TransactionHandle handle, Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\TransactionManager.cs:line 551
   at Starcounter.Transaction.Scope(Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\Transaction.cs:line 224
   at Starcounter.Advanced.XSON.JsonExtension.Scope(Json json, Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Advanced\JsonExtension.cs:line 67
   at Starcounter.XSON.NewtonSoftSerializer.Serialize(Json json, Template template, TextWriter textWriter, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 82
   at Starcounter.XSON.NewtonSoftSerializer.Serialize(Json json, Template template, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 61
   at Starcounter.Internal.PuppetRestHandler.CreateJsonBodyResponse(Session session, Json root) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\BuiltInRestHandlers\PuppetRestHandler.cs:line 229
   at lambda_method(Closure , Request , IntPtr , IntPtr )
   at Starcounter.Rest.UserHandlerInfo.RunUserDelegate(Request req, IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Rest\UriHandlersManager.cs:line 201
   at Starcounter.Internal.Web.AppRestServer.RunDelegateAndProcessResponse(IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack, Request req) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\AppRestServer.cs:line 96
   at Starcounter.Internal.AppsBootstrapper.ProcessExternalRequest(Request req) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\AppsBootstrapper.cs:line 348
HResult=-2146233088
HelpLink=https://docs.starcounter.io/v/2.3.1/?q=SCERR14012
---> Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'decimal' because it is a non-nullable value type
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at Starcounter.SqlEnumerator`1.get_Current() in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\Query\Execution\Enumerators\GenericEnumerator.cs:line 180
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at HelloWorld.Person.get_CurrentBalance() in C:\Users\User\Documents\GitHub\HelloWorld\src\HelloWorld\Program.cs:line 14
   at lambda_method(Closure , Json )
HResult=-2146233088

Changing back to First makes it work.

What's the difference between First and FirstOrDefault()? Does it handle null differently?

I'm on Starcounter 2.3.1.7870

@Mackiovello Mackiovello changed the title Difference between First and FirstOrDefault() Difference between First and FirstOrDefault() Nov 23, 2017
@Mackiovello
Copy link
Contributor Author

Simpler reproducible case:

using Starcounter;
using System.Linq;

namespace StarcounterApplication1
{
    [Database]
    public class Person
    {
        public decimal Money { get; set; }
    }

    class Program
    {
        static void Main()
        {
            Handle.GET("/Hello", () =>
            {
                var result = Db.SQL<decimal>($"SELECT SUM(p.Money) FROM {typeof(Person)} p");
                var first1 = result.First;
                var first2 = result.FirstOrDefault();
                return "done";
            });
        }
    }
}

The line var first2 = result.FirstOrDefault() throws the exception:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'decimal' because it is a non-nullable value type
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at Starcounter.SqlEnumerator`1.get_Current() in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\Query\Execution\Enumerators\GenericEnumerator.cs:line 180
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at StarcounterApplication1.Program.<>c.<Main>b__0_0() in C:\Users\User\source\
epos\StarcounterApplication1\StarcounterApplication1\Program.cs:line 26
   at Starcounter.Rest.UserHandlerInfo.RunUserDelegate(Request req, IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Rest\UriHandlersManager.cs:line 201
   at Starcounter.Internal.Web.AppRestServer.RunDelegateAndProcessResponse(IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack, Request req) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\AppRestServer.cs:line 96
   at Starcounter.Internal.AppsBootstrapper.ProcessExternalRequest(Request req) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\AppsBootstrapper.cs:line 348
HResult=-2146233088

@chrhol
Copy link

chrhol commented Nov 23, 2017

Ouch. It looks like we are back with the issue discussed at length in https://github.com/Starcounter/level1/issues/2134

In short: aggregations for MIN, MAX, SUM might internally return null, while a default value for a generic valuetype cannot be null. Not sure what is a feasible solution for this problem. @miyconst: discuss it tomorrow in the release planning perhaps?

@Mackiovello
Copy link
Contributor Author

Mackiovello commented Nov 24, 2017

Nullable types do not work with calculated properties:

public decimal? CurrentBalance =>
        Db.SQL<decimal?>("SELECT SUM(e.Amount) FROM Expense e WHERE e.Spender = ?", this).FirstOrDefault();

Throws:

System.Exception: ScErrDataBindingForJsonException (SCERR14012): An exception occurred when a databinding for a property in json was used. Property 'CurrentBalance' in 'PersonJson'.
Version: 2.3.1.7870.
Help page: https://docs.starcounter.io/v/2.3.1/?q=SCERR14012.
   at lambda_method(Closure , Json )
   at Starcounter.Templates.Property`1.GetAndCacheValue(Json parent) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Templates\Foundation\Property.cs:line 47
   at Starcounter.XSON.NewtonSoftSerializer.GetValue[T](Property`1 template, Json parent, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 575
   at Starcounter.XSON.NewtonSoftSerializer.SerializeLong(Json json, Template template, JsonWriter writer, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 172
   at Starcounter.XSON.NewtonSoftSerializer.SerializeObject(Json json, JsonWriter writer, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 241
   at Starcounter.Transaction.Scope(Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\Transaction.cs:line 216
   at Starcounter.Advanced.XSON.JsonExtension.Scope(Json json, Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Advanced\JsonExtension.cs:line 67
   at Starcounter.XSON.NewtonSoftSerializer.<>c__DisplayClass10_1.<Serialize>b__0() in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 79
   at Starcounter.Internal.TransactionManager.Scope(TransactionHandle handle, Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\TransactionManager.cs:line 551
   at Starcounter.Transaction.Scope(Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter\Transaction.cs:line 224
   at Starcounter.Advanced.XSON.JsonExtension.Scope(Json json, Action action) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Advanced\JsonExtension.cs:line 67
   at Starcounter.XSON.NewtonSoftSerializer.Serialize(Json json, Template template, TextWriter textWriter, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 82
   at Starcounter.XSON.NewtonSoftSerializer.Serialize(Json json, Template template, JsonSerializerSettings settings) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.XSON\Serializer\NewtonSoftSerializer.cs:line 61
   at Starcounter.Internal.PuppetRestHandler.CreateJsonBodyResponse(Session session, Json root) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\BuiltInRestHandlers\PuppetRestHandler.cs:line 229
   at lambda_method(Closure , Request , IntPtr , IntPtr )
   at Starcounter.Rest.UserHandlerInfo.RunUserDelegate(Request req, IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Rest\UriHandlersManager.cs:line 201
   at Starcounter.Internal.Web.AppRestServer.RunDelegateAndProcessResponse(IntPtr methodSpaceUriSpaceOnStack, IntPtr parametersInfoOnStack, Request req) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\AppRestServer.cs:line 96
   at Starcounter.Internal.AppsBootstrapper.ProcessExternalRequest(Request req) in C:\TeamCity\BuildAgent\work\sc-11226\Level1\src\Starcounter.Apps.JsonPatch\AppsBootstrapper.cs:line 348
HResult=-2146233088
HelpLink=https://docs.starcounter.io/v/2.3.1/?q=SCERR14012
---> System.InvalidOperationException: Nullable object must have a value.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at lambda_method(Closure , Json )
HResult=-2146233079

@miyconst
Copy link
Member

Nullable types do not work with calculated properties:

@Mackiovello I believe that by "calculated properties" you meant the bound properties in Typed JSON, which is not the problem of Db.SQL.

The nullable values are not supported by Typed JSON, so the property declaration should be changed to:

public decimal CurrentBalance =>
        Db.SQL<decimal?>("SELECT SUM(e.Amount) FROM Expense e WHERE e.Spender = ?", this).FirstOrDefault() ?? 0;

@miyconst
Copy link
Member

In short: aggregations for MIN, MAX, SUM might internally return null, while a default value for a generic valuetype cannot be null. Not sure what is a feasible solution for this problem. @miyconst: discuss it tomorrow in the release planning perhaps?

The .Min, .Max, and .Average LINQ extension methods throw System.InvalidOperationException: Sequence contains no elements if the collection has no element. It makes the behavior be on par with what we have, the only problem is the exception message.

MS-SQL returns NULL in this case, the same Starcounter does, and it fails when trying to convert NULL into required data type (int, decimal or so).

For me everything is good enough to keep as is.

cc @k-rus

@Mackiovello Mackiovello changed the title Difference between First and FirstOrDefault() Document difference between First and FirstOrDefault() Nov 27, 2017
@Mackiovello Mackiovello self-assigned this Nov 27, 2017
@Mackiovello
Copy link
Contributor Author

The blog post has been updated to reflect this. Closing

@k-rus
Copy link

k-rus commented Nov 27, 2017

cc @k-rus

@miyconst Can you clarify what is your question to me?

If I remember correctly, SQL standard defines different behavior of aggregates on empty relation than LINQ implements.

@miyconst
Copy link
Member

If I remember correctly, SQL standard defines different behavior of aggregates on empty relation than LINQ implements.

Yes, that is correct, and I cc you to make sure there is nothing extra you would like to add.

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

No branches or pull requests

4 participants