繁体   English   中英

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

[英]Determining if a model should have foreign keys / navigation properties

我正在构建一个相当简单的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; }
}

这让我想到了一个问题,我在GroceryItem类中拥有的SameItems的导航属性是否足以表示多个杂货项目的列表,或者这在引用自身时不起作用?

另外... CategoryQualityBestStore属性是否需要ID属性来表示GroceryItem类内部的外键(例如CategoryID),或者我用这种方法表示的方式还可以吗?

- - 编辑 - -

-重构代码-

我已根据以下建议对模型进行了重构,我认为它可以更好地适应您提出的建议(是第二次),意识到我的模型存在一些缺陷,并将价格成分提取到单独的购买模型中。

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; }
}

但是,我不确定哪个是本文的主题,如果我有一个集合作为模型的一部分(不像第一个示例那样引用自身),我可以用一个表示吗?导航属性还是需要采取额外的步骤?

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

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

它看起来像这样:

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

对于您的问题,最好的答案是“取决于情况”。 导航属性是一种通知Entity Framework实体之间存在关系的方法。 按照惯例,如果您具有导航属性,例如:

public Category Category { get; set; }

实体框架将在表上创建一个以[RelatedPropertyName]_[RelatedPK]形式命名的列。 对于您的类,上面的属性将导致名为Category_ID的列。 您无需做任何其他工作即可使其工作。 EF将自动处理该关系。

但是,通过这种方式,您将无法访问此外键属性。 您的实体的公共API中没有公开此内容。 通常,尤其是当从选择列表中选择相关项目以及类似的情况时,这变得很成问题,因为您必须将所选值存储在其他位置,通常是视图模型上的一个属性,然后使用该属性从数据库中查询相关内容。在将数据库设置到它所属的实体上并最终保存该实体之前。 鉴于具有实际的外键属性,您可以直接将其直接发布回此属性,而Entity Framework将自动连接相关实体。 因此,我倾向于始终将以下模式与导航属性一起使用:

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将通过假设存在一对多关系来处理此问题。 您将在dbo.GroceryItems表上dbo.GroceryItems一列名为GroceryItem_ID的列。 不过,在这里,您不仅将无法直接访问外键,而且也将无法访问公用父API GroceryItem 这可能不是问题,但需要注意。 管理关系的唯一方法是通过父项上的集合,而不是通过该集合中的子项。

但是,由于这是自引用的,并且您没有指定外键或实例导航属性,因此所有EF都会看到的是关系双方的集合,所以我的猜测是您最终会得到一个M2M中间表。 我目前无法自己验证该理论,而且我自己之前也没有尝试过这种特殊情况。

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

public virtual GroceryItem ParentGroceryItem { get; set; }

而且,即使如此,如果没有一些Fluent配置,我也不认为EF可以解决问题:

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

您也可以在其他情况下使用WithRequired代替WithOptional ,这显然会使关系成为必需的关系,但是由于这是自引用的,因此不可能是必需的,因为必须至少有一个根节点且没有父母

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM