我应该如何在MVC3中使用代码优先实体框架(4.1)声明外键关系?


104

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

例如,我有一个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; }
}

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

错误信息

类型'PhysicianPortal.Models.Order'上属性'Patient'上的ForeignKeyAttribute无效。在从属类型“ PhysicianPortal.Models.Order”上找不到外键名称“ Parent”。Name值应该是用逗号分隔的外键属性名称列表。

问候,

圭多

Answers:


164

如果您有一个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 Anselmi

@Guido-我已经根据您的最新代码编辑更新了答案,希望这对您有所帮助。
Sergi Papaseit 2011年

30

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

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

其他类型的约束可以通过数据注释和模型验证来实施。

编辑:

如果不设置,将会产生异常ParentId。它是必填属性(不能为null)。如果您没有设置,则很可能尝试将默认值发送到数据库。默认值为0,因此,如果您没有Id = 0的客户,则会出现异常。


感谢Ladislav-我在块引用中添加了一些其他信息。
Guido

@Ladislav。因此,为了强制执行此约束,我必须同时具有对Parent的引用和对ParentId的引用。那是对的吗?我将在上面添加实际的类以供参考。
Guido Anselmi

@Guido:那是新信息。您没有使用外键属性。默认情况下,所有导航属性都作为可选处理。根据需要使用流利的映射来映射它们。
Ladislav Mrnka,2011年

@Ladislav:再次谢谢你。我环顾四周以了解使用数据注释和Fluent API之间的区别。我按照我认为您所说的对上面的代码进行了更改。以上是我要做的吗?问候。
Guido Anselmi

没有ForeignKey属性定义与外键属性相关的导航属性,反之亦然。您没有外键属性,因此无法使用该属性。尝试在导航属性上使用Required属性(我没有测试过)。
Ladislav Mrnka,2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.