DbContext 在导航后被释放

问题描述 投票:0回答:1

我正在开发 blazor Web 应用程序,但我遇到了奇怪的行为。几乎所有页面都是复制/粘贴,它们都有 CRUD 操作和先前的不同元素列表(赛季、球员、赛程等...),但只有其中一个页面我遇到了问题。当我单击播放器的“编辑”按钮时,我收到引用 _dbContext 的“无法访问已处置的对象”。

向 DisposeAsync() 方法添加断点我发现当我导航到编辑页面时它已被触发。但只发生在玩家页面,不会发生在任何其他页面。

这是玩家页面:

@page "/players"
@attribute [Authorize]

@using Models = Biwenger.Models
@using Biwenger.Services
@inject PlayersService service;
@inject NavigationManager navigationManager;

<h3>Jugadores</h3>

<a class="btn btn-primary" href="/player" role="button">Añadir</a>

<div class="mb-3 lg-6">
    <label for="search" class="form-label">Buscar Jugador:</label>
    <input type="text" id="search" class="form-control" @bind="searchTerm" @oninput="FilterPlayers" placeholder="Escribe el nombre del jugador..." />
</div>
<table class="table">
    <thead>
        <tr>
            <th scope="col">#</th>
            <th scope="col">Nombre</th>
            <th scope="col">Equipo</th>
            <th scope="col">Acciones</th>
        </tr>
    </thead>
    <tbody>
        @if (filteredList.Count > 0)
        {
            foreach (var item in filteredList)
            {
                <tr>
                    <th scope="row">@item.Id</th>
                    <td>@item.Name</td>
                    <td>@item.Team.Name</td>
                    <td>
                        <button type="button" class="btn btn-primary" @onclick="() => EditPlayer(item.Id, item.TeamId)">Editar</button>
                    </td>
                </tr>
            }
        }
        else
        {
            <tr>
                <td colspan="4" class="text-center">No hay registros.</td>
            </tr>
        }
    </tbody>
</table>

@code {

    List<Models.Player> fullList = new List<Models.Player>();
    List<Models.Player> filteredList = new List<Models.Player>();
    string searchTerm = "";

    protected override async Task OnInitializedAsync()
    {
        fullList = await service.GetAllPlayers();
        filteredList = fullList;
    }

    private void FilterPlayers()
    {
        if (string.IsNullOrEmpty(searchTerm))
        {
            filteredList = fullList;
        } else
        {
            filteredList = fullList.Where(p => p.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase))
                .ToList();
        }

        StateHasChanged();
    }
    
    private void EditPlayer(int id, int teamId)
    {
        navigationManager.NavigateTo($"/player/{id}/{teamId}");  <-- Here calls for dispose
    }
}

这是我正在导航到的页面:

@page "/player/{id:int?}/{teamId:int?}"
@attribute [Authorize]

@using Biwenger.Models.ViewModels
@using Biwenger.Services

@inject ILogger<Player> Logger
@inject PlayersService service
@inject TeamsService teamsService
@inject SeasonsService seasonsService;
@inject NavigationManager navigationManager
@inject IJSRuntime JS

<PageTitle>Equipo</PageTitle>

<h3>@(model?.PlayerId == 0 ? "Nuevo Jugador" : "Editar Jugador")</h3>

