c# - EF 模型类是否正确?

标签 c# entity-framework entity-framework-core

我想先使用 EF 代码来应用现有的数据库表建模。

假设我有三个表。

  1. 项目 表。主键是ProjectId,还有ProjectName、StartDate、EndDate等其他列。
  2. 技术 表。主键为TechnologyId,其他列为technologyName、Note等。
  3. ProjectTechnologyLink。将两个表链接在一起。它具有主键 ProjectId 和 TechnologyId,它们也是外键。

ProjectTechnologyLink 中的示例数据

ProjectId    TechnologyId    CreatedBy  CreatedDate
25            3               One       2016-01-01
100           4               One       2016-01-01
100           8               Two       2016-01-01

假设一个项目可以有多种技术,一种技术可以存在于多个项目中。

模型类是: 对于项目:

public class Project
{
    public int ProjectId {get;set;}
    public string ProjectName {get;set;}
    ...
    public ICollection<ProjectTechnologyLink> ProjectTechnologyLink
}

对于技术:

public class Technology
{ 
    public Technology()
    {
        ProjectTechnologyLink = new HashSet<ProjectTechnologyLink>();
    }
    public int TechnologyId {get;set;}
    public string TechnologyName {get;set;}
    ...
    public ICollection<ProjectTechnologyLink> ProjectTechnologyLink {get;set;}
 }

对于 ProjectTechnologyLink 类。

public class ProjectTechnologyLink 
{
    public int ProjectId {get;set;}
    public int TechnologyId {get;set;}
    ...
    public Project Project {get;set;}
    public Technology Technology {get;set;}
}

然后在 OnModelCreating 方法中。

modelBuilder.Entity<ProjectTechLink>(entity => 
    {
        entity.HasKey(e=> new {e.ProjectId, e.TechnologyId});
        entity.ToTable("ProjectTechnology", "myscheme");
        entity.HasOne(x=>x.Project)
              .WithMany(p=>p.ProjectTechnologyLink)
              .HasForeignKey(d=>d.ProjectId)
              .OnDelete(DeleteBehavior.ClientSetNull)
              .HasConstraintName("FK_PROJECT_TECHNOLOGY_LINK_PID");

       entity.HasOne(x=>x.Technology)
              .WithMany(p=>p.ProjectTechnologyLink)
              .HasForeignKey(d=>d.TechnologyId)
              .OnDelete(DeleteBehavior.ClientSetNull)
              .HasConstraintName("FK_PROJECT_TECHNOLOGY_LINK_TID");
      });

我的问题是 是否适合所有代码?有时我看到人们在类中的属性之前放置一些属性。但我没有。

最佳答案

您的代码非常可以接受,应该能很好地为您服务,但让我们回顾一下为什么混合使用属性和 Fluent API。

EF 管道有三个主要点,我们可以在其中注入(inject)数据库元数据(表配置),这些包括:

  1. 属性符号
  2. 代码优先约定
  3. 流畅的配置 API

上面的顺序也描述了它们在管道中的处理顺序,在内部,属性对 DbContext 没有意义,直到它们被内置(或自定义)约定解析,这些约定在内部使用 Fluent API 来配置数据库上下文。

不同的场景需要并允许不同的混合,通常在 Code First 场景中,属性表示法优于大量使用 Fluent API。然而,包括级联删除或诸如此类的多对多关系的外键约束通常直接在 Fluent API 中表达,因为语法可以更简单一些,并确保没有其他约定可以否决我们在数据库中的预期实现。

Attribute notation allows your table schema to be mostly contained within the class definition, but still allows the application runtime to override the interpretations of these attributes by customising, or disabling built in conventions.

如果您的元数据可以使用属性来表示,那么您的模式的整个规范将变得更加简洁,这在需要显示结构和关系的示例中在线展示您的代码解决方案时特别有用。如果你的在线示例只需要表达关系,那么这些示例通常只会使用流利的符号。 - 如果您需要分别表达模式和关系映射,则可以实现代码示例。

如果您使用属性表示法,那么您的示例可以这样表示,并且您不需要在OnModelCreating 中添加任何额外内容:

public class Project
{
    [Key]
    public int ProjectId { get; set; }
    public string ProjectName { get; set; }
    ...
    public virtual ICollection<ProjectTechnologyLink> ProjectTechnologyLink { get; set; } = new HashSet<ProjectTechnologyLink>();
}

NOTE: In the above Project class definition I have used an inline initializer for the ProjectTechnologyLink relationship, i find this style fits in well with Attribute Notation as the default value is now also defined in close proximity with the Property, when initializers are only only defined in the constructor it is easy to forget to include an init at all or it can be hard to find the init logic in code. Now a quick "Got To Definition" will reveal the default implementation as well as any database schema related attributes without having to lookup other resources.

public class Technology
{ 
    public Technology()
    {
        ProjectTechnologyLink = new HashSet<ProjectTechnologyLink>();
    }
    [Key]
    public int TechnologyId { get; set; }
    public string TechnologyName { get; set; }
    ...
    public virtual ICollection<ProjectTechnologyLink> ProjectTechnologyLink { get; set; }
 }
public class ProjectTechnologyLink 
{
    [Key, Column(Order = 0)]
    public int ProjectId { get; set; }
    [Key, Column(Order = 0)]
    public int TechnologyId { get; set; }
    ...
    [ForeignKey(nameof(ProjectId))]
    public virtual Project Project { get; set; }
    [ForeignKey(nameof(TechnologyId))]
    public virtual Technology Technology { get; set; }
}

虚拟导航属性: 在 EF 中,将导航属性标记为虚拟 成员很重要。这将允许 EF 在生成从您的实体类继承的包装类时实现延迟加载并执行这些属性的其他优化实现。即使您不打算支持延迟加载,在其他情况下 EF 将包装您的类,并且无论哪种方式,您的数据定义类都不应该关心或知道可以做出的操作决策和根据您的上下文需要在运行时更改。

Conventions: The previous example demonstrates pure attribute notation. It is very possible to replace the default Conventions with your own for defining primary and foreign keys. Meaning it is theoretically possible to not have any attributes or Fluent notation at all. I try to discourage a pure convention based approach because it makes it a bit harder to find the configuration in a large or distributed schema definition, which is also the same argument I use to discourage a pure Fluent API approach, attributes are the logical place to document the expected usage of a table or field.

关于c# - EF 模型类是否正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58262622/

相关文章:

javascript - 如何将表单数据从 jQuery 获取到 ASP.NET Controller ?

c# - EF Core LINQ 从包含的实体中排除列

c# - 是否可以使用 Linq To Entities 访问生成的 SQL

entity-framework - 如何在 Entity Framework 核心中重命名迁移

c# - 如何将 ProjectTo 从基本类型转换为仅在运行时已知的类型?

c# - 为什么具有相同元素的 HashSet 在调用 GetHashCode() 时返回不同的值?

c# - C# 中组合框的 KeyPress 事件

c# - 从 GUI 启动控制台应用程序并与之通信

c# - return 内部 using 语句

mysql - 未找到具有不变名称 'MySql.Data.MySqlClient' 的 ADO.NET 提供程序的 Entity Framework 提供程序。在生成 View 时