Проблема с EF Core 8, обрабатывающим бит MSSQL tinyint и nullable в Blazor

Ниже приведены выдержки из примера проекта Visual Studio 2022 Blazor, специально созданного для демонстрации проблемы.

Будем признательны за любые советы по исправлению ошибок, перечисленных внизу. Однако изменение типов данных MSSQL, к сожалению, невозможно. Заранее спасибо!

документ: BlazorApp1.proj

<Project Sdk = "Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include = "Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version = "8.0.3" />
    <PackageReference Include = "Microsoft.EntityFrameworkCore" Version = "8.0.3" />
    <PackageReference Include = "Microsoft.EntityFrameworkCore.Design" Version = "8.0.3">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include = "Microsoft.EntityFrameworkCore.SqlServer" Version = "8.0.3" />
    <PackageReference Include = "Microsoft.EntityFrameworkCore.Tools" Version = "8.0.3">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include = "Microsoft.VisualStudio.Web.CodeGeneration.Design" Version = "8.0.2" />
  </ItemGroup>
</Project>

документ: appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "connectionstring": "Server=(localdb)\\MSSQLLocalDB;Database=Test;Trusted_Connection=True"
  }
}

документ: Program.cs

using BlazorApp1.Components;

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // 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.UseAntiforgery();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();
app.Run();

база данных:

CREATE DATABASE Test
CREATE TABLE [dbo].[Sample] (
    [Id]              INT        NOT NULL,
    [NCharRequired]   NCHAR (10) NOT NULL,
    [NCharNullable]   NCHAR (10) NULL,
    [IntRequired]     INT        NOT NULL,
    [IntNullable]     INT        NULL,
    [TinyIntRequired] TINYINT    NOT NULL,
    [TinyIntNullable] TINYINT    NULL,
    [BitRequired]     BIT        NOT NULL,
    [BitNullable]     BIT        NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
);

окно: Консоль управления пакетами

Scaffold-DbContext "Server=(localdb)\MSSQLLocalDB;Database=Test;Trusted_Connection=True" Microsoft.EntityFrameworkCore.SqlServer

документ: Sample.cs

namespace BlazorApp1.Models;
public partial class Sample
{
    public int Id { get; set; }
    public string NcharRequired { get; set; } = null!;
    public string? NcharNullable { get; set; }
    public int IntRequired { get; set; }
    public int? IntNullable { get; set; }
    public byte TinyIntRequired { get; set; }
    public byte? TinyIntNullable { get; set; }
    public bool BitRequired { get; set; }
    public bool? BitNullable { get; set; }
}

документ: TestContext.cs

using Microsoft.EntityFrameworkCore;
namespace BlazorApp1.Models;

public partial class TestContext : DbContext
{
    public TestContext()
    {
    }
    public TestContext(DbContextOptions<TestContext> options)
        : base(options)
    {
    }
    public virtual DbSet<Sample> Samples { get; set; }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
        => optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=Test;Trusted_Connection=True");
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Sample>(entity =>
        {
            entity.HasKey(e => e.Id).HasName("PK__Table__3214EC07F913B460");
            entity.ToTable("Sample");
            entity.Property(e => e.Id).ValueGeneratedNever();
            entity.Property(e => e.NcharNullable)
                .HasMaxLength(10)
                .IsFixedLength()
                .HasColumnName("NCharNullable");
            entity.Property(e => e.NcharRequired)
                .HasMaxLength(10)
                .IsFixedLength()
                .HasColumnName("NCharRequired");
        });
        OnModelCreatingPartial(modelBuilder);
    }
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

контекстное меню: BlazorApp1

Add->
New Scaffolded Item->
Razor Components using Entity Framework (CRUD)->
Template:CRUD
Model class:Sample (BlazorApp1.Models)
DbContext class:TestContext (BlazorApp1.Models)

документ: Create.razor

