Является ли этот код Blazor/EF Core эффективным и безопасным использованием DbContext и доступом к теневым свойствам?

Простое веб-приложение Blazor, созданное в VS2022 с использованием шаблона приложения wb, для редактирования записи поставщика. Все работает, и я могу редактировать запись в одновременных вкладках браузера.

Я видел примеры использования DbContext DI на SO, но они не предлагают доступ Intellisense к теневым свойствам в классе DbContext моего приложения.

Итак, я использовал подход (см. код), при котором я внедряю контекст базы данных своего приложения.

В частности, мне было интересно, вызовет ли внедрение моего класса LicensingContext для получения доступа Intellisense к теневым свойствам DbSets неприятные проблемы или это будет неэффективно? и почему.

Кажется, что контекст создан и расположен соответствующим образом.

LicensingContext (мой класс App DbContext)

// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
#nullable disable
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace Licensing.Models;


public partial class LicensingContext : Microsoft.EntityFrameworkCore.DbContext
{


    DateTime dt = DateTime.Now;

    public LicensingContext(DbContextOptions<LicensingContext> options)
        : base(options)
    {
        Console.WriteLine(string.Format("+++ Licensing context CREATED {0}", dt));
    }
    
    ~LicensingContext()
    {
        Console.WriteLine(string.Format("--- Licensing context DESTROYED {0}", dt));
    }


    public virtual DbSet<Allocations> Allocations { get; set; }

    public virtual DbSet<Dates> Dates { get; set; }

    public virtual DbSet<Departments> Departments { get; set; }

    public virtual DbSet<VendorBundles> VendorBundles { get; set; }

    public virtual DbSet<VendorLicenceModules> VendorLicenceModules { get; set; }

    public virtual DbSet<Vendors> Vendors { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Allocations>(entity =>
        {
            entity.HasKey(e => e.pkId).HasName("PK_fctAllocation_1");

            entity.HasOne(d => d.Department).WithMany(p => p.Allocations).HasConstraintName("FK_fctAllocation_dimDepartment");

            entity.HasOne(d => d.VendorBundle).WithMany(p => p.Allocations)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_fctAllocation_dimVendorBundle");

            entity.HasOne(d => d.Vendor).WithMany(p => p.Allocations).HasConstraintName("FK_fctAllocation_dimVendor");

            entity.HasOne(d => d.VendorLicenceModule).WithMany(p => p.Allocations)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_fctAllocation_dimVendorLicenceModule");
        });

        modelBuilder.Entity<Dates>(entity =>
        {
            entity.HasKey(e => e.pkId).HasName("PK_dimDates");

            entity.Property(e => e.MMYYYY).IsFixedLength();
            entity.Property(e => e.Style101).IsFixedLength();
            entity.Property(e => e.Style103).IsFixedLength();
            entity.Property(e => e.Style112).IsFixedLength();
            entity.Property(e => e.Style120).IsFixedLength();
            entity.Property(e => e.TheDaySuffix).IsFixedLength();
        });

        modelBuilder.Entity<Departments>(entity =>
        {
            entity.HasKey(e => e.pkId).HasName("PK_dimDepartment");

            entity.Property(e => e.Notes).IsFixedLength();
        });

        modelBuilder.Entity<VendorBundles>(entity =>
        {
            entity.HasKey(e => e.pkId).HasName("PK_dimVendorBundle");

            entity.HasOne(d => d.VendorFkNavigation).WithMany(p => p.VendorBundles).HasConstraintName("FK_dimVendorBundle_dimVendor");
        });

        modelBuilder.Entity<VendorLicenceModules>(entity =>
        {
            entity.HasKey(e => e.pkId).HasName("PK_dimLicenceModules");

            entity.HasOne(d => d.Bundle).WithMany(p => p.VendorLicenceModules).HasConstraintName("FK_dimVendorLicenceModule_dimVendorBundle");
        });

        modelBuilder.Entity<Vendors>(entity =>
        {
            entity.HasKey(e => e.pkId).HasName("PK_dimVendor_1");
        });

        OnModelCreatingPartial(modelBuilder);
    }

    partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}

VendorDetails (родительский класс)

@* @page "/Vendors/createedit" *@

@* using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.JSInterop;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Web; *@
@* using Microsoft.AspNetCore.Identity; *@
@using Microsoft.EntityFrameworkCore
@using Licensing.Models

@* @using Microsoft.AspNetCore.Identity; *@
@* @using Microsoft.AspNetCore.Authorization *@
@* @attribute [Authorize] *@


@* @inject IDbContextFactory<Licensing.Models.LicensingContext> DbFactory *@

@inject NavigationManager NavigationManager

<RadzenContent Container = "main">
    <ChildContent>
        <div class = "row">
            <div class = "col-md-12">
                <RadzenTemplateForm Data = "@Vendor" Visible = "@(Vendor != null)" TItem = "Licensing.Models.Vendors" Submit = "@FormSubmit">
                    <ChildContent>

                        <div style = "margin-bottom: 1rem" class = "row">
                            <div class = "col-md-3">
                                <RadzenLabel Text = "Name" Component = "Name" style = "width: 100%" />
                            </div>
                            <div class = "col-md-9">
                                <RadzenTextBox MaxLength = "256" style = "display:block; width: 100%" @bind-Value = "@(Vendor.Name)" Name = "Name" />
                                <RadzenRequiredValidator Component = "Name" Text = "Name is required" style = "position: absolute" />
                            </div>
                        </div>

                        <div style = "margin-bottom: 1rem" class = "row">
                            <div class = "col-md-3">
                                <RadzenLabel Text = "Email" Component = "Email" style = "width: 100%" />
                            </div>
                            <div class = "col-md-9">
                                <RadzenTextBox MaxLength = "256" style = "display: block; width: 100%" @bind-Value = "@(Vendor.Email)" Name = "Email" />
                                <RadzenRequiredValidator Component = "Email" Text = "Email is required" style = "position: absolute" />
                            </div>
                        </div>

                        <div style = "margin-bottom: 1rem" class = "row">
                            <div class = "col-md-3">
                                <RadzenLabel Text = "Last Name" Component = "LastName" style = "width: 100%">
                                </RadzenLabel>
                            </div>
                            <div class = "col-md-9">
                                <RadzenTextBox>
                                    @* MaxLength = "256" style = "width: 100%" @bind-Value = "@(Vendor.LastName)" Name = "LastName"> *@
                                </RadzenTextBox>
                            </div>
                        </div>

                        <div style = "margin-bottom: 1rem" class = "row">
                            <div class = "col-md-3">
                                <RadzenLabel Text = "First Name" Component = "FirstName" style = "width: 100%">
                                </RadzenLabel>
                            </div>
                            <div class = "col-md-9">
                                <RadzenTextBox>
                                    @* MaxLength = "256" style = "width: 100%" @bind-Value = "@(Vendor.FirstName)" Name = "FirstName"> *@
                                </RadzenTextBox>
                            </div>
                        </div>

                        <div style = "margin-bottom: 1rem" class = "row">
                            <div class = "col-md-3">
                                <RadzenLabel Text = "Phone" Component = "Phone" style = "width: 100%">
                                </RadzenLabel>
                            </div>
                            <div class = "col-md-9">
                                <RadzenTextBox>
                                    @* MaxLength = "256" style = "width: 100%" @bind-Value = "@(Vendor.Phone)" Name = "Phone"> *@
                                </RadzenTextBox>
                            </div>
                        </div>

                        <div class = "row">
                            <div class = "col offset-sm-3">
                                <RadzenButton ButtonType = "ButtonType.Submit" Icon = "save" Text = "Save" ButtonStyle = "ButtonStyle.Primary">
                                </RadzenButton>
                                <RadzenButton ButtonStyle = "ButtonStyle.Light" style = "margin-left: 1rem" Text = "Cancel" Click = "@ButtonCancelClick">
                                </RadzenButton>
                            </div>
                        </div>
                    </ChildContent>
                </RadzenTemplateForm>
            </div>
        </div>
    </ChildContent>
