我正在构建一个相当简单的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; }
答案 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
,这显然会使这种关系成为必需的关系,但由于这是自我指涉的,所以不可能拥有它必需的,因为必须至少有一个没有父节点的根节点。