@page "/samples/create"
@inject BlazorApp1.Models.TestContext DB
@using BlazorApp1.Models
@inject NavigationManager NavigationManager
<PageTitle>Create</PageTitle>
<h1>Create</h1>
<h4>Sample</h4>
<hr />
<div class = "row">
    <div class = "col-md-4">
        <EditForm method = "post" Model = "Sample" OnValidSubmit = "AddSample" FormName = "create" Enhance>
            <DataAnnotationsValidator />
            <ValidationSummary class = "text-danger" />
            <div class = "mb-3">
                <label for = "ncharrequired" class = "form-label">NcharRequired:</label>
                <InputText id = "ncharrequired" @bind-Value = "Sample.NcharRequired" class = "form-control" />
                <ValidationMessage For = "() => Sample.NcharRequired" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "ncharnullable" class = "form-label">NcharNullable:</label>
                <InputText id = "ncharnullable" @bind-Value = "Sample.NcharNullable" class = "form-control" />
                <ValidationMessage For = "() => Sample.NcharNullable" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "intrequired" class = "form-label">IntRequired:</label>
                <InputNumber id = "intrequired" @bind-Value = "Sample.IntRequired" class = "form-control" />
                <ValidationMessage For = "() => Sample.IntRequired" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "intnullable" class = "form-label">IntNullable:</label>
                <InputNumber id = "intnullable" @bind-Value = "Sample.IntNullable" class = "form-control" />
                <ValidationMessage For = "() => Sample.IntNullable" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "tinyintrequired" class = "form-label">TinyIntRequired:</label>
                <InputText id = "tinyintrequired" @bind-Value = "Sample.TinyIntRequired" class = "form-control" />
                <ValidationMessage For = "() => Sample.TinyIntRequired" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "tinyintnullable" class = "form-label">TinyIntNullable:</label>
                <InputText id = "tinyintnullable" @bind-Value = "Sample.TinyIntNullable" class = "form-control" />
                <ValidationMessage For = "() => Sample.TinyIntNullable" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "bitrequired" class = "form-label">BitRequired:</label>
                <InputCheckbox id = "bitrequired" @bind-Value = "Sample.BitRequired" class = "form-check-input" />
                <ValidationMessage For = "() => Sample.BitRequired" class = "text-danger" />
            </div>
            <div class = "mb-3">
                <label for = "bitnullable" class = "form-label">BitNullable:</label>
                <InputCheckbox id = "bitnullable" @bind-Value = "Sample.BitNullable" class = "form-check-input" />
                <ValidationMessage For = "() => Sample.BitNullable" class = "text-danger" />
            </div>
            <button type = "submit" class = "btn btn-primary">Create</button>
        </EditForm>
    </div>
</div>
<div>
    <a href = "/samples">Back to List</a>
</div>
@code {
    [SupplyParameterFromForm]
    public Sample Sample { get; set; } = new();
    // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
    public async Task AddSample()
    {
        DB.Samples.Add(Sample);
        await DB.SaveChangesAsync();
        NavigationManager.NavigateTo("/samples");
    }
}

документ: Edit.razor

@page "/Samples/edit"
@inject BlazorApp1.Models.TestContext DB
@using BlazorApp1.Models
@inject NavigationManager NavigationManager
@using Microsoft.EntityFrameworkCore
<PageTitle>Edit</PageTitle>
<h1>Edit</h1>
<h4>Sample</h4>
<hr />
@if (Sample is null)
{
    <p><em>Loading...</em></p>
}
else
{
    <div class = "row">
        <div class = "col-md-4">
            <EditForm method = "post" Model = "Sample" OnValidSubmit = "UpdateSample" FormName = "edit" Enhance>
                <DataAnnotationsValidator />
                <ValidationSummary />
                <input type = "hidden" name = "Sample.Id" value = "@Sample.Id" />
                <div class = "mb-3">
                    <label for = "ncharrequired" class = "form-label">NcharRequired:</label>
                    <InputText id = "ncharrequired" @bind-Value = "Sample.NcharRequired" class = "form-control" />
                    <ValidationMessage For = "() => Sample.NcharRequired" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "ncharnullable" class = "form-label">NcharNullable:</label>
                    <InputText id = "ncharnullable" @bind-Value = "Sample.NcharNullable" class = "form-control" />
                    <ValidationMessage For = "() => Sample.NcharNullable" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "intrequired" class = "form-label">IntRequired:</label>
                    <InputNumber id = "intrequired" @bind-Value = "Sample.IntRequired" class = "form-control" />
                    <ValidationMessage For = "() => Sample.IntRequired" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "intnullable" class = "form-label">IntNullable:</label>
                    <InputNumber id = "intnullable" @bind-Value = "Sample.IntNullable" class = "form-control" />
                    <ValidationMessage For = "() => Sample.IntNullable" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "tinyintrequired" class = "form-label">TinyIntRequired:</label>
                    <InputText id = "tinyintrequired" @bind-Value = "Sample.TinyIntRequired" class = "form-control" />
                    <ValidationMessage For = "() => Sample.TinyIntRequired" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "tinyintnullable" class = "form-label">TinyIntNullable:</label>
                    <InputText id = "tinyintnullable" @bind-Value = "Sample.TinyIntNullable" class = "form-control" />
                    <ValidationMessage For = "() => Sample.TinyIntNullable" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "bitrequired" class = "form-label">BitRequired:</label>
                    <InputCheckbox id = "bitrequired" @bind-Value = "Sample.BitRequired" class = "form-check-input" />
                    <ValidationMessage For = "() => Sample.BitRequired" class = "text-danger" />
                </div>
                <div class = "mb-3">
                    <label for = "bitnullable" class = "form-label">BitNullable:</label>
                    <InputCheckbox id = "bitnullable" @bind-Value = "Sample.BitNullable" class = "form-check-input" />
                    <ValidationMessage For = "() => Sample.BitNullable" class = "text-danger" />
                </div>
                <button type = "submit" class = "btn btn-primary">Save</button>
            </EditForm>
        </div>
    </div>
}
<div>
    <a href = "/samples">Back to List</a>