<EditForm EditContext="editContext" OnValidSubmit="Submit" FormName="form">
    <DataAnnotationsValidator />
    <div class="col-mb-3 col-lg-6 col-md-6">
        <label for="name" class="form-label">Nombre</label>
        <input id="name" class="form-control" @bind="model!.Name" @onblur="CheckIfNameExists" @ref="nameInput"></input>
    </div>
    <div class="col-mb-3 col-lg-6">
        <ValidationMessage For="() => model!.Name"></ValidationMessage>
    </div>
    <div class="col-mb-3 col-lg-6 col-md-6">
        <label for="team" class="form-label">Equipo</label>
        <InputSelect id="team" class="form-control" @bind-Value="model!.TeamId">
            <option value="">Seleccione un equipo</option>
            @foreach (Models.Team team in listTeams)
            {
                <option value="@(team.Id)">@team.Name</option>
            }
        </InputSelect>
    </div>
    <div class="col-mb-3 col-lg-6">
        <ValidationMessage For="() => model!.TeamId"></ValidationMessage>
    </div>
    <div class="col-mb-3 col-lg-6 col-md-6">
        <label for="team" class="form-label">Posición</label>
        @if (positionsItems != null)
        {
        <BootstrapSelect
            TItem="Models.DropdownItem<String>"
            Data="@positionsItems"
            @bind-Value="model!.Position"
            TextField="@((item) => item.Value)"
            ValueField="@((item) => item.Key.ToString())"
            TType="Biwenger.Enums.Positions">
        </BootstrapSelect>
            
        }
    </div>
    <div class="col-mb-3 col-lg-6">
        <ValidationMessage For="() => model!.TeamId"></ValidationMessage>
    </div>
    <div class="col-mb-3 col-lg-6 col-md-6">
        <label for="cost" class="form-label">Coste @currentSeason!.Name</label>
        <input type="number" id="cost" @bind="currentPlayerCost!.Cost" min="1" class="form-control text-end" @ref="costInput" @onfocus="selectAllText" @onblur="addMillions" />
    </div>
    <div class="form-check">
        <input class="form-check-input" type="checkbox" value="@model!.Black" id="checkBlack" @bind="model!.Black" />
        <label class="form-check-label" for="checkBlack"> Es negro</label>
    </div>

    <div class="form-check">
        <input class="form-check-input" type="checkbox" value="@model!.Active" id="checkActive" @bind="model!.Active" />
        <label class="form-check-label" for="checkActive"> Activo</label>
    </div>

    <div class="col-mb-3 col-lg-6">
        <button type="button" class="btn btn-secondary ms-2" @onclick="GoBack">Volver</button>
        <button type="submit" class="btn @buttonClass" disabled="@(!editContext?.Validate() ?? false || isLoading)">
            @if (isLoading)
            {
                <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
                <span class="visually-hidden">Enviando...</span>
            }
            else if (showSuccess)
            {
                <span>Guardado...</span>
            }
            else if (showError)
            {
                <span>Error...</span>
            }
            else
            {
                <span>Enviar</span>
            }
        </button>
    </div>
</EditForm>

