[英]Entity Framework database tables association
我有一个这样的域模型:
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 足够聪明,可以知道实体之间的关系,并且从您的用例来看,当您想访问有关客户的信息(例如姓名)时,最好以这种方式放置 Typed 属性,您确实可以使用它使用rental.Customer.Name
,你不必单独拉它。
在 EF 定义架构的情况下使用 Code-First 时,EF 将使用约定在表中创建合适的 FK 列并在幕后使用这些列。 您可以使用您想要的任何命名约定定义您自己的 FK,但您需要告诉 EF 使用哪个列作为哪个表的 FK 关系。 这是通过使用[ForeignKey]
属性或使用外键配置关系(modelBuilder 或EntityTypeConfiguration
)来完成的。
按照约定,EF 将通过相关对象的类型定义 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
之前,Order 上的任何 FK 都不会自动更新。
一般来说,完全避免在实体中暴露 FK 是一个更好的主意。 对于Map.MapKey
,这可以通过使用Map.MapKey
来完成,其中 EF Core 支持阴影属性。 例如,配置以下实体以定义要使用的 FK 列名称,但避免暴露 FK 属性:
public class Order
{
// ...
public virtual User CreatedBy { get; set; }
public virtual User LastModifiedBy { get; set; }
}
使用模型构建器或EntityTypeConfiguration
EF6
.HasRequired(x => x.CreatedBy)
.WithMany()
.Map(x => x.MapKey("CreatedByUserId"));
.HasRequired(x => x.LastModifiedBy)
.WithMany()
.Map(x => x.MapKey("LastModifiedByUserId"));
英孚核心
.HasOne(x => x.CreatedBy)
.WithMany()
.HasForeignKey("CreatedByUserId");
.HasOne(x => x.LastModifiedBy)
.WithMany()
.HasForeignKey("LastModifiedByUserId");
/w EF Core HasForeignKey
可用于定义 FK 列的影子属性,而不是指向实体中的公开属性。 这里的优点是相关实体的密钥不再有两个真实来源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.