简体   繁体   English

实体框架数据库表关联

[英]Entity Framework database tables association

I have a domain model like this:我有一个这样的域模型:

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

Entity Framework associated that with two other tables ( Customers and Movies ) and automatically created foreign key columns in Rentals table.实体框架将其与其他两个表( CustomersMovies )相关联,并在Rentals表中自动创建外键列。 In my .NET course I was taught to make associations with other tables with the addition of an Id property, so for instance the Rental domain model would contain these properties:在我的 .NET 课程中,我被教导通过添加Id属性与其他表建立关联,例如, Rental域模型将包含以下属性:

public int MovieId { get; set; }
public int CustomerId { get; set; }

But the code works well without these lines of code.但是代码在没有这些代码行的情况下运行良好。 Can you explain to me which approach is better?你能向我解释一下哪种方法更好吗? Or maybe one of them gives more flexibility?或者其中之一提供了更大的灵活性? I'm new to .NET so every explanation would be welcomed.我是 .NET 的新手,所以每一个解释都会受到欢迎。

First below lines will throw you a syntax error, that's not valid c# code下面的第一行会抛出一个语法错误,这不是有效的 c# 代码

public MovieId { get; set; }
public CustomerId { get; set; }

The second thing EF is smart enough to know the relation between entities, and from use case point of you, it's always better to put Typed property that way when you want to access the information of about customer(Eg Name) you have it really available using rental.Customer.Name and you don't have to pull it separately.第二件事 EF 足够聪明,可以知道实体之间的关系,并且从您的用例来看,当您想访问有关客户的信息(例如姓名)时,最好以这种方式放置 Typed 属性,您确实可以使用它使用rental.Customer.Name ,你不必单独拉它。

When using Code-First where EF is defining your schema, EF will use conventions to create suitable FK columns in the tables and use those behind the scenes.在 EF 定义架构的情况下使用 Code-First 时,EF 将使用约定在表中创建合适的 FK 列并在幕后使用这些列。 You can define your own FK with whatever naming convention you want, but you will need to tell EF what column to use as the FK relationship for what table.您可以使用您想要的任何命名约定定义您自己的 FK,但您需要告诉 EF 使用哪个列作为哪个表的 FK 关系。 This is done by using the [ForeignKey] attribute or configuring the relationship (modelBuilder or EntityTypeConfiguration ) with the foreign key.这是通过使用[ForeignKey]属性或使用外键配置关系(modelBuilder 或EntityTypeConfiguration )来完成的。

By convention EF will define the FK column name by the Type of the related object, which may not be what you want in the schema.按照约定,EF 将通过相关对象的类型定义 FK 列名称,这可能不是您想要的架构。 For example if you have an Order entity which you want references to a User entity for CreatedBy and LastModifiedBy, you might expect to have something like例如,如果您有一个 Order 实体,您希望引用 CreatedBy 和 LastModifiedBy 的 User 实体,您可能希望有类似的东西

public class Order
{
    // ...
    public virtual User CreatedBy { get; set; }
    public virtual User LastModifiedBy { get; set; }
}

EF would create FK as something like: User_Id and User_Id2 EF 会像这样创建 FK:User_Id 和 User_Id2

This can trip up people that want to include the FK in the entity:这可能会绊倒想要在实体中包含 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; }
}

... expecting that to work then wondering why the IDs in the table aren't being populated. ... 期待它的工作然后想知道为什么表中的 ID 没有被填充。

To use more meaningful FKs you need to configure them:要使用更有意义的 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; }
}

The ForeignKey attribute can be put on either the FK pointing at the navigation property or on the navigation property pointing back to the FK. ForeignKey属性可以放在指向导航属性的 FK 上,也可以放在指向 FK 的导航属性上。

However, I generally recommend not defining FKs in the entity as this can lead to potential issues when updating references as there are 2 sources of truth for the relationship.但是,我通常建议不要在实体中定义 FK,因为这可能会在更新引用时导致潜在问题,因为关系有 2 个真实来源。

Do I use order.LastModifiedByUserId or order.LastModifiedBy.UserId ?我使用order.LastModifiedByUserId还是order.LastModifiedBy.UserId What if I want to update a user and some code references one or the other of those?如果我想更新用户并且某些代码引用了其中的一个怎么办? If I change the User navigation property when does the FK on Order get updated?如果我更改用户导航属性,订单上的 FK 何时更新? What if I set the FK and not the navigation property?如果我设置 FK 而不是导航属性怎么办?

If I want to update the LastModifiedBy for an order:如果我想更新订单的LastModifiedBy

I might be tempted to do this:我可能会想这样做:

var order = context.Orders.Single(x => x.OrderId == orderId);
order.LastModifiedByUserId = currentUserId;

However, I should do this:但是,我应该这样做:

var currentUser = context.Users.Single(x => x.UserId == currentUserId);
var order = context.Orders.Single(x => x.OrderId == orderId);
order.LastModifiedBy = currentUser;

Depending on whether the LastModifiedBy navigation property was eager loaded, or the DBContext could populate it (because it was already loaded and could be populated when saturating the order entity) will determine the behaviour of trying to update the FK.根据 LastModifiedBy 导航属性是预先加载还是 DBContext 可以填充它(因为它已经加载并且可以在使订单实体饱和时填充)将决定尝试更新 FK 的行为。 The second option is the consistent one, however note that even there, any FK on Order will not be updated automatically until SaveChanges is called.第二个选项是一致的,但是请注意,即使在那里,在调用SaveChanges之前,Order 上的任何 FK 都不会自动更新。

Generally it is a better idea to avoid exposing FKs in the entity at all.一般来说,完全避免在实体中暴露 FK 是一个更好的主意。 For EF6 this can be accomplished by using Map.MapKey , where EF Core supports Shadow Properties.对于Map.MapKey ,这可以通过使用Map.MapKey来完成,其中 EF Core 支持阴影属性。 For example, to configure the following entity to define the FK column name to use, but avoid exposing a FK property:例如,配置以下实体以定义要使用的 FK 列名称,但避免暴露 FK 属性:

public class Order
{
    // ...
    public virtual User CreatedBy { get; set; }
    public virtual User LastModifiedBy { get; set; }
}

Using modelBuilder or EntityTypeConfiguration使用模型构建器或EntityTypeConfiguration

EF6 EF6

.HasRequired(x => x.CreatedBy)
    .WithMany()
    .Map(x => x.MapKey("CreatedByUserId"));
.HasRequired(x => x.LastModifiedBy)
    .WithMany()
    .Map(x => x.MapKey("LastModifiedByUserId"));

EF Core英孚核心

.HasOne(x => x.CreatedBy)
    .WithMany()
    .HasForeignKey("CreatedByUserId");
.HasOne(x => x.LastModifiedBy)
    .WithMany()
    .HasForeignKey("LastModifiedByUserId");

/w EF Core the HasForeignKey can be used to define a shadow property for the FK column rather than point at an exposed property in the entity. /w EF Core HasForeignKey可用于定义 FK 列的影子属性,而不是指向实体中的公开属性。 The advantage here is that there is no longer two sources of truth for the related entity's key.这里的优点是相关实体的密钥不再有两个真实来源。

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

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