Search code examples
c#componentsblazor

Why does 'InvokeAsync' show as an error in a Blazor component?


I've recently started including Blazor components in my ASP.Net core web app. I've decided to split my components into multiple files using the pattern [ComponentName].razor and [ComponentName].razor.cs for all the C# coding.

Something annoying which has been popping up is all my state change notification calls are showing as errors. Now, these errors disappear when I build and run the app, but they still congest my error list window. These will disappear if I close and then open Visual Studio 2019, but they come back after a couple of minutes. What is going on and how can I fix these errors?

Cheers!

InvokeAsync highlighted as an error

Edit: Here's some code as requested. The offending lines are in TransactionTableView.razor.cs. 'await InvokeAsync(StateHasChanged)' shows up as an error.

TransactionTableView.razor

<table class="table data-table" id="taskstable">
    <thead>
        <tr>
            @if (ShowSelect)
            {
                <th>
                    <input type="checkbox" @bind="SelectAllTransactions" @bind:event="oninput" @onclick="() => CheckAllTransactions(!SelectAllTransactions)"/>
                    Select all
                </th>
            }
            @if (ShowParent)
            {
                <th>
                    Parent
                </th>
            }
            <th>
                Task
            </th>
            <th>
                Status
            </th>
            <th>
                Due Date
            </th>
            <th>
                Completion Date
            </th>
            <th>
                Amount Due
            </th>
            <th>
                Amount Paid
            </th>
            <th>
                Comments
            </th>
        </tr>
    </thead>
    <tbody class="tasks-body">
        @if (Transactions != null)
        {
            @foreach (var transaction in DisplayTransactions)
            {
                <tr class="@transaction.AlertClassDisplay()">
                    @if (ShowSelect)
                    {
                        <td>
                            <input @bind="transaction.Select" type="checkbox" @oninput="() => SelectSingleTask(transaction.TransactionId)"/>
                        </td>
                    }
                    @if (ShowParent)
                    {
                        <td>
                            @transaction.TenementKey
                        </td>
                    }
                    <td>
                        @transaction.Description
                    </td>
                    <td class="task-status">
                        <a asp-action="Details" asp-controller="Alerts" method="get" asp-route-transactionId="@transaction.TransactionId">
                            @transaction.TransactionStatus
                        </a>

                    </td>
                    <td>
                        @transaction.DueDateDisplay()

                    </td>
                    <td>
                        @($"{transaction.CompletionDate:dd MMM yyyy}")
                    </td>
                    <td>
                        @transaction.AmountDue
                    </td>
                    <td>
                        @transaction.AmountPaid
                    </td>
                    <td>
                        @transaction.Comments
                    </td>
                </tr>
            }
        }
    </tbody>
</table>

TransactionTableView.razor.cs