@code {
    private EditContext? editContext;
    private ElementReference nameInput;
    private ElementReference costInput;
    private Biwenger.Models.Season? currentSeason;
    private Biwenger.Models.PlayerSeasonCost? currentPlayerCost;

    [Parameter]
    public int? id { get; set; }

    [Parameter]
    public int? teamId { get; set; }

    [SupplyParameterFromForm]
    private PlayerWithCostViewModel? model { get; set; }

    private List<Models.Team> listTeams = new List<Models.Team>();

    private ValidationMessageStore? messageStore;

    private bool isLoading = false;
    private bool showSuccess = false;
    private bool showError = false;

    private string buttonClassSuccess = "btn-success";
    private string buttonClassError = "btn-danger";
    private string buttonClassIdle = "btn-primary";
    private string buttonClass = "btn-primary";

    IList<Models.DropdownItem<String>> positionsItems;

    protected override async Task OnInitializedAsync()
    {
        positionsItems = new List<Models.DropdownItem<String>>
        {
            new Models.DropdownItem<String> { Key = 0, Value = "Posición" },
            new Models.DropdownItem<String> { Key = 1, Value = "PT" },
            new Models.DropdownItem<String> { Key = 2, Value = "DF" },
            new Models.DropdownItem<String> { Key = 3, Value = "MC" },
            new Models.DropdownItem<String> { Key = 4, Value = "DL" },
        };

        editContext = new EditContext(model ??= new PlayerWithCostViewModel());
        currentSeason = seasonsService.GetCurrentSeason();
        currentPlayerCost = new Models.PlayerSeasonCost();
        listTeams = await teamsService.GetAllTeams();

        if (id.HasValue && id.Value > 0)
        {
            model = await service.GetPlayerWithCostById(id.Value, teamId!.Value);
            if (model != null)
            {
                editContext = new EditContext(model);
            }
        }

        messageStore = new ValidationMessageStore(editContext);
    }

    private async void CheckIfNameExists()
    {
        if (!string.IsNullOrEmpty(model!.Name))
        {
            bool exists = await service!.NameExists(model!.Name, id, model!.TeamId);

            messageStore?.Clear(() => model.Name);

            if (exists)
            {
                messageStore?.Clear();
                messageStore?.Add(() => model.Name, "El nombre ya existe");

            }

            editContext?.NotifyValidationStateChanged();
        }
    }

    private void GoBack()
    {
        navigationManager.NavigateTo("/players");
    }

    private async Task Submit()
    {
        isLoading = true;
        Logger.LogInformation("Se ha llamado a submit");

        bool success = false;

        if (editContext!.Validate())
        {
            if (id.HasValue && id.Value > 0)
            {
                success = await service.UpdatePlayer(model!, currentSeason!.Id);
            }
            else
            {
                if (model!.Costs!.Count == 0)
                {
                    model!.Costs.Add(new Models.PlayerSeasonCost()
                        {
                            Cost = currentPlayerCost!.Cost,
                            SeasonId = currentSeason!.Id,
                            TeamId = model!.TeamId
                        });
                }

                success = await service!.AddPlayer(model!);
            }

            isLoading = false;

            if (success)
            {
                showSuccess = true;
                buttonClass = buttonClassSuccess;

                StateHasChanged();

                await Task.Delay(3000);

                showSuccess = false;
                buttonClass = buttonClassIdle;

                StateHasChanged();

                if (!id.HasValue)
                {
                    model = new PlayerWithCostViewModel();
                    editContext = new EditContext(model);
                    messageStore = new ValidationMessageStore(editContext);
                    messageStore.Clear();
                }

                model!.Name = "";
                model.TeamId = 0;
                currentPlayerCost = new Models.PlayerSeasonCost();
                model!.Black = false;
                model!.Active = true;


                StateHasChanged();
                await JS.InvokeVoidAsync("focusElement", nameInput);
            }
            else
            {
                showSuccess = true;
                buttonClass = buttonClassError;

                await Task.Delay(1000);

                showSuccess = false;
                buttonClass = buttonClassIdle;
            }
        }

    }

    private async Task selectAllText()
    {
        await JS.InvokeVoidAsync("selectElementText", costInput);
    }

    private void addMillions()
    {
        if (currentPlayerCost!.Cost < 100)
        {
            currentPlayerCost.Cost = currentPlayerCost.Cost * 1000000;
        }
    }
}

还有玩家服务:

using Biwenger.Data;
using Biwenger.Models;
using Biwenger.Models.ViewModels;
using Microsoft.EntityFrameworkCore;

namespace Biwenger.Services
{
    public class PlayersService
    {
        private readonly ApplicationDbContext _dbContext;

