如何解决实体框架数据库表关联
我有一个像这样的领域模型:
public class Rental
{
public int Id { get; set; }
public DateTime DateRented { get; set; }
public DateTime? DateReturned { get; set; }
[Required]
public Customer Customer { get; set; }
[Required]
public Movie Movie { get; set; }
}
实体框架将其与其他两个表(Customers
和Movies
)相关联,并自动在Rentals
表中创建外键列。在我的.NET课程中,我被教导要通过添加Id
属性与其他表进行关联,例如,Rental
域模型将包含以下属性:
public int MovieId { get; set; }
public int CustomerId { get; set; }
但是,如果没有这些代码行,代码将运行良好。您能告诉我哪种方法更好吗?也许其中之一可以提供更大的灵活性?我是.NET的新手,所以欢迎您提供所有解释。
解决方法
下面的第一行会引发语法错误,那是无效的C#代码
public MovieId { get; set; }
public CustomerId { get; set; }
第二件事,EF足够聪明,可以知道实体之间的关系,从您的用例角度来看,当您要访问有关客户的信息(例如,Eg Name)时,最好以这种方式放置Typed属性确实可以使用rental.Customer.Name
使用它,而不必分开拉它。
当使用EF定义您的架构的Code-First时,EF将使用约定在表中创建合适的FK列,并在幕后使用这些列。您可以使用所需的任何命名约定来定义自己的FK,但是您需要告诉EF将哪个列用作什么表的FK关系。这是通过使用[ForeignKey]
属性或使用外键配置关系(modelBuilder或EntityTypeConfiguration
)来完成的。
按照惯例,EF将通过相关对象的 Type 来定义FK列名称,这可能不是您在架构中想要的。例如,如果您有一个Order实体想要引用CreatedBy和LastModifiedBy的User实体,那么您可能会希望拥有类似
的东西public class Order
{
// ...
public virtual User CreatedBy { get; set; }
public virtual User LastModifiedBy { get; set; }
}
EF会将FK创建为类似User_Id和User_Id2
这会使想要将FK包括在实体中的人绊倒:
public class Order
{
// ...
public int CreatedByUserId { get; set; }
public virtual User CreatedBy { get; set; }
public int LastModifiedByUserId { get; set; }
public virtual User LastModifiedBy { get; set; }
}
...希望能正常工作,然后想知道为什么不填充表中的ID。
要使用更有意义的FK,您需要对其进行配置:
public class Order
{
// ...
[ForeignKey("CreatedBy")]
public int CreatedByUserId { get; set; }
public virtual User CreatedBy { get; set; }
[ForeignKey("LastModifiedBy")]
public int LastModifiedByUserId { get; set; }
public virtual User LastModifiedBy { get; set; }
}
ForeignKey
属性可以放在指向导航属性的FK上,也可以放在指向FK的导航属性上。
但是,我通常建议不在实体中定义FK,因为在更新引用时可能会导致潜在的问题,因为该关系有2个真相。
我使用order.LastModifiedByUserId
还是order.LastModifiedBy.UserId
?如果我想更新用户并且某些代码引用其中一个或另一个,该怎么办?如果更改“用户导航”属性,何时订购的FK会更新?如果我设置FK而不是导航属性怎么办?
如果我要为订单更新LastModifiedBy
:
我可能会这样做:
var order = context.Orders.Single(x => x.OrderId == orderId);
order.LastModifiedByUserId = currentUserId;
但是,我应该这样做:
var currentUser = context.Users.Single(x => x.UserId == currentUserId);
var order = context.Orders.Single(x => x.OrderId == orderId);
order.LastModifiedBy = currentUser;
取决于是否渴望加载LastModifiedBy导航属性,还是取决于DBContext是否可以填充它(因为它已经被加载并且在使订单实体饱和时可以被填充)将决定尝试更新FK的行为。第二个选项是一致的选项,但是请注意,即使在那里,在调用SaveChanges
之前,订单上的任何FK都不会自动更新。
通常,最好完全避免在实体中公开FK。对于EF6,可以使用Map.MapKey
(其中EF Core支持阴影属性)来实现。例如,配置以下实体以定义要使用的FK列名称,但避免公开FK属性:
public class Order
{
// ...
public virtual User CreatedBy { get; set; }
public virtual User LastModifiedBy { get; set; }
}
使用modelBuilder或EntityTypeConfiguration
EF6
.HasRequired(x => x.CreatedBy)
.WithMany()
.Map(x => x.MapKey("CreatedByUserId"));
.HasRequired(x => x.LastModifiedBy)
.WithMany()
.Map(x => x.MapKey("LastModifiedByUserId"));
EF核心
.HasOne(x => x.CreatedBy)
.WithMany()
.HasForeignKey("CreatedByUserId");
.HasOne(x => x.LastModifiedBy)
.WithMany()
.HasForeignKey("LastModifiedByUserId");
/ w EF核心HasForeignKey
可用于为FK列定义阴影属性,而不是指向实体中的暴露属性。这样做的好处是,关联实体的密钥不再有两个真相来源。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。