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

Binding nullable int to input causes null reference exception #466

Closed
floreseken opened this Issue Apr 2, 2018 · 4 comments

Comments

Projects
None yet
4 participants
@floreseken
Copy link

floreseken commented Apr 2, 2018

Minimal repro steps

Consider this Blazor page:

<input @bind(TestInt) />

@functions
{
    public int? TestInt { get; set; }

}

Expected result

Page should render without exceptions

Actual result

A nullref exception is thrown:

SCRIPT5022: System.NullReferenceException: Object reference not set to an instance of an object.
  at Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder.AddAttribute (System.Int32 sequence, System.String name, System.Object value) <0x169c870 + 0x0005c> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Datack.Blazor.Pages.Data.Index.BuildRenderTree (Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder) <0x1977930 + 0x000dc> in <cd4fad1f0c094e678f0f1612015bbf56>:0 
  at (wrapper delegate-invoke) <Module>.invoke_void_RenderTreeBuilder(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder)
  at Microsoft.AspNetCore.Blazor.Rendering.ComponentState.RenderIntoBatch (Microsoft.AspNetCore.Blazor.Rendering.RenderBatchBuilder batchBuilder, Microsoft.AspNetCore.Blazor.RenderFragment renderFragment) <0x16964f8 + 0x0006c> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Microsoft.AspNetCore.Blazor.Rendering.Renderer.RenderInExistingBatch (Microsoft.AspNetCore.Blazor.Rendering.RenderQueueEntry renderQueueEntry) <0x16960a0 + 0x00040> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Microsoft.AspNetCore.Blazor.Rendering.Renderer.ProcessRenderQueue () <0x16931b8 + 0x00048> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Microsoft.AspNetCore.Blazor.Rendering.Renderer.AddToRenderQueue (System.Int32 componentId, Microsoft.AspNetCore.Blazor.RenderFragment renderFragment) <0x16923d0 + 0x00068> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Microsoft.AspNetCore.Blazor.Components.RenderHandle.Render (Microsoft.AspNetCore.Blazor.RenderFragment renderFragment) <0x16920b8 + 0x00036> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Microsoft.AspNetCore.Blazor.Routing.Router.Refresh () <0x17672a0 + 0x00130> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at Microsoft.AspNetCore.Blazor.Routing.Router.OnLocationChanged (System.Object sender, System.String newAbsoluteUri) <0x1974c90 + 0x0002c> in <6f55c2fa30114054ab77a67cb3b359ec>:0 
  at (wrapper delegate-invoke) System.EventHandler`1[System.String].invoke_void_object_TEventArgs(object,string)
  at (wrapper delegate-invoke) System.EventHandler`1[System.String].invoke_void_object_TEventArgs(object,string)
  at Microsoft.AspNetCore.Blazor.Browser.Services.BrowserUriHelper.NotifyLocationChanged (System.String newAbsoluteUri) <0x1974b00 + 0x00026> in <aa678c00565443a1a03daf9fa7da0de1>:0 

Further technical details

Build: 0.1
OS: Windows 10 Pro
Browser: Edge

Making the nullable int non-nullable wil fix this.
So this does work as expected:

<input @bind(TestInt) />

@functions
{
    public int TestInt { get; set; }

}
@Gyciauskas

This comment has been minimized.

Copy link

Gyciauskas commented Apr 2, 2018

Helpful info!

Currently i'm trying to make working selectable list or it call maybe drop-down list (I'm back-end developer). Anyway, can't make it working with drop-down list if drop-down list items is dynamic get data from server.

This is enum drop-down list working great:

            <div class="form-group">
                    <select class="form-control" @bind(_user.UserType)>
                        @foreach (var userType in Enum.GetValues(typeof(UserType)))
                        {
                            <option>@userType</option>
                        }
                    </select>
            </div>

This one is struggling:

            <div class="form-group">
                    <select class="form-control" @bind(_user.CompanyName)> // CompanyName - string
                        @foreach (var company in _companies) // _companies is List<Company> Company object contain Id and name
                        {
                            <option value=@company.name>@company.name</option>
                        }
                    </select>
                </div>
            </div>

Also possible that i'm just doing it wrong (I guess it 90% possibility)

@Suchiman

This comment has been minimized.

Copy link
Contributor

Suchiman commented Apr 3, 2018

This issue appears for any non string binding that is null, the code that causing this is

Append(RenderTreeFrame.Attribute(sequence, name, value.ToString()));

The exception occurs because calling .ToString() obviously NRE's on null, so the fix could be as easy as adding ?.
For string, null works as there's a different overload.

The interesting part is that AttributeValue declared object which makes ToString seem redundant

[FieldOffset(24)] public readonly object AttributeValue;

but JS assumes it to be always string
attributeValue: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 24),

This odd design appears to be for
else if (_lastNonAttributeFrameType == RenderTreeFrameType.Component)
{
Append(RenderTreeFrame.Attribute(sequence, name, value));
}

@floreseken

This comment has been minimized.

Copy link
Author

floreseken commented Apr 20, 2018

Tested this on 0.2 and that throws a decent understandable exception, so closing this.

@floreseken floreseken closed this Apr 20, 2018

@haasey

This comment has been minimized.

Copy link

haasey commented Nov 26, 2018

I feel like this is a hole. If someone uses entity framework to bind to a database, it will force them to create new entities taking care of nulls. Would love to see some configuration that allows treating nulls as "" for strings and nulls as 0 for ints. I don't want to get into a philosophical discussion on why they aren't equal. In my mind I realize that but the application has to deal with it somehow, and I would prefer to handle it globally not field by field.

If I have a nullable string in a database and I want to bind using blazor I don't like having to do some type of extension method to solve this over and over.
public static string NoNull(this string value)
{
string rc = "";
if (value != null)
{
rc = value;
}
return rc;
}

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