Blazor form problem

New to Radzen...

I've created an app which shows parent records in a grid. When a row is selected, the app moves to a details page. This page uses a fieldset with rows and columns to display a single record (the one selected in the grid on the previous page).

I have the fieldset laid out using labels and textboxes (also 2 text areas and 2 checkboxes). On load of the second page I get data from my database and save it to a local variable called accountDetails.

My problem starts when I try to bind the data in the local to the various components. I can enter the binding value just fine (and I have also made each item Read-only and where possible, also disabled to prevent editing). When I try to run, though, I get this error message on compile:

dotnet: obj\Debug\netcoreapp3.1\Razor\Pages\AccountDetails.razor.g.cs(136,381): error CS1977: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.

Obviously this is coming from the generated code, The only way to get past this is to remove the bindings from all of the controls - but then I am not displaying any information, which makes the point of having this form rather useless.

How can I bind this data (which is there - I verified by throwing a grid on the page and binding to the variable with success) to independent controls? I am not looking for any kind of change notification because the data should never change - just displayed.

Help!

Hi @dferreira042,

Can you paste a few screenshots of you page in Radzen? Show us the Load handler of the page and the properties of the form (from the property grid).

I don't have a form - there is no standard HTML form. What I have are 2 fieldsets with labels, textboxes, text areas and checkboxes (see attached screen shot). Also attached is the load handler - I am calling a custom method to set some local variables (the ${accountID} specifically) and then using that to call the stored procedure which gives me the row I am looking for.

When I use a datagrid instead, this works just fine with binding to the accountData variable. But since there is no direct way of allowing for overflow text by field, this just won't work (Highlights and Concerns are fields that can have up to 1024 characters).

OK, being smart this time...

Update - I added a Template Form control to the page and moved everything into that. I set the Data property of the Template to accountData and renamed all of the data-capable controls to the same name as fields in accountData. Still having the same problem.

Thank you, @dferreira042,

Could you please try adding a new Attribute with Name TItem and Value set to AccountDetail (I assume this is the type of your model which corresponds to a database table).

Thanks for the quick responses!

To be clear, the data is coming from a stored procedure which reflects joins between 4 tables. Because it is a stored procedure, there is no CRUD that is generated from it. Will that in any way affect how this data binding will work?

It may affect Radzen that it fails to infer the type required for the TItem property. You can check the generated code to see what the return type of the getAccountDetailByAccountId is and set it as the Value of the TItem attribute as I have shown in the screenshot. It will probably compile as expected after that.

The RadzenTemplateForm is a generic Blazor component and requires a type argument (called TItem). In most cases Radzen should infer that type argument but it seems this doesn't happen in your scenario.

Here is the generated code:

    public async Task<IQueryable<Models.AzureSalesforce.GetAccountDetailByAccountId>> GetGetAccountDetailByAccountIds(string AccountID)
      {
          OnGetAccountDetailByAccountIdsDefaultParams(ref AccountID);

          var items = context.GetAccountDetailByAccountIds.FromSqlRaw("EXEC [dbo].[GetAccountDetailByAccountID] @AccountID={0}", AccountID).ToList().AsQueryable();

          OnGetAccountDetailByAccountIdsInvoke(ref items);

          return await Task.FromResult(items);
      }

I set the TItem property on the TemplateForm to be GetAccountDetailByAccountId, and I added the binding info back to each control (${accountData.Type}, etc.). I also added the accountData object as the data source for the form.

Fails with the same error.

I removed the data binding info from each control but left the Data property for the form as accountData and the TItem attribute is still set. Now I get this error:

dotnet: fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
      Unhandled exception in circuit 'YrIm9jrqYBAk0h7-IlgW2BSu3aNLwWdAPUqDh7SAquk'.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The best overloaded method match for 'Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<AllStarSalesforce.Models.AzureSalesforce.GetAccountDetailByAccountId>(AllStarSalesforce.Models.AzureSalesforce.GetAccountDetailByAccountId)' has some invalid arguments
   at CallSite.Target(Closure , CallSite , Type , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at AllStarSalesforce.Pages.AccountDetails.<BuildRenderTree>b__0_0(RenderTreeBuilder __builder2)
   at Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder.AddContent(Int32 sequence, RenderFragment fragment)
   at Radzen.Blazor.RadzenContent.BuildRenderTree(RenderTreeBuilder __builder)
   at Microsoft.AspNetCore.Components.ComponentBase.<.ctor>b__6_0(RenderTreeBuilder builder)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

I am out of ideas here.

Your method returns a list of items - IQueryable<Models.AzureSalesforce.GetAccountDetailByAccountId> where the RadzenTemplateForm works with only one item. Try setting the accountData property to ${result.FirstOrDefault()}.

OK, I did that. I still can't use the Value property on the controls to bind to accountData (same "lambda" expression error).

I removed the binding assignment, and it compiled. When I go to the page, though, I get this new error:

dotnet: fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
      Unhandled exception in circuit 'yc_56xU7pZtK-if_0PfSjfJeZyWlAUMUxOl9ztSjse0'.
System.ArgumentNullException: Value cannot be null. (Parameter 'model')
   at Microsoft.AspNetCore.Components.Forms.EditContext..ctor(Object model)
   at Radzen.Blazor.RadzenTemplateForm`1.OnParametersSet()
   at Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()

dotnet: warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100]
      Unhandled exception rendering component: Cannot access a disposed object.
System.ObjectDisposedException: Cannot access a disposed object.
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.ThrowObjectDisposedException()
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.GrowBuffer(Int32 desiredCapacity)
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.Append(T[] source, Int32 startIndex, Int32 length)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

dotnet: fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
      Unhandled exception in circuit 'yc_56xU7pZtK-if_0PfSjfJeZyWlAUMUxOl9ztSjse0'.
System.ObjectDisposedException: Cannot access a disposed object.
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.ThrowObjectDisposedException()
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.GrowBuffer(Int32 desiredCapacity)
   at Microsoft.AspNetCore.Components.RenderTree.ArrayBuilder`1.Append(T[] source, Int32 startIndex, Int32 length)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
   at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange`1 oldTree, ArrayRange`1 newTree)
   at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

What next?

This exception: System.ArgumentNullException: Value cannot be null. (Parameter 'model') happens because accountData is initially null. Try setting the Visible property of the TemplateForm to ${accountData != null}.

Just tried that. Same problem.

This is strange. Can you show me again the Load event of the page and the Property grid of the TemplateForm?

Here is the Load event:

And here is the TemplateForm properties:

The custom code I am calling in its entirety:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Radzen;
using Radzen.Blazor;

namespace AllStarSalesforce.Pages
{
    public partial class AccountDetailsComponent
    {
        private void retrieveGlobalVariables()
        {
            this.accountID = Startup.AccountID;
        }
    }
}

I ran this in Visual Studio and set a breakpoint on the assignment to accountData. After the FirstOrDefault call it is a single GetAccountDetailByAccountId object populated with the correct data. So it looks like accountData is not null.

One other note: on the TemplateForm properties I am unable to set the Data to accountData now that I have used the FirstOrDefault method - it no longer shows up in the dropdown selections.

Getting this solved is crucial for the app I am working on - literally every other page in the app (except the entry point where you select the account to work with - that is working beautifully) will function the same - a stored procedure which takes the account ID and sends back some mix of data from tables extracted from Salesforce and hosted in an Azure SQL DB. So If I can't get this to work, none of the other pages will.

If accountData is indeed not null then this exception should not happen.

Anyway I am afraid I am out of ideas. If you have a Radzen Professional subscription you can send us the project to info@radzen.com and we will troubleshoot further.

I am a new subscriber (started at the end of December) - so I would send this to you except the database is HUGE and I don't want to lose time paring it down to something I can send. I'll do that as a last resort.

I am going to regenerate the data objects and then create a new page/form to try again. If that fails, I might try recreating the stored procedures as views and see if that works - although that is not a fantastic idea since the views will contain literally 100k rows and it seems horribly inefficient to filter on the server rather than in the DB engine.

More to come later as I experiment.

OK, some success. My accountData variable was not showing up in the dropdown on the Data property of the TemplateForm control and that was stopping me from going further. I hit the Data bind button and manually added the accountData variable and now the page loads.

I verified in Visual Studio that accountData has data.

My next issue is binding the individual fields in accountData to the various text boxes. I tried using the syntax ${accountData.Name} in the Value property, but that throws the lambda expression error that started all of this.

I looked at the samples in GitHub and tried to mimic that. I added a "ref" attribute with the field name, but that throws an error that the field name does not exist in the current context. Same with the "@ref" attribute. I have named all of the data controls to match the field name ("Highlights" is both the name of the textbox and the field in accountData). Nothing I have done so far, though, will put the data from accountData into the controls.

FYI, I have not added an Action or Method to the TemplateForm because the user is not going to do anything but look at the data.

Any suggestions?

Hi @dferreira042,

Unfortunately I don't have any other suggestions. We will have to reproduce the problem somehow in order to tell what the issue is.

Some additional information:

I compared what I have to the Radzen Northwind Blazor demo code. Looking at the EditOrder stuff in Northwind, in the designer CS file I see there is a definition for a RadzenTemplateForm with a type of Order plus all of the bindable fields appear as variables. The form variable appears as a reference in the .RAZOR file on the form definition element. So it seems that this is how the various fields are exposed to the form.

None of this appears in my code. So in Visual Studio I adjusted my code to look like the Northwind sample (with Radzen shut down) and ran it from there. Still no binding.

Is it possible that, because my source is a stored procedure, I can't data bind to individual form fields but can do so to a grid?