我应该如何在 MVC3 中使用 Code First Entity Framework (4.1) 声明外键关系?

Posted

技术标签:

【中文标题】我应该如何在 MVC3 中使用 Code First Entity Framework (4.1) 声明外键关系?【英文标题】:How Should I Declare Foreign Key Relationships Using Code First Entity Framework (4.1) in MVC3? 【发布时间】:2011-07-29 09:50:14 【问题描述】:

我一直在寻找有关如何使用代码优先 EF 4.1 声明外键关系和其他约束的资源,但运气不佳。基本上我在代码中构建数据模型并使用 MVC3 来查询该模型。一切都通过 MVC 工作,这很棒(向微软致敬!)但现在我不希望它工作,因为我需要有数据模型约束。

例如,我有一个 Order 对象,它有大量的外部对象(表)属性。现在我可以创建一个 Order 没问题,但不能添加外键或外部对象。 MVC3 设置这个没有问题。

我意识到我可以在保存之前自己将对象添加到控制器类中,但是如果没有满足约束关系,我希望对 DbContext.SaveChanges() 的调用失败。

新信息

所以,具体来说,我想要一个 当我尝试时发生异常 保存一个 Order 对象而不 指定客户对象。这 如果我似乎不是这种行为 只需按照描述组合对象 在大多数 Code First EF 文档中。

最新代码:

public class Order

    public int Id  get; set; 

    [ForeignKey( "Parent" )]
    public Patient Patient  get; set; 

    [ForeignKey("CertificationPeriod")]
    public CertificationPeriod CertificationPeriod  get; set; 

    [ForeignKey("Agency")]
    public Agency Agency  get; set; 

    [ForeignKey("Diagnosis")]
    public Diagnosis PrimaryDiagnosis  get; set; 

    [ForeignKey("OrderApprovalStatus")]
    public OrderApprovalStatus ApprovalStatus  get; set; 

    [ForeignKey("User")]
    public User User  get; set; 

    [ForeignKey("User")]
    public User Submitter  get; set; 

    public DateTime ApprovalDate  get; set; 
    public DateTime SubmittedDate  get; set; 
    public Boolean IsDeprecated  get; set; 

这是我现在在访问 VS 生成的 Patient 视图时遇到的错误:

错误信息

属性上的 ForeignKeyAttribute 类型上的“患者” 'PhysicianPortal.Models.Order' 不是 有效的。外键名称“父” 未在依赖类型上找到 'PhysicianPortal.Models.Order'。这 名称值应以逗号分隔 外键属性名称列表。

问候,

圭多

【问题讨论】:

【参考方案1】:

如果您有一个 Order 类,添加一个引用模型中另一个类的属性,例如 Customer 应该足以让 EF 知道其中存在关系:

public class Order

    public int ID  get; set; 

    // Some other properties

    // Foreign key to customer
    public virtual Customer Customer  get; set; 

您始终可以明确设置FK 关系:

public class Order

    public int ID  get; set; 

    // Some other properties

    // Foreign key to customer
    [ForeignKey("Customer")]
    public string CustomerID  get; set; 
    public virtual Customer Customer  get; set; 

ForeignKeyAttribute 构造函数将字符串作为参数:如果将其放在外键属性上,则它表示关联导航属性的名称。如果将它放在导航属性上,则它表示关联外键的名称。

这意味着,如果您将ForeignKeyAttribute 放在Customer 属性的哪个位置,则该属性将在构造函数中采用CustomerID

public string CustomerID  get; set; 
[ForeignKey("CustomerID")]
public virtual Customer Customer  get; set; 

根据最新代码编辑 由于这一行,您会收到该错误:

[ForeignKey("Parent")]
public Patient Patient  get; set; 

EF 将寻找一个名为Parent 的属性以将其用作外键强制执行器。你可以做两件事:

1) 删除ForeignKeyAttribute 并替换为RequiredAttribute 以根据需要标记关系:

[Required]
public virtual Patient Patient  get; set; 

RequiredAttribute 装饰属性还有一个很好的副作用:数据库中的关系是用ON DELETE CASCADE 创建的。

我还建议将属性设置为 virtual 以启用延迟加载。

2) 创建一个名为Parent 的属性,该属性将用作外键。在这种情况下,将其称为 ParentID 可能更有意义(您还需要更改 ForeignKeyAttribute 中的名称):

public int ParentID  get; set; 

根据我在这种情况下的经验,尽管反过来更好:

[ForeignKey("Patient")]
public int ParentID  get; set; 

public virtual Patient Patient  get; set; 

【讨论】:

感谢 Sergi - 我在块引用中添加了一些附加信息。 @Guido - 我已经根据您最新的代码编辑更新了我的答案,希望对您有所帮助。【参考方案2】:

您可以通过以下方式定义外键:

public class Parent

   public int Id  get; set; 
   public virtual ICollection<Child> Childs  get; set; 


public class Child

   public int Id  get; set; 
   // This will be recognized as FK by NavigationPropertyNameForeignKeyDiscoveryConvention
   public int ParentId  get; set;  
   public virtual Parent Parent  get; set; 

现在 ParentId 是外键属性,定义了孩子和现有父母之间的必要关系。在没有现有父级的情况下保存子级将引发异常。

如果您的 FK 属性名称不包含导航属性名称和父 PK 名称,您必须使用 ForeignKeyAttribute 数据注释或流式 API 来映射关系

数据标注:

// The name of related navigation property
[ForeignKey("Parent")]
public int ParentId  get; set; 

流畅的 API:

modelBuilder.Entity<Child>()
            .HasRequired(c => c.Parent)
            .WithMany(p => p.Childs)
            .HasForeignKey(c => c.ParentId);

data annotations and model validation 可以强制执行其他类型的约束。

编辑:

如果你不设置ParentId,你会得到一个异常。它是必需的属性(不可为空)。如果您只是不设置它,它很可能会尝试将默认值发送到数据库。默认值为 0,因此如果您没有 Id = 0 的客户,您将收到异常。

【讨论】:

感谢 Ladislav - 我在块引用中添加了一些附加信息。 @Ladislav。因此,为了强制执行此约束,我必须同时拥有对 Parent 的引用和对 ParentId 的引用。那是对的吗?我将添加上面的实际类以供参考。 @Guido:这是新信息。您没有使用外键属性。默认情况下,您的所有导航属性都作为可选属性处理。根据需要使用流利的映射来映射它们。 @Ladislav:再次感谢您。我正在环顾四周以了解使用数据注释和 Fluent API 之间的区别。我根据我认为您所说的对上面的代码进行了更改。以上是我必须做的吗?问候。 没有 ForeignKey 属性定义与外键属性相关的导航属性,反之亦然。您没有外键属性,因此您不能使用该属性。尝试在导航属性上使用 Required 属性(我没有测试过)。

以上是关于我应该如何在 MVC3 中使用 Code First Entity Framework (4.1) 声明外键关系?的主要内容,如果未能解决你的问题,请参考以下文章

MVC 3 Code first First,内置成员身份验证

我应该使用实体框架 4.1 和 MVC3 启用或禁用动态代理吗?

如何将 Entity Framework 4.1 与 MVC 3 登录一起使用

如何在 MVC3 中使用 https 生成绝对 URL?

在asp.net MVC3中怎么为一个实体类的ID属性设置为自动增长呢?此时ID不是主键,Code才是主键?如下

MVC3+EF4.1学习系列-------创建EF4.1 code first的第一个实例