partial class TransactionTableView
    {
        // Injected services
        [Inject]
        private ApplicationDbContext _context { get; set; }

        // Data
        public List<int> TenementIds { get; set; }
        public List<TransactionViewModel> Transactions { get; set; }
        public List<TransactionViewModel> DisplayTransactions { get; set; }
        public List<Filter> Filters { get; set; } = new List<Filter>();

        private bool ShowParent { get; set; } = true;
        private bool ShowSelect { get; set; } = true;
        private bool SelectAllTransactions { get; set; } = false;

        public async Task Refresh()
        {
            var transactions = new List<Transaction>(_context.Transactions.Where(x => x.TenementId != null && TenementIds.Contains((int)x.TenementId)));

            transactions = transactions.OrderBy(x => x.DueDate).ToList();

            var models = new List<TransactionViewModel>();
            transactions.ForEach(x =>
            {
                models.Add(x.GetViewModel(_context));
            });
            Transactions = models;
            await RefreshFilters();
        }

        public async Task RefreshFilters()
        {
            var holder = Transactions;

            if (Filters != null)
            {
                foreach (var filter in Filters)
                {
                    if(filter.FilteringProperty.PropertyType == typeof(string))
                    {
                        holder = holder.Where(x => ((string)filter.FilteringProperty.GetValue(x)).ToLower().Contains(filter.Values.First().ToString().ToLower())).ToList();
                    }

                    if(filter.FilteringProperty.PropertyType == typeof(DateTime) || filter.FilteringProperty.PropertyType == (typeof(DateTime?)))
                    {
                        var dFilter = (DateFilter)filter;

                        if (dFilter.SelectedAfterDate)
                        {
                            holder = holder.Where(x => (DateTime)dFilter.FilteringProperty.GetValue(x) > dFilter.TargetDate).ToList();
                        }
                        else if (dFilter.SelectBeforeDate)
                        {
                            holder = holder.Where(x => (DateTime)dFilter.FilteringProperty.GetValue(x) < dFilter.TargetDate).ToList();
                        }
                        else if (dFilter.SelectBetweenDates)
                        {
                            holder = holder.Where(x => 
                            {
                                var targetDate = (DateTime)dFilter.FilteringProperty.GetValue(x);

                                return targetDate < dFilter.TargetDate && targetDate > (DateTime)dFilter.FirstTargetDate;
                            }).ToList();
                        }
                    }

                    if(filter.FilteringProperty.PropertyType == typeof(TransactionStatus))
                    {
                        var targetStatuses = filter.Values.Select(x => (TransactionStatus)x);

                        holder = holder.Where(x => targetStatuses.Contains(x.TransactionStatus)).ToList();
                    }
                }
            }

            DisplayTransactions = holder;
            await RefreshAllTaskSelector();
            await InvokeAsync(StateHasChanged);
        }

        private async Task CheckAllTransactions(bool checkAll)
        {
            foreach(var displayTask in DisplayTransactions)
            {
                displayTask.Select = checkAll;
            }

            await InvokeAsync(StateHasChanged);
        }

        private async Task SelectSingleTask(int transactionId)
        {
            var task = DisplayTransactions.SingleOrDefault(x => x.TransactionId == transactionId);
            task.Select = !task.Select;

            if (DisplayTransactions.Any() && DisplayTransactions.Where(x => !x.Select).Count() == 0)
            {
                SelectAllTransactions = true;
            }
            else
            {
                SelectAllTransactions = false;
            }

            await InvokeAsync(StateHasChanged);
        }

        private async Task RefreshAllTaskSelector()
        {
            if (DisplayTransactions.Any() && DisplayTransactions.Where(x => !x.Select).Count() == 0)
            {
                SelectAllTransactions = true;
            }
            else
            {
                SelectAllTransactions = false;
            }
        }

        private async Task DeleteSelected()
        {
            if (_context.ChangeTracker.HasChanges())
            {
                throw new ApplicationException("DB already has changes");
            }

            var targets = new List<TransactionViewModel>(DisplayTransactions.Where(x => x.Select));

            foreach(var target in targets)
            {
                Transactions.Remove(target);
                DisplayTransactions.Remove(target);
                var transaction = _context.Transactions.SingleOrDefault(x => x.TransactionId == target.TransactionId);
                _context.Transactions.Remove(transaction);
            }

            await _context.SaveChangesAsync();
            await InvokeAsync(StateHasChanged);
        }

        private async Task CompleteSelected()
        {
            if (_context.ChangeTracker.HasChanges())
            {
                throw new ApplicationException("DB already has changes");
            }

            var targets = new List<TransactionViewModel>(DisplayTransactions.Where(x => x.Select));

            foreach (var target in targets)
            {
                var dbTask = _context.Transactions.SingleOrDefault(x => x.TransactionId == target.TransactionId);
                dbTask.CompletionDate = DateTime.Now;
                dbTask.TransactionStatus = TransactionStatus.Completed;

                target.CompletionDate = dbTask.CompletionDate;
                target.TransactionStatus = dbTask.TransactionStatus;

                var orig = Transactions.SingleOrDefault(x => x.TransactionId == target.TransactionId);
                orig.CompletionDate = dbTask.CompletionDate;
                orig.TransactionStatus = dbTask.TransactionStatus;
            }

            await _context.SaveChangesAsync();
            await RefreshFilters();
            await InvokeAsync(StateHasChanged);
        }

        private async Task ArchiveSelected()
        {
            if (_context.ChangeTracker.HasChanges())
            {
                throw new ApplicationException("DB already has changes");
            }

            var targets = new List<TransactionViewModel>(DisplayTransactions.Where(x => x.Select));

            foreach (var target in targets)
            {
                var dbTask = _context.Transactions.SingleOrDefault(x => x.TransactionId == target.TransactionId);
                dbTask.CompletionDate = DateTime.Now;
                dbTask.TransactionStatus = TransactionStatus.Archived;

                target.CompletionDate = dbTask.CompletionDate;
                target.TransactionStatus = dbTask.TransactionStatus;

                var orig = Transactions.SingleOrDefault(x => x.TransactionId == target.TransactionId);
                orig.CompletionDate = dbTask.CompletionDate;
                orig.TransactionStatus = dbTask.TransactionStatus;
            }

            await _context.SaveChangesAsync();
            await RefreshFilters();
            await InvokeAsync(StateHasChanged);
        }
    }

Solution

  • I've tried with a little test project and I can confirm that you can have 2 possible issue:

    • you have created/moved the code behind file TransactionTableView.razor.cs in another folder than the TransactionTableView.razor (as said by Henk) without specify the @inherits path
    • you haven't derived your class from ComponentBase

    If you want to separate the two files in two different folder you need to add inherits, like here:

    EmployeeData.razor in Pages folder

    @page "/employee/{Id}"
    @using TestMenu.Shared
    @using TestMenu.Client.Services
    @inherits TestMenu.Client.Pages.test.EmployeeData
    
    <h3>Employee</h3>
    
    Result data: <b>@employeeData</b>
    
    

    EmployeeData.razor.cs in Pages/test folder

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Components;
    using TestMenu.Client.Services;
    using TestMenu.Shared;
    
    namespace TestMenu.Client.Pages.test
    {
        public partial class EmployeeData: ComponentBase
        {
            [Inject] IEmployeeService EmployeeService { get; set; }
    
            [Parameter]
            public string Id { get; set; }
    
            protected Employee Employee = new();
            protected string employeeData;
    
            protected override async Task OnInitializedAsync()
            {
                Employee = (await EmployeeService.GetEmployeeById(int.Parse(Id)));
                employeeData = Employee.Name;
                await InvokeAsync(StateHasChanged);
            }
        }
    }
    

    if you remove the : ComponentBase from the code behind of EmployeeData.razor.cs, you receive an error like the one in your screenshot.