I am currently trying to learn how to display T-sql data through Blazor. I have created a local server that I have put some test data in as the primary key. Currently, I am trying to have it displayed in a format where a user can view, edit and/or delete from from the blazor page. However, currently the data does not display at all.
I have tried changing the Table values and calling each unit of the table separately and tried running it off of a network database with no noticeable changes to the output.
(https://i.sstatic.net/HSegN.png)
It is supposed to display the data from the below database, but nothing is shown.
CREATE TABLE [dbo].[TestTable] (
[Id] TEXT NOT NULL,
[Ralf1] TEXT NULL,
[Hilda2] TEXT NULL,
[Ed3] TEXT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
The actual table inputs:
(https://i.sstatic.net/NV4L9.png)
I am trying to have it populated with Text's due to potential user input, but I have had the same result with other values as well.
(https://i.sstatic.net/zwWnm.png)
My file layout isn't much different than the default, I have added an extra file called "Test" to help differentiate the most important "data" files.
appsettings:
All that has been added to this file is the connection string
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Testdb;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Program.cs:
Here, I reinforced the connection string and made sure it scoped to the proper file
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.EntityFrameworkCore;
using Test.Data;
using TestService.Data;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<TestTableService>();
builder.Services.AddDbContext<TestService.Data.Test.TestdbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
TestdbContext:
The tables is populated and I have tried recreating the table with just the primary key active.
// <auto-generated> This file has been auto generated </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace TestService.Data.Test;
public partial class TestdbContext : DbContext
{
public TestdbContext(DbContextOptions<TestdbContext> options)
: base(options)
{
}
public virtual DbSet<TestTable> TestTables { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TestTable>(entity =>
{
entity.HasKey(e => e.Id).HasName("PK__Table__3214EC07DEEEB24A");
entity.ToTable("TestTable");
entity.Property(e => e.Id).HasMaxLength(50);
entity.Property(e => e.Ed3).HasMaxLength(50);
entity.Property(e => e.Hilda2).HasMaxLength(50);
entity.Property(e => e.Ralf1).HasMaxLength(50);
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
TestTable:
A simple call for the data in the database table
#nullable disable
using System;
using System.Collections.Generic;
namespace TestService.Data.Test;
public partial class TestTable
{
public string Id { get; set; }
}
TestTableService:
using TestService.Data.Test;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Test.Data
{
public class TestTableService
{
private readonly TestdbContext _context;
public TestTableService(TestdbContext context)
{
_context = context;
}
public async Task<List<TestTable>>
GetIDAsync(string strCurrentUser)
{
return await _context.TestTables.AsNoTracking().ToListAsync();
}
}
}
FetchData:
all the calls and functions route here to finally be displayed
@page "/fetchdata"
@using Test.Data
@using TestService.Data.Test
@code
{
List<TestTable> Datas = new List<TestTable>();
}
@inherits OwningComponentBase<TestTableService>
<h1>Test</h1>
@if (Datas == null)
{
<p><em>Loading</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Id</th>
</tr>
</thead>
<tbody>
@foreach (var Data in Datas)
{
<tr>
<td>@Data.Id</td>
</tr>
}
</tbody>
</table>
}
I'd expect to see something ike this in your page too (but please read the final note below):
@inject TestTableService TestTableSvc
@code{
protected override async Task OnInitializedAsync()
{
Datas = await TestTableSvc.GetIDAsync("no idea what you want this arg for");
await InvokeAsync(StateHasChanged);
}
}
Incidentally, as it is, Datas
will never be null unless you set it so because you init the prop with an empty list, so your razor code for checking it to be null is redundant. Right now it would appear that your page is blank because the razor code takes the "not null" route and then renders all the items in the list (zero items)
I recommend breakpointing on the InvokeAsync line and checking that your DB call has returned data. It looks like you're using EFCore Power Tools so I'm not immedicately drawing a conclusion that anything is wrong on the data delivery side, but be mindful that you've put the connection string in your appsettings; it's usually wiser to use the "User Secrets" system in VS, to avoid accidentally committing conenction strings into a shared repo
Also, you indicated in your post that you desire to edit this data and send it back to the DB but you used AsNoTracking in your query, and that's going to make your life somewhat harder work, as EF won't track changes you make to those entities.
Finally, there are considerations for working with EF in Blazor Server that are slightly different to how you might have experienced it before, because the lifetime of objects that are DI'd can be potentially very (very) long (the length of time the user remains connected to the app) - far longer than an EF context is intended to live for.
It's usually better to inject a DbContextFactory that you use to manufacture contexts on demand, or new
up a context whenever you need one than inject one (directly or implicitly by injecting something that has a context injected), but this MSDN article has details: https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-ef-core?view=aspnetcore-8.0