Asp.Net MVC 6 实体框架 - 模型关系

本文关键字:模型 关系 框架 实体 Net MVC Asp | 更新日期: 2023-09-27 18:34:47

我最近开始 Asp.Net MVC开发(使用MVC 6和Asp 5(。

我正在尝试在 2 个模型类之间建立关系。产品和类别。产品属于一个类别,因此一个类别可以包含许多产品。

这是我的模型代码:

public class Category
{
    public int id { get; set; }
    public string name { get; set; }
    public virtual ICollection<Product> product { get; set; }
}
public class Product
{
    public int id { get; set; }
    public string name { get; set; }
    public decimal price { get; set; }
    public virtual Category category { get; set; }
}
然后

,我使用带有视图的实体框架从模型创建了 2 个控制器,然后执行迁移。然后,我对两者都有 CRUD 操作,效果很好,但问题是我无法将产品分配到类别。我只能使用生成的视图设置产品的名称和价格。

希望有人能帮忙。显然,我希望在创建产品时出现某种下拉菜单,以显示所有类别,因此我可以将它们链接在一起。

谢谢。

*编辑*产品控制器 - 创建方法:

// POST: Products/Create
    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Create(Product product)
    {
        if (ModelState.IsValid)
        {
            _context.Product.Add(product);
            _context.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(product);
    }

再次,请注意,我不是在尝试手动添加功能。我正在尝试让整个脚手架/迁移过程处理对象关系,因此它可以为我自动生成它并加快我的开发过程。

*

编辑 2 *产品型号更改为:

public class Product
{
    public int ProductID { get; set; }
    public string ProductName { get; set; }
    public decimal ProductPrice { get; set; }
    [ForeignKey("Category")]
    public int CategoryID { get; set; }
    public virtual Category category { get; set; }
}

现在,这将在我创建产品时创建一个类别下拉列表。但是 - 即使我创建了一个类别,列表也是空的,所以仍然缺少一些东西?也许类别模型需要其他信息?这是它在 GET 创建中显示列表的方式:

// GET: Products/Create
    public IActionResult Create()
    {
        ViewData["CategoryID"] = new SelectList(_context.Category, "id", "category");
        return View();
    }

这只是一个空列表...快到了,但还没有完全。

Asp.Net MVC 6 实体框架 - 模型关系

你的产品类应该是这样的

public class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal ProductPrice { get; set; }
[ForeignKey("Category")]
public int CategoryID {get;set;}
public virtual Category category { get; set; }
}

现在,您的产品表中将有一列,用于存储它所属的类别。

要获取包含所有可用类别的下拉列表以启用选择 ,您可以尝试以下操作

 ViewBag.CategoryID= new SelectList(db.Category, "CategoryID", "CategoryName");

在视图中,您可以按如下方式使用它

@Html.DropDownList("CategoryID", null, htmlAttributes: new { @class = "form-control" })

产品需要包含 CategoryId(外键(。然后,您可以告诉 EF 关系的样子(即使如果命名正确,EF 可能会自己弄清楚(:

public class Category
{
    public int CategoryId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Product> Products { get; set; } 
}
public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId { get; set; }
    public virtual Category Category { get; set; }
}
public class StoreContext : DbContext  
{ 
    public DbSet<Category> Categories{ get; set; } 
    public DbSet<Product> Products { get; set; } 
    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
        // optionally, even EF can follow convention and detect 1-m relationship between product and category. 
        modelBuilder.Entity<Product>().HasRequired(p => p.Category) 
            .WithMany(c => c.Products) 
            .HasForeignKey(p => p.CategoryId);
    }
}

创建新产品时,请使用正确的值设置 CategoryId 属性,该值将它连接到类别。

您的 ViewModel 应如下所示(可以包含整个产品类或稍后必须映射的单个属性(:

public class ProductViewModel
{
    public ProductViewModel() 
    { 
        EditableProduct = new Product();
    }
    public List<SelectListItem> Categories { get; set; }
    public Product EditableProduct { get; set; }
}

产品控制器将填充所有类别并准备视图模型:

public ActionResult Edit(int? id)
{
    var model = new ProductViewModel();
    model.Categories = dbContext.Categories.Select(x => new SelectListItem() {Text = x.Name, Value = x.Id.ToString()}).ToList();
    // check if product even exists
    model.EditableProduct = id.HasValue ? dbContext.Products.Find(id.Value) : new Product();
    return View(model);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Edit(ProductViewModel model)
{
    // check validation
    Product product;
    if (model.EditableProduct.Id > 0)
        product = dbContext.Products.Find(model.EditableProduct.Id);
    else
    {
        product = new Product();
        dbContext.Products.Add(product);
    }
    product.CategoryId = model.EditableProduct.CategoryId;
    product.Name = model.EditableProduct.Name;
    // add try/catch 
    dbContext.SaveChanges();
    return RedirectToAction("Edit", new {id = product.Id});
}

然后,Razor 视图可以呈现类别:

@Html.DropDownListFor(x => x.EditableProduct.CategoryId, Model.Categories)

这是所有查找列表和一对多关系可以遵循的一般模式。

忠告:不要将数据库/域模型"泄露"到视图中。将名称、id 等属性放入 View Model 类,并在收到请求并对其执行验证时将它们映射到 EF 模型中。尽量避免将产品对象放入视图模型中,就像我在这个例子中所做的那样!

这似乎是 MVC 脚手架中的一个错误,您可以在此处找到更多详细信息:https://github.com/aspnet/Scaffolding/issues/149

在您的视图中尝试这样的事情:

@{ var categories = (IEnumerable<SelectListItem>)ViewData["CategoryID"]; }
<select asp-for="CategoryID" asp-items="categories" class="form-control"></select>

这为我解决了类似的问题。