</RadzenContent>

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IReadOnlyDictionary<string, dynamic>? Attributes { get; set; }

    [Inject]
    protected NotificationService NotificationService { get; set; } = default!;

    [Inject]
    protected DialogService DialogService { get; set; } = default!;


    protected Licensing.Models.Vendors? Vendor { get; set; }
      

    protected virtual async System.Threading.Tasks.Task FormSubmit(Licensing.Models.Vendors args) { }


    protected async System.Threading.Tasks.Task ButtonCancelClick(MouseEventArgs args)
    {
        await Task.Run(() => DialogService.Close(null));
    }

}

EditVendor.razor (подкласс)

@*
    DCE Aug 24

    This is a dervied class from CreateEdit component class.
    CreateEdit is reused such that entities can be created or edited.
    Each subclass will perform any validation needed.

    be sure to use base.BuildRenderTree as this is only called for the parent class.
*@

@page "/vendors/edit"
@using Microsoft.EntityFrameworkCore

@inherits VendorDetails

<PageTitle>Edit</PageTitle>

@{
    base.BuildRenderTree(__builder);
}

@code {

    [Parameter] public int Id { get; set; }

    [Inject]
    public LicensingContext? Context { get; set; }


    protected override async System.Threading.Tasks.Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        base.Vendor = Context!.Vendors.FirstOrDefault(x => x.pkId == Id);
    }


    protected override async System.Threading.Tasks.Task FormSubmit(Licensing.Models.Vendors args)
    {
        if (base.Vendor != null)
        {
            try
            {
                await InvokeAsync(() => Context!.SaveChangesAsync());
                DialogService.Close();

            }
            catch (System.Exception e)
            {
                NotificationService.Notify(
                    new NotificationMessage()
                        {
                            Severity = NotificationSeverity.Error,
                            Summary = $"Error: Unable to save Vendor!",
                            Detail = string.Format("Msg:{0}", e.Message.ToString())
                        });
            }
        }
    }
}

Пытался использовать @inject IDBFactory... в компоненте razor, но это не дало мне доступа к теневым свойствам, поэтому я создал поле класса Context, которое содержит экземпляр моего класса Apps DbContext, LicensingContext

РЕДАКТИРОВАТЬ 1 LicensingContext регистрируется как служба (для dbContext) в файле Program.cs.

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

Ответы 1

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

Мне было интересно, вызовет ли внедрение моего класса LicensingContext для получения доступа Intellisense к теневым свойствам DbSets болезненные проблемы или это будет неэффективно? и почему.

Обычно это не вызывает серьезных проблем или неэффективности. Однако если LicensingContext внедряется с помощью внедрения зависимостей (DI) и часто используется, это может привести к проблемам с производительностью. Поэтому важно убедиться, что область действия объекта контекста правильно настроена в управлении жизненным циклом.

Настройка DbContext как ограниченной области — рекомендуемый подход для балансировки производительности и использования ресурсов:

LicensingContext регистрируется как служба (для dbContext) в файле Program.cs.

Потому что вы зарегистрировали DbContext, используя код типа builder.Services.AddDbContext<YourDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

По умолчанию метод AddDbContext настраивает DbContext как ограниченный. Это означает, что для каждого запроса или операции создается новый экземпляр DbContext, но в рамках того же запроса экземпляр DbContext является общим.

это не дало мне доступа к теневым свойствам

Кстати, похоже, вы не настроили теневые свойства сущности в своем методе OnModelCreating.

Вы можете настроить свойства тени, используя Property<TProperty>(String).

// for example:
modelBuilder.Entity<Blog>()
            .Property<DateTime>("LastUpdated");

Вы можете получить доступ к теневым свойствам через API ChangeTracker:

// for example:
context.Entry(myBlog).Property("LastUpdated").CurrentValue = DateTime.Now;

Вы можете обратиться к этой официальной документации за инструкциями.

большое спасибо. все решено.

TheDoc 12.08.2024 18:14

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