</div>
@code {
    [SupplyParameterFromQuery]
    public int Id { get; set; }
    [SupplyParameterFromForm]
    public Sample? Sample { get; set; }
    protected override async Task OnInitializedAsync()
    {
        Sample ??= await DB.Samples.FirstOrDefaultAsync(m => m.Id == Id);
        if (Sample is null)
        {
            NavigationManager.NavigateTo("notfound");
        }
    }
    // To protect from overposting attacks, enable the specific properties you want to bind to.
    // For more details, see https://aka.ms/RazorPagesCRUD.
    public async Task UpdateSample()
    {
        DB.Attach(Sample!).State = EntityState.Modified;
        try
        {
            await DB.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!SampleExists(Sample!.Id))
            {
                NavigationManager.NavigateTo("notfound");
            }
            else
            {
                throw;
            }
        }
        NavigationManager.NavigateTo("/samples");
    }
    bool SampleExists(int id)
    {
        return DB.Samples.Any(e => e.Id == id);
    }
}

окно: Список ошибок

Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Create.razor  14
Error (active)  CS1503  Argument 1: cannot convert from 'byte' to 'string'  BlazorApp1  .\Create.razor  39
Error (active)  CS1503  Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<byte>' to 'Microsoft.AspNetCore.Components.EventCallback'    BlazorApp1  .\Create.razor  39
Error (active)  CS0029  Cannot implicitly convert type 'byte' to 'string'   BlazorApp1  .\Create.razor  39
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Create.razor  39
Error (active)  CS1503  Argument 1: cannot convert from 'byte?' to 'string' BlazorApp1  .\Create.razor  44
Error (active)  CS1503  Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<byte?>' to 'Microsoft.AspNetCore.Components.EventCallback'   BlazorApp1  .\Create.razor  44
Error (active)  CS0029  Cannot implicitly convert type 'byte?' to 'string'  BlazorApp1  .\Create.razor  44
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Create.razor  44
Error (active)  CS1503  Argument 1: cannot convert from 'bool?' to 'bool'   BlazorApp1  .\Create.razor  54
Error (active)  CS1503  Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<bool?>' to 'Microsoft.AspNetCore.Components.EventCallback'   BlazorApp1  .\Create.razor  54
Error (active)  CS0266  Cannot implicitly convert type 'bool?' to 'bool'. An explicit conversion exists (are you missing a cast?)   BlazorApp1  .\Create.razor  54
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Create.razor  54
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Edit.razor    21
Error (active)  CS1503  Argument 1: cannot convert from 'byte' to 'string'  BlazorApp1  .\Edit.razor    47
Error (active)  CS1503  Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<byte>' to 'Microsoft.AspNetCore.Components.EventCallback'    BlazorApp1  .\Edit.razor    47
Error (active)  CS0029  Cannot implicitly convert type 'byte' to 'string'   BlazorApp1  .\Edit.razor    47
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Edit.razor    47
Error (active)  CS1503  Argument 1: cannot convert from 'byte?' to 'string' BlazorApp1  .\Edit.razor    52
Error (active)  CS1503  Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<byte?>' to 'Microsoft.AspNetCore.Components.EventCallback'   BlazorApp1  .\Edit.razor    52
Error (active)  CS0029  Cannot implicitly convert type 'byte?' to 'string'  BlazorApp1  .\Edit.razor    52
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Edit.razor    52
Error (active)  CS1503  Argument 1: cannot convert from 'bool?' to 'bool'   BlazorApp1  .\Edit.razor    62
Error (active)  CS1503  Argument 2: cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<bool?>' to 'Microsoft.AspNetCore.Components.EventCallback'   BlazorApp1  .\Edit.razor    62
Error (active)  CS0266  Cannot implicitly convert type 'bool?' to 'bool'. An explicit conversion exists (are you missing a cast?)   BlazorApp1  .\Edit.razor    62
Error (active)  CS1662  Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type BlazorApp1  .\Edit.razor    62

