Child Component Support in Blazor CSS isolation

The CSS isolation is only applied to the component level by default but you can extend it to the child component using “::deep” attribute in the CSS file. This attribute is the Blazor attribute so, it only understands and parses by the Blazor engine. When “::deep” attribute used in the CSS file, the Blazor engine also applied scope identifier to all descendants of components.

ChildComponent.razor

<hr />
<p>
    paragraph:: This is child component content
</p>
<div>
    div:: This is child component content
</div>
<hr />
@code {
}

ParentComponent.razor

@page "/parentcomponent"
<h3>Parent Component</h3>
<div>
    <p>paragraph:: This is Parent Component content</p>
    <ChildComponent></ChildComponent>
    <div>
        div:: This is parent component content
    </div>
</div>
@code {
}

ParentComponent.razor.css

p {
    color: red
}
::deep div {
    color:orange;
    font-weight:bold;
}

References
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/css-isolation?view=aspnetcore-6.0
https://www.c-sharpcorner.com/article/css-isolation-in-blazor/

Add/Load Components Dynamically in ASP.NET Blazor

@page "/somepage"

@dynamicComponent()

@functions{
  RenderFragment dynamicComponent() => builder =>
    {
        builder.OpenComponent(0, typeof(SurveyPrompt));
        builder.AddAttribute(1, "Title", "Some title");
        builder.CloseComponent();
    };
}

References
https://stackoverflow.com/questions/50188680/add-load-components-dynamically
https://github.com/dotnet/aspnetcore/issues/16104
https://docs.microsoft.com/en-us/aspnet/core/blazor/components/dynamiccomponent?view=aspnetcore-6.0

Get Current URL in a Blazor Component

protected override async Task OnInitializedAsync()
{
    NavigationManager.LocationChanged += NavigationManagerOnLocationChanged;
    // access to uri on first page load
    Logger.LogInformation(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
}

private void NavigationManagerOnLocationChanged(object? sender, LocationChangedEventArgs e)
{
    // access to uri on change
    Logger.LogInformation(NavigationManager.ToBaseRelativePath(NavigationManager.Uri));
}

References
https://stackoverflow.com/questions/50102726/get-current-url-in-a-blazor-component

Set up Automapper in ASP.NET Core

Add the main AutoMapper Package to your solution via NuGet.

Add the AutoMapper Dependency Injection Package to your solution via NuGet.

Create a new class for a mapping profile. (I made a class in the main solution directory called MappingProfile.cs and add the following code.) I’ll use a User and UserDto object as an example.

public class MappingProfile : Profile {
     public MappingProfile() {
         // Add as many of these lines as you need to map your objects
         CreateMap<User, UserDto>();
         CreateMap<UserDto, User>();
     }
 }

Then add the AutoMapperConfiguration in the Startup.cs as shown below:

public void ConfigureServices(IServiceCollection services) {
    // .... Ignore code before this

   // Auto Mapper Configurations
    var mapperConfig = new MapperConfiguration(mc =>
    {
        mc.AddProfile(new MappingProfile());
    });

    IMapper mapper = mapperConfig.CreateMapper();
    services.AddSingleton(mapper);

    services.AddMvc();

}

To invoke the mapped object in code, do something like the following:

public class UserController : Controller {

    // Create a field to store the mapper object
    private readonly IMapper _mapper;

    // Assign the object in the constructor for dependency injection
    public UserController(IMapper mapper) {
        _mapper = mapper;
    }

    public async Task<IActionResult> Edit(string id) {

        // Instantiate source object
        // (Get it from the database or whatever your code calls for)
        var user = await _context.Users
            .SingleOrDefaultAsync(u => u.Id == id);

        // Instantiate the mapped data transfer object
        // using the mapper you stored in the private field.
        // The type of the source object is the first type argument
        // and the type of the destination is the second.
        // Pass the source object you just instantiated above
        // as the argument to the _mapper.Map<>() method.
        var model = _mapper.Map<UserDto>(user);

        // .... Do whatever you want after that!
    }
}

References
https://stackoverflow.com/questions/40275195/how-to-set-up-automapper-in-asp-net-core

Change Access Modifier of .resx to Public in Dot NET

Create resource file.
Edit the .csproj file manually.
Search for the embedded resource tag that includes the *.resx file.
Change Generator child tag content to ‘PublicResXFileCodeGenerator’.
Apply “Move to resource”.

<ItemGroup>
    <EmbeddedResource Update="Resources/Resources.resx">
        <Generator>PublicResXFileCodeGenerator</Generator>
    </EmbeddedResource>
</ItemGroup>

References
https://youtrack.jetbrains.com/issue/RIDER-33587/Allow-to-change-access-modifier-of-resx-to-public

Get an element by ID or Class in Blazor

Blazor doesn’t manipulate the DOM directly at C# side. You cancall the JavaScript method by using JavaScript Interop to get an element by ID or class.

  • The getElementsByClassName() method returns a collection of all elements in the document with the specified class names.
  • The getElementById() method returns a collection of all elements in the document with the specified ID names.
@page "/"
@inject IJSRuntime JsRuntime

<h1 id="headingElement">Hello, world!</h1>

<p class="para-element">Welcome to your new app.</p>

@code {
    protected override async void OnAfterRender(bool firstRender)
    {
        await JsRuntime.InvokeVoidAsync("elementId");
    }
}
<body>
      . . .
      . . .

      <script>
        function elementId() {
            // Get element with the specified ID name
            var idValue = document.getElementById("headingElement");
            console.log(idValue.innerHTML);
            // Get element with the specified Class name
            var classValue = document.getElementsByClassName("para-element");
            console.log(classValue[0].innerHTML);
        }
    </script>
</body>

References
https://www.syncfusion.com/faq/blazor/javascript-interop/how-do-i-get-an-element-by-id-or-class-in-blazor

ASP.NET Core Blazor Server with Entity Framework Core

appsettings.Development.json

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
    "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  }
}

