Blazor Server异步Task<string>无法显示字符串及用户表关联问题
解决Blazor排班表异步获取用户名及数据库关联问题
一、异步方法显示异常的解决办法
你直接在组件渲染代码里调用@GetNameFromIdAsync(day.Manager1)会输出Task对象的字符串结果,而非实际用户名——因为异步方法返回的是Task<string>,必须通过@await获取结果。但更高效的做法是提前预加载所有需要的用户名,避免循环中多次调用异步方法(频繁访问数据库/Identity服务会导致性能问题)。
1. 预加载用户名映射字典
在组件代码部分添加字典存储用户ID与用户名的映射,在初始化时批量查询:
private Dictionary<string, string> _userIdToNameMap = new Dictionary<string, string>(); private List<DayOfWeek> _rosterDays = new List<DayOfWeek>(); // 建议改名避免和系统类型冲突 protected override async Task OnInitializedAsync() { // 先获取所有排班数据 _rosterDays = await context.DayOfWeek.ToListAsync(); // 收集所有需要查询的用户ID,去重 var allUserIds = _rosterDays.SelectMany(day => new[] { day.Manager1, day.Manager2, day.Kitchen1, day.Kitchen2, day.Kitchen3, day.Driver1, day.Driver2, day.Driver3 }) .Where(id => !string.IsNullOrEmpty(id)) .Distinct() .ToList(); // 批量查询用户名并填充字典 foreach (var id in allUserIds) { var user = await UserManager.FindByIdAsync(id); _userIdToNameMap[id] = user?.FirstName ?? "未分配"; } }
2. 修改渲染代码
直接从字典中读取用户名,无需重复调用异步方法:
@foreach (var day in _rosterDays) { <tr> <td>@day.Day</td> <td>@day.OpenTime</td> <td>@day.CloseTime</td> <td>@(_userIdToNameMap.TryGetValue(day.Manager1, out var name) ? name : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Manager2, out var name2) ? name2 : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Kitchen1, out var name3) ? name3 : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Kitchen2, out var name4) ? name4 : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Kitchen3, out var name5) ? name5 : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Driver1, out var name6) ? name6 : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Driver2, out var name7) ? name7 : "未分配")</td> <td>@(_userIdToNameMap.TryGetValue(day.Driver3, out var name8) ? name8 : "未分配")</td> <AuthorizeView Roles="Administrator"> <td><a href="@($"rosterdays/edit?id={day.Id}")"><button class="btn-warning">Edit</button></a></td> <td><a href="@($"rosterdays/delete?id={day.Id}")"><button class="btn-danger">Delete</button></a></td> </AuthorizeView> </tr> }
注:如果一定要直接在渲染时调用异步方法,可改用
@await GetNameFromIdAsync(day.Manager1),但此方式会在循环中触发多次异步请求,性能较差,不推荐。
二、数据库中关联ApplicationUser与排班表
可以通过外键和EF Core导航属性,将ASP.NET Identity的ApplicationUser表与你的排班表关联,无需每次调用UserManager查询用户信息。
1. 修改排班表实体类
建议将实体类名改为RosterDay(避免和系统System.DayOfWeek冲突),添加外键和导航属性:
public class RosterDay { public int Id { get; set; } public string Day { get; set; } public TimeSpan OpenTime { get; set; } public TimeSpan CloseTime { get; set; } // 外键属性,指向AspNetUsers表的Id public string Manager1Id { get; set; } public string Manager2Id { get; set; } public string Kitchen1Id { get; set; } public string Kitchen2Id { get; set; } public string Kitchen3Id { get; set; } public string Driver1Id { get; set; } public string Driver2Id { get; set; } public string Driver3Id { get; set; } // 导航属性,用于EF Core加载关联用户 [ForeignKey(nameof(Manager1Id))] public ApplicationUser Manager1 { get; set; } [ForeignKey(nameof(Manager2Id))] public ApplicationUser Manager2 { get; set; } // 其余岗位的导航属性同理添加 }
2. 配置DbContext
在你的数据库上下文类中添加DbSet,并配置外键关系:
public class AppDbContext : IdentityDbContext<ApplicationUser> { public DbSet<RosterDay> RosterDays { get; set; } public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 配置外键关系:删除用户时将排班表中的外键设为null modelBuilder.Entity<RosterDay>() .HasOne(r => r.Manager1) .WithMany() .HasForeignKey(r => r.Manager1Id) .OnDelete(DeleteBehavior.SetNull); // 其余岗位的外键关系同理配置 } }
3. 查询时直接加载关联用户
查询排班数据时,通过Include直接加载关联的用户信息,无需调用UserManager:
_rosterDays = await context.RosterDays .Include(r => r.Manager1) .Include(r => r.Manager2) .Include(r => r.Kitchen1) // 其余岗位的Include .ToListAsync();
渲染时直接读取用户的FirstName:
<td>@day.Manager1?.FirstName ?? "未分配"</td>
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