        public PlayersService(ApplicationDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task<List<Player>> GetAllPlayers()
        {
            return await _dbContext.Players.Include(p => p.Team).AsNoTracking().ToListAsync();
        }

        public async Task<List<Player>> GetAllActivePlayers()
        {
            return await _dbContext.Players.Where(p => p.Active == true).AsNoTracking().ToListAsync();
        }


        public async Task<bool> AddPlayer(PlayerWithCostViewModel player)
        {
            var strategy = _dbContext.Database.CreateExecutionStrategy();
            bool result = false;

            await strategy.ExecuteAsync(async () =>
            {
                var transaction = await _dbContext.Database.BeginTransactionAsync();
                
                try
                {
                    var newPlayer = new Player
                    {
                        Active = player.Active,
                        Black = player.Black,
                        TeamId = player.TeamId,
                        Name = player.Name,
                        Position = player.Position,
                    };

                    await _dbContext.Players.AddAsync(newPlayer);


                    var playerSeasonCost = new PlayerSeasonCost
                    {
                        Cost = player.Costs!.First().Cost,
                        TeamId = player.TeamId,
                        PlayerId = newPlayer.Id,
                        SeasonId = player.Costs!.First().SeasonId,
                    };

                    await _dbContext.PlayersSeasonsCost.AddAsync(playerSeasonCost);
                    await _dbContext.SaveChangesAsync();


                    await transaction.CommitAsync();
                    result = true;
                }
                catch (Exception ex)
                {
                    await transaction.RollbackAsync();

                    result = false;
                }
            });

            return result;
        }

        public async Task<bool> UpdatePlayer(PlayerWithCostViewModel player, int seasonId)
        {
            Player? currentPlayer = await _dbContext.Players.FindAsync(player.PlayerId);

            if (currentPlayer == null)
            {
                return false;
            }

            var transaction = await _dbContext.Database.BeginTransactionAsync();

            try
            {
                currentPlayer.Name = player.Name;
                currentPlayer.Black = player.Black;
                currentPlayer.Position = player.Position;
                currentPlayer.Active = player.Active;
                currentPlayer.TeamId = player.TeamId;

                _dbContext.Players.Update(currentPlayer);
                await _dbContext.SaveChangesAsync();
                
                await transaction.CommitAsync();
                return true;
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                return false;
            }
        }

        public async Task<Player?> GetPlayerByid(int id)
        {
            return await _dbContext.Players.FindAsync(id);
        }

        public async Task<PlayerWithCostViewModel?> GetPlayerWithCostById(int playerId, int teamId)
        {
            try
            {

                Player? player = await _dbContext.Players
                    .Include(p => p.PlayersSeasonsCost)
                    .Where(p => p.Id == playerId && p.TeamId == teamId)
                    .FirstOrDefaultAsync(); <-- Already _dbContext Disposed

                if (player == null)
                {
                    return null;
                }

                List<PlayerSeasonCost>? pscList = await _dbContext.PlayersSeasonsCost.Where(psc => psc.PlayerId == playerId).AsNoTracking().ToListAsync();

                PlayerWithCostViewModel pwcvm = new PlayerWithCostViewModel
                {
                    PlayerId = playerId,
                    TeamId = teamId,
                    Active = player.Active,
                    Black = player.Black,
                    Position = player.Position,
                    Name = player.Name,
                    Costs = pscList ??= new List<PlayerSeasonCost>()
                };

                return pwcvm;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        public async Task<bool> NameExists(string name, int? id, int teamId)
        {
            if (id.HasValue && id.Value > 0)
            {
                return await _dbContext.Players.AnyAsync(t => t.Name == name && t.TeamId == teamId && t.Id != id);
            }

            return await _dbContext.Players.AnyAsync(t => t.Name == name && t.TeamId == teamId);
        }

        public async Task<List<PlayerWithCurrentCostVM>> GetPlayersWithCurrentCost()
        {
            List<PlayerWithCurrentCostVM> pwcc = new List<PlayerWithCurrentCostVM>();

            pwcc = await _dbContext.Players.Include(p => p.PlayersSeasonsCost)
                    .ThenInclude(psc => psc.Season)
                    .Where(p => p.Active == true)
                    .Select(p => new PlayerWithCurrentCostVM
                    {
                        Player = p,
                        Name = p.Name,
                        Cost = p.PlayersSeasonsCost == null ? 0 : p.PlayersSeasonsCost.Where(psc => psc.Season.Active == true).First().Cost
                    }) 
                    .AsNoTracking()
                    .ToListAsync();

            return pwcc;
        }

    }
}
c# asp.net-core blazor
1个回答
0
投票

最后问题出在这部分代码:

 <BootstrapSelect
            TItem="Models.DropdownItem<String>"
            Data="@positionsItems"
            @bind-Value="model!.Position"
            TextField="@((item) => item.Value)"
            ValueField="@((item) => item.Key.ToString())"
            TType="Biwenger.Enums.Positions">
        </BootstrapSelect>

BootsatrpSelect 似乎不适用于泛型类型或枚举。将其绑定到 int 变量,问题就消失了。

谢谢大家的反馈

© www.soinside.com 2019 - 2024. All rights reserved.