Database access

using var context = new MyContext();

return await context.MyEntities.ToListAsync();
if (Loading)
{
    return;
}

try
{
    Loading = true;

    ...
}
finally
{
    Loading = false;
}

New DbContext instances

builder.Services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
private async Task DeleteContactAsync()
{
    using var context = DbFactory.CreateDbContext();
    Filters.Loading = true;

    if (Wrapper is not null && context.Contacts is not null)
    {
        var contact = await context.Contacts
            .FirstAsync(c => c.Id == Wrapper.DeleteRequestId);

        if (contact is not null)
        {
            context.Contacts?.Remove(contact);
            await context.SaveChangesAsync();
        }
    }

    Filters.Loading = false;

    await ReloadAsync();
}

Scope to the component lifetime

@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory
public void Dispose()
{
    Context?.Dispose();
}
protected override async Task OnInitializedAsync()
{
    Busy = true;

    try
    {
        Context = DbFactory.CreateDbContext();

        if (Context is not null && Context.Contacts is not null)
        {
            var contact = await Context.Contacts.SingleOrDefaultAsync(c => c.Id == ContactId);

            if (contact is not null)
            {
                Contact = contact;
            }
        }
    }
    finally
    {
        Busy = false;
    }

    await base.OnInitializedAsync();
}

References
https://docs.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-6.0

Entity Framework Core Database-First using Npgsql

dotnet ef dbcontext scaffold "Host=my_host;Database=my_db;Username=my_user;Password=my_pw" Npgsql.EntityFrameworkCore.PostgreSQL

If you have other tables in your database, you may use additional parameters – -Schemas and -Tables – to filter the list of schemas and/or tables that are added to the model. For example, you can use the following command:

dotnet ef dbcontext scaffold "host=server;database=test;user id=postgres;" Devart.Data.PostgreSql.Entity.EFCore -Tables dept,emp

References
https://www.npgsql.org/efcore/index.html
https://www.devart.com/dotconnect/postgresql/docs/EFCore-Database-First-NET-Core.html