提问人:Matthew Arnold 提问时间:11/8/2022 最后编辑:Matthew Arnold 更新时间:11/8/2022 访问量:267
ASP.Net Core MVC 模型返回的 UserId FK 无效
ASP.Net Core MVC Models returning not valid with UserId FK
问:
好的,所以我遇到了一些问题,在这一点上完全不知道可能出什么问题。我有一个项目已经使用我从头开始构建的登录/注册系统完成了。这很简单。我决定实施 Microsoft 身份平台,以便可以使用 Goggle、Facebook 等添加第三方授权。我已成功添加 identty 平台,并且登录和注册正常工作。我现在唯一的问题是,我用于向网站添加帖子的表单需要将UserId记录为外键,以便我可以记录每个帖子的创建者。
我已将 UserId 作为外键添加到模型中,但现在每当我尝试添加帖子时,ModelState.IsValid 都会不断返回 false,但没有抛出模型验证错误。当我从控制器中删除 IsValid 检查时,它会将所有内容记录到数据库中,包括 UserId......因此,我不确定为什么当它清楚地正确记录所有数据时会返回 false。
这是我的帖子模型
public class Post
{
[Key]
public int PostId { get; set; }
[Required(ErrorMessage ="is required")]
[MinLength(3, ErrorMessage ="must be at least 3 characters")]
[MaxLength(50, ErrorMessage ="must be 50 characters or less")]
public string Title { get; set; }
[Required(ErrorMessage ="is required")]
[GreaterThan0]
[Display(Name ="Players on Team")]
public int PlayersOnTeam { get; set; }
[Required(ErrorMessage ="is required")]
[GreaterThan0]
[Display(Name ="Max Players On Team")]
public int MaxPlayersOnTeam { get; set; }
// 0 = PC, 1 = PS4, 2 = Xbox One, 3 = PS5, 4 = PS3, 5 = Xbox 360
[Required(ErrorMessage ="is required")]
public string Platform { get; set; }
[Required(ErrorMessage ="is required")]
public string Language { get; set; }
[Required(ErrorMessage ="is required")]
[Display(Name ="Group Type")]
public string GroupType { get; set; }
[Required(ErrorMessage ="is required")]
[GreaterThan0]
[Display(Name ="Minimum Level")]
public int MinLevel { get; set; }
[Required(ErrorMessage ="is required")]
[MinLength(10, ErrorMessage ="must be at least 10 characters")]
[MaxLength(500, ErrorMessage ="must be 500 characters or less")]
public string? Description { get; set; }
[Required(ErrorMessage ="is required")]
public string GameActivity { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime UpdatedAt { get; set; } = DateTime.Now;
// foreign keys
[ForeignKey("UserId")]
public AdvanceUser Author { get; set; }
// before I had this set up as which is why the controller uses AdvanceUser.Id, not UserId
// public string Id { get; set;}
// public AdvanceUser Author { get; set; }
}
这是我用于创建新帖子的 Post Controller 功能。我已经尝试了以下两个示例,最终都做了同样的事情。请注意,我知道它在此代码中使用了 AdvanceUser.Id 字段而不是 AdvanceUser.UserId(我已经来回更改了它,所以这不是问题。我使用UserId字段,但仍然无法正常工作
[HttpPost("/lfg/posts/create")]
public IActionResult Create(Post newPost)
{
if (!loggedIn)
{
return RedirectToAction("Index", "User");
}
if (uid != null)
{
newPost.Id = uid;
}
if (ModelState.IsValid == false)
{
return New();
}
var currentUser = _context.Users.FirstOrDefault(u => u.Id == User.FindFirstValue(ClaimTypes.NameIdentifier));
newPost.Author = currentUser;
_context.Posts.Add(newPost);
_context.SaveChanges();
return Dashboard();
}
[HttpPost("/lfg/posts/create")]
public async Task<IActionResult> Create(Post newPost)
{
var user = await _userManager.FindByIdAsync(uid);
if (!loggedIn)
{
return RedirectToAction("Index", "User");
}
// if (uid != null)
// {
// newPost.Id = uid;
// }
newPost.Author = user;
if (ModelState.IsValid == false)
{
return New();
}
_context.Posts.Add(newPost);
await _context.SaveChangesAsync();
return Dashboard();
}
它们位于类的顶部,用于身份验证和获取当前用户的用户 ID
private string? uid
{
get
{
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
return userId;
}
}
private bool loggedIn
{
get
{
return User.Identity.IsAuthenticated;
}
}
这是我添加新帖子的观点
@model Post
<div class="container w-75 mx-auto">
<h2 class="text-center">New Post</h2>
<form class="shadow p-3 rounded" asp-controller="Post" asp-action="Create" method="POST">
<div class="mb-3">
<label asp-for="Title"></label>
<span asp-validation-for="Title" class="text-danger font-weight-bold"></span>
<input class="form-control" asp-for="Title">
</div>
<div class="mb-3">
<label asp-for="GameActivity"></label>
<span asp-validation-for="GameActivity" class="text-danger font-weight-bold"></span>
<select class="form-control" asp-for="GameActivity">
@{
foreach (GameActivity activity in ViewBag.allActivities)
{
<option value="@activity.Name">@activity.Name</option>
}
}
</select>
</div>
@* <div class="mb-3">
<label asp-for="GameActivity"></label>
<span asp-validation-for="GameActivity" class="text-danger font-weight-bold"></span>
<select class="form-control" asp-for="GameActivity">
<option value="Raid">Raid</option>
<option value="Public Event">Public Event</option>
</select>
</div> *@
<div class="mb-3">
<label asp-for="PlayersOnTeam"></label>
<span asp-validation-for="PlayersOnTeam" class="text-danger font-weight-bold"></span>
<input class="form-control" asp-for="PlayersOnTeam">
</div>
<div class="mb-3">
<label asp-for="MaxPlayersOnTeam"></label>
<span asp-validation-for="MaxPlayersOnTeam" class="text-danger font-weight-bold"></span>
<input class="form-control" asp-for="MaxPlayersOnTeam">
</div>
<div class="mb-3">
<label asp-for="Platform"></label>
<span asp-validation-for="Platform" class="text-danger font-weight-bold"></span>
<select class="form-control" asp-for="Platform">
<option value="PC">PC</option>
<option value="PS4">PS4</option>
<option value="Xbox">Xbox One</option>
<option value="PS5">PS5</option>
<option value="PS3">PS3</option>
</select>
</div>
<div class="mb-3">
<label asp-for="Language"></label>
<span asp-validation-for="Language" class="text-danger font-weight-bold"></span>
<input class="form-control" asp-for="Language">
</div>
<div class="mb-3">
<label asp-for="GroupType"></label>
<span asp-validation-for="GroupType" class="text-danger font-weight-bold"></span>
<select class="form-control" asp-for="GroupType">
<option value="LFG">LFG</option>
<option value="LFM">LFM</option>
</select>
</div>
<div class="mb-3">
<label asp-for="MinLevel"></label>
<span asp-validation-for="MinLevel" class="text-danger font-weight-bold"></span>
<input class="form-control" asp-for="MinLevel">
</div>
<div class="mb-3">
<label asp-for="Description"></label>
<span asp-validation-for="Description" class="text-danger font-weight-bold"></span>
<textarea class="form-control" asp-for="Description"></textarea>
</div>
<button class="btn btn-success">Submit</button>
</form>
</div>
最后,这是我的Program.cs类。以防万一需要
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using LFGHub.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.Certificate;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
// builder.Services.AddAuthentication(
// CertificateAuthenticationDefaults.AuthenticationScheme)
// .AddCertificate();
// builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
// .AddEntityFrameworkStores<ApplicationDbContext>();
// builder.Services.AddControllersWithViews();
builder.Services.AddIdentity<AdvanceUser, IdentityRole>(options => {
options.Password.RequireDigit = true;
options.Password.RequireUppercase = true;
}).AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders().AddDefaultUI().AddRoles<IdentityRole>();
builder.Services.AddRazorPages();
builder.Services.AddScoped<RoleManager<IdentityRole>>();
builder.Services.AddScoped<UserManager<AdvanceUser>>();
var app = builder.Build();
var serviceProvider = app.Services.GetService<IServiceProvider>();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
// 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.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// app.UseAuthentication();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();
app.Run();
编辑: 下面是 AdvanceUser 模型。我不确定什么可能是不正确的。
using Microsoft.AspNetCore.Identity;
using LFGHub.Models;
public class AdvanceUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string DisplayName { get; set; }
public string MobileNumber { get; set; }
public string DestinyUsername { get; set; }
// public virtual ICollection<Post>? Posts { get; set; }
public List<Post> Posts { get; set; } = new List<Post>();
}
这是我的数据库上下文
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using LFGHub.Models;
namespace LFGHub.Data;
public class ApplicationDbContext : IdentityDbContext<AdvanceUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
builder.Entity<Post>()
.HasOne(c => c.Author)
.WithMany(x => x.Posts);
}
//public DbSet<Game> Games { get; set; }
public DbSet<GameActivity> GameActivities { get; set; }
public DbSet<Post> Posts { get; set; }
// public DbSet<GroupMember> GroupMembers { get; set; }
public DbSet<NewsPost> NewsPosts { get; set; }
}
答:
我弄清楚了问题出在哪里。我必须更改 Post 模型 foregin 键属性,如下所示:
[ForeignKey("User")]
public string UserId { get; set; }
public AdvanceUser? User { get; set; }
在我的 AdvanceUser 类中,我将 ICollection 从虚拟 ICollection 更改为普通的 ICollection
public ICollection<Post> Posts { get; set; }
最后,我将数据库上下文更改为
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
// Commented this out too
// builder.Entity<Post>()
// .HasOne(c => c.Author)
// .WithMany(x => x.Posts);
}
//public DbSet<Game> Games { get; set; }
public DbSet<GameActivity> GameActivities { get; set; }
public DbSet<Post> Posts { get; set; }
// public DbSet<GroupMember> GroupMembers { get; set; }
public DbSet<NewsPost> NewsPosts { get; set; }
我注意到,当我查看数据库中的表时,它正在创建两个 userId 列。一个称为 UserId,另一个称为 AuthorId。所以我做了这些改变,它现在起作用了!感谢所有帮助的人!
评论
UserId