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

Quirkiness of Blazor Server Apps deployed on Hosted IIS VM on Azure #58956

Open
1 task done
MarianoJP opened this issue Nov 14, 2024 · 2 comments
Open
1 task done

Quirkiness of Blazor Server Apps deployed on Hosted IIS VM on Azure #58956

MarianoJP opened this issue Nov 14, 2024 · 2 comments
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. question Status: Resolved

Comments

@MarianoJP
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I am posting this to help me understand what is happening behind the scene with my code in a Blazor Server Application. As mentioned, my Blazor Server App (the App here in after), is, at least to me, a complex mixture of .razor, C# (Classes, Function with HTML, Model, etc.), JavaScript (Cannot forgo this if you are on the web, sadly), and persistence.

In the past as developers we learn that Development Environments may not always translate well to Production Environments. This is the primordial aspect for this post that I wish to learn and understand more. To start out simply, the necessary configuration on IIS for a Blazor application to run, are a maze that even though you may have done it several times, you get lost in the next configuration. Albeit, assuring you crossed all your t's and dotted all your i's, for some reason the Production Environment of an IIS running on Azure VM brings forth my issue of the Quirkiness of how a Blazor Environment behaves running on it.

For starters, the App I developed requires access to a database (SQL Express for this purpose), and in the application.json file the connection string with all the proper credentials, security, encryption, etc. to have the App access the Database. In the App, I am using EF Core Identity for User Management (Why re-invent the wheel), since I require local accounts as well as interaction with OAUTH MS or AWS or Google accounts. The interaction is managed separately since I cannot use the MS Graph library should I access OAUTH for AWS and/or Google.

The core of the problem is the EF Core Identity. Having the following code (excerpt) in a Service in the App:

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace MyNamespace
{
     public class UserService
     {
         private readonly UserManager<ApplicationUser> _userManager;
         private readonly ILogger<UserService> _logger;
         private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); // Allow only one operation at a time

         public List<ApplicationUser> Users { get; private set; } = new List<ApplicationUser>();
         public ApplicationUser User { get; private set; }

         public UserService(UserManager<ApplicationUser> userManager, ILogger<UserService> logger)
         {
              _userManager = userManager;
              _logger = logger;
         }
    public async Task LoadUsersAsync()
    {
        await _semaphore.WaitAsync();  // Asynchronously wait to enter
        try
        {
            if (Users == null || !Users.Any())
            {
                Users = _userManager.Users.AsNoTracking().ToList();  // This prevents second operation error
                _logger.LogInformation("Users loaded successfully.");
            }
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Error loading users.");
        }
        finally
        {
            _semaphore.Release();  // Release the semaphore
        }
    }

    public async Task RefreshUsersAsync()
    {
        await _semaphore.WaitAsync();
        try
        {
            Users = _userManager.Users.AsNoTracking().ToList();  // This prevents second operation error
        }
        finally
        {
            _semaphore.Release();
        }
    }
    public async Task SetMe(ApplicationUser Me)
    {
        await _semaphore.WaitAsync();
        try
        {
            User = _userManager.Users.AsNoTracking().FirstOrDefault(c => c.Id == Me.Id);
        }
        finally
        {
            _semaphore.Release();
        }

    }
    public async Task<IdentityResult> UpdateUser(ApplicationUser Me)
    {
        await SetMe(Me);
        await _semaphore.WaitAsync();
        try
        {
            ApplicationUser freshUser = await _userManager.FindByIdAsync(Me.Id);
            XMPUtilityClass XU = new XMPUtilityClass();
            XU.CopyProperties(Me, freshUser);
            var ret = await _userManager.UpdateAsync(freshUser);
            return ret;
        }
        finally
        {

            _semaphore.Release();
        }
    }
     }
}

This service is to be used in Pages and Components alike in the App to implement as best as my beginner's programming skills allow, to circumvent the problem of the EF Core Identity no having a UserManage.GetUsersAsync(). In the App in the StartUp.CS file the service is registered as a Scoped service services.AddScoped<UserService>();. In the pages and the components the service is used as:

@inject UserService US;

<HTML CODE>

@code
{
    protected override async Task OnInitializedAsync()
    {
        await US.LoadUsersAsync();
        Users = US.Users;
    }
    ... other code and functions ....
}

And although most of the times I have no problem loading the Users Async, the issue that sets the difference in the Production vs. Development environment is if the service is used as follows using LINQ:

private void ChangeFilter(ChangeEventArgs e)
{
     if(e.Value.ToString().Length >0)
     {
          FilteredUsers = Users.Where(c => c.FirstName.Contains(e.Value.ToString(), StringComparison.OrdinalIgnoreCase) || c.FirstLastName.Contains(e.Value.ToString(), StringComparison.OrdinalIgnoreCase)).ToList();
     }
}

works perfectly in the Development Environment, but when the App is deployed into the IIS, it returns the error:

"Category: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHostEventId: 111SpanId: 85fad198feb49aa3TraceId: 0c3026fa36b065b1c198ca23f4acc066ParentId: 0000000000000000RequestId: 4000075e-0001-ee00-b63f-84710c7967bbRequestPath: /_blazorTransportConnectionId: 27FNbZLL8yv1rXpg-Jc6HwUnhandled exception in circuit '74gA2uaQXxptSrbqcU4PJWZB4hHLPkVCB68ZjdwXV1E'.Exception: System.NullReferenceException: Object reference not set to an instance of an object. at......"

The issue is that the error does not happen in the Development with the same exact code. All compiles, runs and performs as it should. This simple LINQ statement had to be changed to the following loop in order for the code to work on both environments:

FilteredUsers = new List<ApplicationUser>();
string filterValue = e.Value.ToString();

foreach (var user in Users)
{
    if ((user.FirstName != null && user.FirstName.Contains(filterValue, StringComparison.OrdinalIgnoreCase)) ||
        (user.FirstLastName != null && user.FirstLastName.Contains(filterValue, StringComparison.OrdinalIgnoreCase)))
    {
        FilteredUsers.Add(user);
    }
}

Could someone help me identify my mistake or why the issue that the LINQ code works on a one computer using Visual Studio 2022 with all patches and .NET 8.0, and on the server IIS VM configured with all the necessary configuration for a Blazor Server Application on IIS plus the runtime .NET 8.0 does not work? Any insights will save me many hours of development and debugging.

Expected Behavior

No response

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

.Net 8.0

Anything else?

I am using EF Core Identity.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Nov 14, 2024
@javiercn
Copy link
Member

@MarianoJP thanks for contacting us.

This doesn't seem to be a Blazor specific issue (nor an IIS issue for that matter) but an issue with how the query gets executed in different environments.

Without being a database expert, it can be a different on the database configuration.

@javiercn
Copy link
Member

While we do our best to look through all the issues filed here, to get a faster response we suggest posting your questions to StackOverflow

@javiercn javiercn added question ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. labels Nov 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. question Status: Resolved
Projects
None yet
Development

No branches or pull requests

2 participants