确定模型是否应具有外键/导航属性

时间:2014-08-17 00:42:53

标签: c# asp.net-mvc entity-framework ef-code-first

我正在构建一个相当简单的MVC项目,并且仍然可以首先使用导航属性和带有代码的外键。

这是主要的模型类:

public class GroceryItem
{
    public int ID { get; set; }
    public string Name { get; set; }
    public GroceryCategory Category { get; set; }
    public QualityProfile Quality { get; set; }
    public GroceryStore BestStore { get; set; }
    public double BestPrice { get; set; }
    public double LastSeenPrice { get; set; }

    //Navigation Properties
    public virtual ICollection<GroceryItem> SimilarItems { get; set; }
}

这些是相关的课程:

public class GroceryStore
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public Uri Website { get; set; }
}

public class QualityProfile
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    /// <summary>
    /// Rank out of 1-10, 10 being the best
    /// </summary>
    public byte Ranking { get; set; }
}

public class GroceryCategory
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

这让我想到了我的问题, CloseItems 的导航属性我在 GroceryItem 类中足以表示多个杂货项目的列表,或者这不起作用因为它指的是自己?

此外... 类别质量 BestStore 属性需要ID属性来表示中的外键GroceryItem 类(例如CategoryID),还是我表示好的方式?

---- ---- EDIT

- 重构代码 -

我根据以下建议重新考虑了我的模型,我认为更好地适应了你所做的建议(是第二次),意识到我的模型有点瑕疵,并将价格成分提取到了单独购买模型。

public class GroceryItem
{
    public int ID { get; set; }
    public string Name { get; set; }

    [ForeignKey("Category")]
    public int CategoryID { get; set; }

    [ForeignKey("Quality")]
    public int QualityID { get; set; }

    //Navigation Properties
    public virtual QualityProfile Quality { get; set; }
    public virtual GroceryCategory Category { get; set; }
}

然而,我不确定哪个是关于这篇文章的主题,如果我有一个集合作为模型的一部分(一个不像第一个例子那样引用自己),我可以只代表具有导航属性还是需要采取额外步骤?

IE中。如果我要在 GroceryItem 上允许多个不同的类别,而不是这样:

[ForeignKey("Category")]
public int CategoryID { get; set; }
public virtual GroceryCategory Category { get; set; }

它看起来像这样:

public virtual ICollection<GroceryCategory> Categories { get; set; }

1 个答案:

答案 0 :(得分:3)

你的问题的最佳答案是,&#34;它取决于&#34;。导航属性是向实体框架通知实体之间存在关系的一种方式。按照惯例,如果您有导航属性,例如:

public Category Category { get; set; }

实体框架将在表格上创建一个以[RelatedPropertyName]_[RelatedPK]形式命名的列。给定您的类,上面的属性将导致名为Category_ID的列。没有什么比你需要做的更好了。该关系将由EF自动处理。

但是,通过这种方式,您无法访问此外键属性。它未在您实体的公共API中公开。通常,特别是在从选择列表和类似的此类场景中选择相关项时,这会成为问题,因为您必须将选定的值存储在其他位置,通常是视图模型上的属性,然后使用它来查询相关的事物。数据库在设置它所属的实体之前,最后保存实体。然而,使用实际的外键属性,您只需直接发回到此属性,实体框架将自动连接相关实体。因此,我倾向于使用导航属性始终遵循以下模式:

public int FooId { get; set; }
public virtual Foo Foo { get; set; }

在大多数情况下,Entity Framework会自动连接这两个,这样FooId将保存Foo导航属性的外键关系。但是,偶尔,EF会绊倒并尝试在幕后创建隐式外键,但是你可以通过明确告诉EF这是外键来纠正这种行为:

[ForeignKey("Foo")]
public int FooId { get; set; }

大致相同的是集合导航属性。 EF会将此视为表明存在一对多关系,并在对方实体上添加隐式外键。鉴于你的收藏:

public virtual ICollection<GroceryItem> SimilarItems { get; set; }

对立实体实际上是同一个实体,它提供了一个有趣的用例。通常,EF会通过假设一对多的关系来处理这个问题。您最终会在GroceryItem_ID表格中找到名为dbo.GroceryItems的列。但是,在这里,您不仅不能直接访问外键,而且也没有用于访问父GroceryItem的公共API。这可能不是问题,但需要注意的事项。您能够管理关系的唯一方法是通过父级的集合,而不是通过该集合中的子项。

但是,由于这是自引用的,并且您没有指定外键或实例导航属性,因此所有EF都会看到关系两侧的集合,所以我的猜测是您实际上已经结束带有中间表的M2M。我现在无法自己测试这个理论,我以前也没有尝试过这个特殊的场景。

要创建真正的一对多,您需要创建另一个类似于以下的导航属性:

public virtual GroceryItem ParentGroceryItem { get; set; }

而且,即使如此,我也不认为EF会在没有一点Fluent配置的情况下得到这一点:

HasMany(m => m.SimilarItems).WithOptional(m => m.ParentGroceryItem);

你也可以在其他场景中使用WithRequired而不是WithOptional,这显然会使这种关系成为必需的关系,但由于这是自我指涉的,所以不可能拥有它必需的,因为必须至少有一个没有父节点的根节点。