尝试创建包含与数据库中另一个表 (
Edificio
) 的关系的 Facultad
时,不允许存储该表,并且模型 EdiFac
中的 Edificio
虚拟公共外键显示为空。在一切真实有效之前,modelState
不允许保存。请帮助我找到解决方案。
模型类
Facultad
:
namespace web_uni_mvc.Models;
public partial class Facultad
{
public int FacId { get; set; }
[Display(Name = "Nombre de la Facultad")]
public string FacNombre { get; set; } = null!;
[Display(Name = "Estatus de la Facultad")]
public string FacEstatus { get; set; } = null!;
public virtual ICollection<Edificio> Edificios { get; set; } = new List<Edificio>();
}
模型类
Edificio
:
namespace web_uni_mvc.Models;
public partial class Edificio
{
[Key]
public int EdiId { get; set; }
[Display(Name = "Nombre del Edificio")]
public string EdiNombre { get; set; } = null!;
[Display(Name = "Numero de Aulas")]
public int EdiNumAulas { get; set; }
//[Display(Name = "Facultad")]
[Required]
public int EdiFacId { get; set; }
[Display(Name = "Estatus")]
public string EdiEstatus { get; set; } = null!;
public virtual ICollection<Aula> Aulas { get; set; } = new List<Aula>();
[Required]
public virtual Facultad EdiFac { get; set; } = null!;
}
UniversidadContext
:
namespace web_uni_mvc.Models;
public partial class UniversidadContext : DbContext
{
public UniversidadContext()
{
}
public UniversidadContext(DbContextOptions<UniversidadContext> options)
: base(options)
{
}
public virtual DbSet<Aula> Aulas { get; set; }
public virtual DbSet<Edificio> Edificios { get; set; }
public virtual DbSet<Facultad> Facultads { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Aula>(entity =>
{
entity.HasKey(e => e.AulId).HasName("PK__Aula__A8EA7F0A995FB5E8");
entity.ToTable("Aula");
entity.Property(e => e.AulId).HasColumnName("Aul_Id");
entity.Property(e => e.AulEdiId).HasColumnName("Aul_Edi_Id");
entity.Property(e => e.AulEstatus)
.HasMaxLength(1)
.IsUnicode(false)
.HasColumnName("Aul_Estatus");
entity.Property(e => e.AulNumero).HasColumnName("Aul_Numero");
entity.HasOne(d => d.AulEdi).WithMany(p => p.Aulas)
.HasForeignKey(d => d.AulEdiId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK__Aula__Aul_Edi_Id__3C69FB99");
});
modelBuilder.Entity<Edificio>(entity =>
{
entity.HasKey(e => e.EdiId).HasName("PK__Edificio__F567AB14B5C3D23D");
entity.ToTable("Edificio");
entity.Property(e => e.EdiId).HasColumnName("Edi_Id");
entity.Property(e => e.EdiEstatus)
.HasMaxLength(1)
.IsUnicode(false)
.HasColumnName("Edi_Estatus");
entity.Property(e => e.EdiFacId).HasColumnName("Edi_Fac_Id");
entity.Property(e => e.EdiNombre)
.HasMaxLength(255)
.IsUnicode(false)
.HasColumnName("Edi_Nombre");
entity.Property(e => e.EdiNumAulas).HasColumnName("Edi_Num_Aulas");
entity.HasOne(d => d.EdiFac).WithMany(p => p.Edificios)
.HasForeignKey(d => d.EdiFacId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK__Edificio__Edi_Fa__398D8EEE");
});
modelBuilder.Entity<Facultad>(entity =>
{
entity.HasKey(e => e.FacId).HasName("PK__Facultad__58357016F79198D0");
entity.ToTable("Facultad");
entity.Property(e => e.FacId).HasColumnName("Fac_Id");
entity.Property(e => e.FacEstatus)
.HasMaxLength(1)
.IsUnicode(false)
.HasColumnName("Fac_Estatus");
entity.Property(e => e.FacNombre)
.HasMaxLength(255)
.IsUnicode(false)
.HasColumnName("Fac_Nombre");
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
Edificio
控制器:
public IActionResult Create()
{
ViewData["EdiFacId"] = new SelectList(_context.Facultads, "FacId", "FacNombre");
return View();
}
// POST: Edificios/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("EdiId,EdiNombre,EdiNumAulas,EdiFacId,EdiEstatus")] Edificio edificio)
{
if (ModelState.IsValid)
{
_context.Add(edificio);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["EdiFacId"] = new SelectList(_context.Facultads, "FacId", "FacNombre", edificio.EdiFacId);
return View(edificio);
}
为
Edificio
创建视图:
<div class="form-group pb-3">
<label asp-for="EdiFacId" class="control-label"></label>
<select asp-for="EdiFacId" class="form-control" asp-items="ViewBag.EdiFacId"></select>
</div>
我想创建
Edificio
实例。它使用 .NET 7。
简短的回答:不要在视图和控制器之间传递实体。
是的,许多“官方”示例就是这样做的。不,这是一种非常糟糕的做法,一旦您进入比保存 ToDo“实体”更复杂的示例,就会导致许多问题,并让您接触到为数据篡改打开大门的解决方案。
这样的事情不起作用的核心原因是,当您从控制器传递一个实体作为视图的模型时,您正在将该实体传递给视图引擎。这需要 cshtml 并组合 HTML 和一段 JS 来传递给客户端浏览器。当客户端浏览器捕获表单提交或您向服务器发出 Ajax / POST 并公开接受实体作为参数的方法时,MVC JS 或您自己的 JS 需要构建实体的新实例来发送它。它从 Web 的 HTML 和 JS 端哪里获取这个实体?它从 HTML 中的各种控件中抓取字段。
这样想,如果您要在页面中编写一个 JavaScript 方法来调用接受实体(尤其是具有相关实体的实体)的服务器操作,您需要如何执行?首先,您需要创建一个新实例。
@Model
对于客户端 HTML/JS 来说不存在。这就是为什么你需要为实体中不使用文本框等显示的字段添加 @Html.HiddenFor(x => x.ToDoId)
等操作。这样,当 JS 收集表单数据以通过 POST 发送时,它可以填充在 ToDoId 的值中。从技术上讲,如果您想要重建实体及其相关实体的完整有效对象图,则每个相关实体的每个属性都需要驻留在 HTML 和/或 JS 中的某个位置。那可能吗?是的。推荐吗?绝对不。 (基本上是从美好的 ASP 时代重新发明 ViewState。)
因此,简而言之,您作为模型发送到视图的内容不会保留在客户端并神奇地发送回服务器。即使您确实保留了客户端表示(这是可能的),也不应该对实体图执行此操作,否则您可能会遇到延迟加载,并且您正在公开有关您的域的所有内容以供客户端检查。即使这样,当它被发送回服务器时,它也只不过是数据的反序列化副本,而不是最初发送的同一实例。它永远不应该被信任,并且您在尝试处理跟踪引用时仍然会遇到问题。
不要将实体发送到视图,而是仅使用视图所需的数据组成视图模型,并接受仅表示您需要返回的数据的视图模型或单个值。服务器端您可以根据需要加载和更新实体。这可以帮助打破关于实际传递数据的坏习惯,可以更安全地序列化和存储客户端,并且通常比尝试传递实体的性能更好。实体代表数据状态,应该只关心数据状态,而不是视图状态。