Попробовал:

  1. Активно искал в Интернете и просматривал множество связанных статей, но не нашел решения.

  2. Добавление их в OnModelCreating, но ошибки не исчезли.

entity.Property(e => e.TinyIntRequired).HasColumnType("tinyint").IsRequired(true);
entity.Property(e => e.TinyIntNullable).HasColumnType("tinyint").IsRequired(false);
entity.Property(e => e.BitNullable).HasColumnType("bit").IsRequired(false);
  1. Исследовательское изменение типов данных MSSQL с tinyint на int и бит null разрешено на запрещено. Ошибки устранены. Однако изменение типов данных, к сожалению, невозможно.

См. мой обновленный ответ, чтобы показать, как использовать отдельный объект редактирования.

MrC aka Shaun Curtis 24.03.2024 20:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
99
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, ваша проблема заключается в том, что вы пытаетесь использовать элементы управления вводом, которые не поддерживают используемые вами типы данных.

InputNumber не поддерживает byte, поэтому вам нужно создать версию, которая поддерживает.

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Rendering;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;

namespace SO78206175.Components;

public class BlazrInputNumber<TValue> : InputBase<TValue>
{
    private static readonly string _stepAttributeValue = GetStepAttributeValue();

    private static string GetStepAttributeValue()
    {
        // Unwrap Nullable<T>, because InputBase already deals with the Nullable aspect
        // of it for us. We will only get asked to parse the T for nonempty inputs.
        var targetType = Nullable.GetUnderlyingType(typeof(TValue)) ?? typeof(TValue);
        if (targetType == typeof(int) ||
            targetType == typeof(long) ||
            targetType == typeof(short) ||
            targetType == typeof(float) ||
            targetType == typeof(double) ||
            targetType == typeof(byte) ||
            targetType == typeof(decimal))
        {
            return "any";
        }
        else
        {
            throw new InvalidOperationException($"The type '{targetType}' is not a supported numeric type.");
        }
    }

    [Parameter] public string ParsingErrorMessage { get; set; } = "The {0} field must be a number.";

    [DisallowNull] public ElementReference? Element { get; protected set; }

    /// <inheritdoc />
    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        builder.OpenElement(0, "input");
        builder.AddAttribute(1, "step", _stepAttributeValue);
        builder.AddMultipleAttributes(2, AdditionalAttributes);
        builder.AddAttribute(3, "type", "number");
        if (NameAttributeValue is not null)
            builder.AddAttribute(4, "name", NameAttributeValue);

        if (CssClass is not null)
            builder.AddAttribute(5, "class", CssClass);

        builder.AddAttribute(6, "value", CurrentValueAsString);
        builder.AddAttribute(7, "onchange", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
        builder.SetUpdatesAttributeName("value");
        builder.AddElementReferenceCapture(8, __inputReference => Element = __inputReference);
        builder.CloseElement();
    }

    protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out TValue result, [NotNullWhen(false)] out string? validationErrorMessage)
    {
        if (typeof(TValue) == typeof(byte))
        {
            if (!byte.TryParse(value, out byte outcome))
            {
                result = default;
                validationErrorMessage = "The entry exeeds the maximum or minimum value for a byte.";
                return false;

            }
        }

        if (typeof(TValue) == typeof(byte?))
        {
            if (value is not null && !byte.TryParse(value, out byte outcome))
            {
                result = default;
                validationErrorMessage = "The entry exeeds the maximum or minimum value for a byte.";
                return false;
            }
        }

        if (BindConverter.TryConvertTo<TValue>(value, CultureInfo.InvariantCulture, out result))
        {
            validationErrorMessage = null;
            return true;
        }
        else
        {
            validationErrorMessage = string.Format(CultureInfo.InvariantCulture, ParsingErrorMessage, DisplayName ?? FieldIdentifier.FieldName);
            return false;
        }
    }

    protected override string? FormatValueAsString(TValue? value)
    {
        // Avoiding a cast to IFormattable to avoid boxing.
        switch (value)
        {
            case null:
                return null;

            case byte @byte:
                return @byte.ToString(CultureInfo.CurrentCulture);

            case int @int:
                return BindConverter.FormatValue(@int, CultureInfo.InvariantCulture);

            case long @long:
                return BindConverter.FormatValue(@long, CultureInfo.InvariantCulture);

            case short @short:
                return BindConverter.FormatValue(@short, CultureInfo.InvariantCulture);

            case float @float:
                return BindConverter.FormatValue(@float, CultureInfo.InvariantCulture);

            case double @double:
                return BindConverter.FormatValue(@double, CultureInfo.InvariantCulture);

            case decimal @decimal:
                return BindConverter.FormatValue(@decimal, CultureInfo.InvariantCulture);

            default:
                throw new InvalidOperationException($"Unsupported type {value.GetType()}");
        }
    }
}

