2

我正在尝试首先使用 EF 代码创建具有多对多关系的数据库。

public class Item
{
    public int ItemId { get; set; }
    public String Description { get; set; }
    public ICollection<Tag> Tags { get; set; }

    public Item()
    {
        Tags = new HashSet<Tag>();
    }
}

public class Tag
{
    public int TagId { get; set; }
    public String Text { get; set; }
    public ICollection<Item> Presentations { get; set; }

    public Tag()
    {
        Presentations = new HashSet<Item>();
    }
}

public class ItemsEntities : DbContext
{
    public DbSet<Item> Items { get; set; }
    public DbSet<Tag> Tags { get; set; }
}

之后,我将一个项目添加到数据库中

var tag = new Tag { Text = "tag1" };
var item = new Item
{
    Description = "description1",
    Tags = new List<Tag>()
};
item.Tags.Add(tag);
using (var db = new ItemsEntities())
{
    db.Items.Add(item);
    db.SaveChanges();
}

问题是我无法输出带有相关标签的项目。控制器如下所示:

public ActionResult Index()
{
    ItemsEntities db = new ItemsEntities();
    return View(db.Items.ToList());
}

并且查看页面有以下代码:

@foreach (var item in Model) 
{
   <tr>
    <td>
        @Html.DisplayFor(model => item.Description)
    </td>
    <td>
        @foreach (var tag in item.Tags)
        {
            @tag.Text
        }
    </td>
</tr>
}

我希望该表包含“description1”和“tag1”,但我只得到“description1”。我真的不明白问题出在哪里。这样做的正确方法是什么?

4

2 回答 2

2

您的导航属性需要被标记virtual

public class Item
{
    public int ItemId { get; set; }
    public String Description { get; set; }
    public virtual ICollection<Tag> Tags { get; set; }

    public Item()
    {
        Tags = new HashSet<Tag>();
    }
}

public class Tag
{
    public int TagId { get; set; }
    public String Text { get; set; }
    public virtual ICollection<Item> Presentations { get; set; }

    public Tag()
    {
        Presentations = new HashSet<Item>();
    }
}
于 2012-07-05T03:33:26.680 回答
1

为了使您的代码正常工作,您可以按照virtual@danludwig 的说明标记您的集合属性。通过将集合属性标记为virtualEF Code First 将在迭代视图中的项目时延迟加载这些属性。使用这种方法会遇到 SELECT N+1 问题。让我们检查您的视图代码:

@foreach (var item in Model) 
{
   <tr>
     <td>
       @Html.DisplayFor(model => item.Description)
     </td>
     <td>
       @foreach (var tag in item.Tags)
       {
         @tag.Text
       }
     </td>
   </tr>
}

在此foreach循环中,您将遍历模型中使用 EF 数据上下文选择的所有项目。

db.Items.ToList()

这是您的第一个选择。但是在您上面的视图中,每次访问项目的Tags属性时都会执行另一个选择。重要的是每个项目。这意味着如果您有 100 个Itemsin db.Items DbSet,您将执行 101 个选择。这对大多数系统来说是不可接受的。

更好的方法是为每个项目预先选择标签。一种方法是使用 Include或选择与项目相关的标签到专用对象中。

public class ItemWithTags 
{
    public Item Item { get;set; }
    public IEnumerable<Tag> Tags { get;set; }
}

public ActionResult Index()
{
    ItemsEntities db = new ItemsEntities();

    var itemsWithTags = db.Items.Select(item => new ItemWithTags() { Item = item, Tags = item.Tags});
    return View(itemsWithTags.ToList());
}

在您的视图中,您可以遍历itemsWithTags集合、访问项目的属性以及Tags访问ItemWithTags.

您的代码的另一个问题是,ItemsEntities DbContext在您的代码中打开但从未关闭。您可以使用 VS MVC 模板生成一个控制器来DbContext正确处理打开和关闭!

您可以使用 MVC Mini Profiler 之类的工具来检查针对数据库执行的命令。这个Stackoverflow 问题展示了如何使用 EF Code First 设置 MVC Mini Profiler。

于 2012-07-05T04:00:38.687 回答