我们正在创建像https://www.etsy.com/这样的市场。我们在列表分类方面存在问题。我们希望将列表中的项目分为3个级别,f.ex必须按此顺序分类:
Category 1
Sub Category 1.1
Sub Category 1.1.1
重要的一点是,当你选择一个类别时,f.ex。电子产品,然后在子类别中,你只能看到电脑,智能手机,电视等东西。
这就是我们现在所拥有的
public class Listing
{
public int ListingId { get; set; }
public String Name { get; set; }
public int Subcategory2Id { get; set; }
public virtual Subcategory2 Subcategory2 { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public String CategoryName { get; set; }
public virtual ICollection<Subcategory1> Subcategory1s { get; set; }
}
public class Subcategory1
{
public int Subcategory1Id { get; set; }
public String Subcategory1Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Categories { get; set; }
public virtual ICollection<Subcategory2> Subcategory2s { get; set; }
}
public class Subcategory2
{
public int Subcategory2Id { get; set; }
public String Subcategory2Name { get; set; }
public int Subcategory1Id { get; set; }
public virtual Subcategory1 Subcategory1s { get; set; }
public virtual ICollection<Listing> Listings { get; set; }
}
在IdentityModels-ApplicationDbContext中我们有
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Listing> Listings { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Subcategory1> Subcategory1s { get; set; }
public DbSet<Subcategory2> Subcategory2s { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
事情是我们不确定这是他正确的做法,我们不知道如何继续,这意味着当你创建一个列表时,你必须有3个下拉列表,你在哪里选择相应的类别。首先,您选择您的类别,然后您可以选择子类别1等...
答案 0 :(得分:4)
您绝对不应该有多个类别/子类别实体。一个类别可以有一个父类,它可以有子元素,但它们都是“类别”。
public class Category
{
public int Id { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
}
ParentId
可以为空,因为顶级类别没有父级。
实体框架往往会被自引用关系弄糊涂,因此您可能需要一些流畅的配置来帮助它:
public class Category
{
// properties
public class Mapping : EntityTypeConfiguration<Category>
{
public class Mapping()
{
HasOptional(m => m.Parent).WithMany(m => m.Children);
}
}
}
然后,在你的背景下:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Category.Mapping());
}
完成所有这些后,当您处于“电子”类别时,只需迭代它的Children
属性即可显示子类别。
<强>更新强>
如果您需要完整的层次结构而不是一次只需一个级别,那么您有几个选择。首先,您可以在查询时包含多个级别:
db.Categories.Include("Children.Children");
但这并不是很有效率,我绝对不会建议比三级儿童更深入地钻研。但是,这就是你所要求的,所以这仍然是一种可行的方法。
其次,您可以创建一个存储过程来为您遍历层次结构。它有点复杂,但是通过WITH
和UNION ALL
的组合,您可以创建层次结构的平面表示,然后递归使用LINQ的GroupBy
将其重新分解为层次结构
@ Hackerman建议使用HIERARCHYID的最后一个潜在的第三个选项,但不幸的是,要做到这一点,你必须从EF上下文中完全删除Category
,这也意味着删除与它的任何直接关系,以及。要将产品与类别相关联,您只能存储id(而不是外键),然后使用该id在第二步中手动查找类别。不幸的是,虽然这个解决方案使得更容易处理层次结构,但它使其他一切变得更加困难。无论哪种方式,都取决于你。
答案 1 :(得分:0)
这似乎是一个正确的解决方案。
您也可以只为所有类别使用一个类(一个DB表等)。然后,您的“类别”类/表必须包含父类别的引用(可为空)。这允许对所有类别进行通用处理。
例如,当用户创建项目时,您可以显示主类别的下拉列表。如果用户选择包含其他类别的类别,则会显示包含子类别等的其他下拉列表...
答案 2 :(得分:0)
我在这里给出了一个带图像上传的类别和子类别的例子。
public class ProductController : Controller
{
ApplicationDbContext db = new ApplicationDbContext();
// GET: Product
public ActionResult Index()
{
return View();
}
public ActionResult insert(int? id)
{
ViewBag.categoryList = db.Product.Where(x => x.CategoryId == 0).Select(x => new SelectListItem { Text = x.name, Value = x.Id.ToString() }).ToList();
var product = db.Product.Where(x => x.Id == id).Select(x => x).FirstOrDefault();
if (product == null) { product = new Product(); product.CategoryId = 0; }
return View(product);
}
[HttpPost]
public ActionResult insert(Product model)
{
if (Request.Files.Count > 0)
if (Request.Files["fileupload"].ContentLength > 0)
{
var fileupload = Request.Files[0];
var fileName = Path.GetFileName(fileupload.FileName);
model.Imagename = fileName;
model.ImageUrl = DateTime.Now.Ticks.ToString() + "." + fileName.Split('.')[1];
string baseurl = Server.MapPath("/") + "Images/" + model.ImageUrl;
fileupload.SaveAs(baseurl);
}
if (model.Id > 0)
{
var productEntity = db.Product.Where(x => x.Id == model.Id).Select(x => x).FirstOrDefault();
if (model.Imagename != null)
productEntity.Imagename = model.Imagename;
if (model.ImageUrl != null)
productEntity.ImageUrl = model.ImageUrl;
productEntity.name = model.name;
productEntity.CategoryId = model.CategoryId;
}
else
{
db.Product.Add(model);
}
db.SaveChanges();
return RedirectToAction("Index");
}
public ActionResult ProductList()
{
var product = db.Product.Where(x => x.Id > 0).Select(x => x).ToList();
return View(product);
}
public ActionResult getsubcategory(int id)
{
var list = db.Product.Where(x => x.CategoryId == id)
.Select(x => new SelectListItem { Text = x.name, Value = x.Id.ToString() }).ToList();
return Json(list, JsonRequestBehavior.AllowGet);
}
}
此上层控制器用于插入更新记录。
html代码:
@model WebApplication1.Models.Product
@{
ViewBag.Title = "insert";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>insert</h2>
@using (Html.BeginForm("insert","product", FormMethod.Post,new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
@Html.HiddenFor(x=>x.Id)
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
<label class="control-label col-md-2">SubCategory</label>
<div class="col-md-10">
@Html.DropDownList("SubCategory", new SelectList(ViewBag.categoryList, "Value", "Text", Model.CategoryId), "-Select-", new { @onchange = "categoryselect()", htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.CategoryId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.CategoryId, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.CategoryId, new SelectList(ViewBag.categoryList, "Value", "Text", Model.CategoryId),"-Select-", new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.CategoryId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.TextBoxFor(model => model.name, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.name, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Imagename, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<input id="Imagename" name="fileupload" type="file" class = "form-control" />
@*@Html.(model => model.Imagename, new { htmlAttributes = new { @class = "form-control" } })*@
@Html.ValidationMessageFor(model => model.Imagename, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
<script>
function categoryselect () {
var d = $("#SubCategory option:selected").val();
$.ajax({
url: "/product/getsubcategory?id="+d
, type: "get"
, success: function (data) {
// alert(data)
$("#CategoryId").html('<option value="">-select- </option>');
for(var i=0;i<data.length;i++)
$("#CategoryId").append('<option value="' + data[i].Value + '">' + data[i].Text + '</option>')
}
})
}
</script>
模型:
namespace WebApplication1.Models
{
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CategoryId { get; set; }
public string name { get; set; }
public string ImageUrl { get; set; }
public string Imagename { get; set; }
}
public class Category
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int PrentId { get; set; }
public string name { get; set; }
}
}
索引页:
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<div id="productList">
</div>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
$(document).ready(function () {
$.ajax({
url:"/product/productlist"
, type: "GET"
,success:function(data)
{
$("#productList").html(data)
}
})
})
</script>
列表页面:
@model IEnumerable<WebApplication1.Models.Product>
<p>
@Html.ActionLink("Create New", "Insert")
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.CategoryId)
</th>
<th>
@Html.DisplayNameFor(model => model.name)
</th>
<th>
@Html.DisplayNameFor(model => model.ImageUrl)
</th>
<th>
@Html.DisplayNameFor(model => model.Imagename)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.CategoryId)
</td>
<td>
@Html.DisplayFor(modelItem => item.name)
</td>
<td>
@Html.DisplayFor(modelItem => item.ImageUrl)
</td>
<td>
@Html.DisplayFor(modelItem => item.Imagename)
</td>
<td>
@Html.ActionLink("Edit", "insert", new { id=item.Id })
</td>
</tr>
}
</table>