Флажки не поддерживают bool?, поскольку они двоичные: a bool? имеет три состояния! Вам нужно создать специальный элемент управления. Вот пример элемента управления группой кнопок:

<div class = "form-control">

<div class = "btn-group" role = "group" aria-label = "Basic example">
    <button type = "button" class = "@_notSetColour" @onclick = "() => OnButtonChanged(-1)"> Not Set</button>
    <button type = "button" class = "@_onColour" @onclick = "() => OnButtonChanged(1)">On</button>
    <button type = "button" class = "@_offColour" @onclick = "() => OnButtonChanged(0)">Off</button>
</div>

</div>

@code {
    [Parameter] public bool? Value { get; set; }
    [Parameter] public EventCallback<bool?> ValueChanged { get; set; }

    private int _value => this.Value switch
    {
        true => 1,
        false => 0,
        _ => -1
    };

    private bool? GetBoolValue(int value) => value switch
    {
        1 => true,
        0 => false,
        _ => null
    };

    private string _notSetColour => _value == -1 ? "btn btn-secondary" : "btn btn-outline-secondary";
    private string _onColour => _value == 1 ? "btn btn-success" : "btn btn-outline-success";
    private string _offColour => _value == 0 ? "btn btn-danger" : "btn btn-outline-danger";

    private Task OnButtonChanged(int value)
    {
        this.ValueChanged.InvokeAsync(GetBoolValue(value));
        return Task.CompletedTask;
    }
}

Моя демо-страница:

@page "/"

<PageTitle>Home</PageTitle>

<h1>Example</h1>

<EditForm EditContext = "_editContext">
    <label>Byte Required</label>
    <BlazrInputNumber class = "form-control mb-2" max = "@byte.MaxValue" min = "@byte.MinValue" @bind-Value = "_model.TinyIntRequired" />
    <ValidationMessage For = "() => _model.TinyIntRequired" />
    <label>Byte Nullable</label>
    <BlazrInputNumber class = "form-control mb-2" max = "@byte.MaxValue" min = "@byte.MinValue" @bind-Value = "_model.TinyIntNullable" />
    <ValidationMessage For = "() => _model.TinyIntNullable" />

    <label>Byte Nullable</label>
    <InputBoolButtons @bind-Value = "_model.BitNullable" />
</EditForm>

<div class = "bg-dark text-white m-2 p-2">
    <pre>Byte Required: @_model.TinyIntRequired</pre>
    <pre>Byte Nullable: @(_model.TinyIntNullable?.ToString() ?? "Null")</pre>
    <pre>Bit Nullable: @(_model.BitNullable?.ToString() ?? "Null")</pre>
</div>
@code {
    private Sample _model = new();
    private EditContext? _editContext;

    protected override Task OnInitializedAsync()
    {
        _editContext = new(_model);
        return Task.CompletedTask;
    }
}

Дополнительная информация

Если вы хотите использовать стандартные элементы управления редактированием, вы можете создать объект редактирования, в который вы сопоставляете свой объект данных, а затем извлекаете его для сохранения.

Вот код, демонстрирующий, как:


public partial class Sample
{
    public int Id { get; set; }
    public byte? TinyIntNullable { get; set; }
}

// This is the object you write the validator for
// and you point the editor controls to
public class SampleEditMutator
{
    public int Id { get; private set; }
    public short? TinyIntNullable { get; set; }

    public SampleEditMutator(Sample record)
    {
        this.Id = record.Id;
        this.TinyIntNullable = Convert.ToInt16(record.TinyIntNullable);
    }

    // Call this to apply the edit changes to a Sample object
    public Sample Mutate(Sample record)
    {
        record.TinyIntNullable =  ConvertToByte(this.TinyIntNullable);
        return record;
    }

    private byte? ConvertToByte(short? value)
    {
        if (value is null)
            return null;

        if (byte.TryParse(this.TinyIntNullable.ToString(), out byte outvalue))
            throw new Exception("Not Allowed");

        return outvalue;
    }
}

Другие вопросы по теме