首页 > 解决方案 > 在 Blazor 中登录后使用实体框架返回的对象未完成

问题描述

问题描述如下,首先我给你看代码。

在我的 Blazor 应用程序的数据库中,有三个表:Factor、FactorEntry、EntrySet。

一个 EntrySet 包含多个属于一个 Factor 的 FactorEntry。

以下是相关的模型类:

public class Factor
{
    public int FactorId { get; set; }
    public string FactorName { get; set; }
...
    public virtual ICollection<FactorEntry> FactorEntry { get; set; }
}


public class FactorEntry
{
    public int FactorEntryId { get; set; }
    public int EntryValue { get; set; }
    public int FactorId { get; set; }
    public virtual Factor Factor { get; set; }
    public virtual EntrySet EntrySet { get; set; }
}

public class EntrySet
{
    public int EntrySetId { get; set; }
    public DateTime EntrySetDate { get; set; }
    public string UserId { get; set; }
    public virtual ICollection<FactorEntry> FactorEntries { get; set; } 
}

这是我用来为特定日期和用户请求 EntrySet 的方法:

    public async Task<EntrySet> GetEntrySet(DateTime date, string userID)
    {
        var entry = await _context.EntrySets
            .Include(entry => entry.FactorEntries)
            .Where(d => d.EntrySetDate.Date == date.Date)
            .Where(u => u.UserId == userID)
            .FirstOrDefaultAsync();

        return entry;
    }

在我的 Index.razor 有一个日期选择器。当日期更改时,我为选择的日期和登录的用户调用 GetEntrySet 方法。 OnInitialized 我将 Datepicker 的日期更改为当前日期。

<InputDate @bind-Value="this.ChoosenDate" />

@code{

public EntrySet CurrentEntrySet { get; set; } = new EntrySet() {

    FactorEntries = new List<FactorEntry>()
};

private bool entryExisted;

private DateTime choosenDate;

public DateTime ChoosenDate
{
    get { return choosenDate; }
    set
    {
        choosenDate = value;
        this.DateChange();
    }
}

public string UserID { get; set; }

protected override void OnInitialized()
{
    base.OnInitialized();

    var principal = httpContextAccessor.HttpContext.User;
    this.UserID = principal.FindFirstValue(ClaimTypes.NameIdentifier);

    ChoosenDate = DateTime.Now;
}

private async Task DateChange()
{
    if (String.IsNullOrEmpty(this.UserID))
    {
        Console.WriteLine("NoUserID");
    }
    else
    {
        var entrySetForDate = await service.GetEntrySet(this.choosenDate, this.UserID);

        if (entrySetForDate == null)
        {
            this.entryExisted = false;
            await this.CreateNewEntrySet();
            StateHasChanged();
        }
        else
        {
            this.entryExisted = true;
            CurrentEntrySet = entrySetForDate;
            StateHasChanged();
        }
    }
}

主要问题

一切正常。我使用 datepicker 更改日期,GetEntrySet 返回正确的 EntrySet,包括所有 FactorEntry 和相关的 Factor。

但是有一种情况会导致问题:在登录之后(使用 ASP.net Core Identity),在 OnInitialized 中更改日期时。GetEntrySet 返回正确的EntrySet,具有正确的FactorEntry 列表,但FactorEntry 中的Factor 为Null。

我不知道,为什么会这样。有什么线索吗?

附带问题

正如您在代码中看到的,我在 ChoosenDate 的 Set{} 中调用了异步方法 DateChange(),该方法绑定到了日期选择器。在 Set{} 中,我等不及了。知道我该如何改变吗?

问候斯特凡

标签: c#entity-frameworkblazorblazor-server-side

解决方案


跳出来的一件事是,当您急切加载 FactorEntries 时,您并没有急切加载它们下面的 Factor:

    var entry = await _context.EntrySets
        .Include(entry => entry.FactorEntries)
            .ThenInclude(fe => fe.Factor)
        .Where(d => d.EntrySetDate.Date == date.Date)
        .Where(u => u.UserId == userID)
        .FirstOrDefaultAsync();

如果您想在客户端和服务器之间传递实体,我建议尽可能避免双向引用。被序列化后,它们可以达到一个深度,引用将被保留为#null。我假设您可能已禁用延迟加载以帮助避免序列化程序触发读取。由于在您阅读条目时 DbContext 是否恰好在跟踪因子,您可能会遇到类似这样的间歇性或情境问题。禁用延迟加载后,当您使用其 FactorEntries 集合加载条目集时,当它填充每个 FactorEntry 时,它仍将通过其缓存查看是否跟踪了任何引用的 FactorId,如果可用,它将填充这些引用。

对于 Blazor,我怀疑 DbContext 在视图刷新之间存在一定的寿命(希望不会在会话之间共享),因此通常情况下,当您请求条目时,因素很有可能在正常条件下被加载,但是在第一次登录时,可能尚未阅读该用户条目的因素。如果是这种情况,我会对性能非常谨慎,具体取决于 DbContext 实例的存活时间。DbContext 跟踪的实体越多,读取和写入操作将继续获得越慢。


推荐阅读