我想先使用EF代码来应用现有的数据库表建模。
假设我有三个桌子。
Project
表。主键是ProjectId,它具有其他列,例如ProjectName,StartDate和EndDate等。Technology
表。主键是TechnologyId,其他列是technologyName,Note等。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");
});
我的问题是 所有代码都正确吗?有时我看到人们在类的属性之前放置了一些属性。但是我没有。
答案 0 :(得分:1)
您的代码是可以接受的,应该可以很好地为您服务,但是让我们回顾一下为什么其中混用了属性和Fluent API。
EF管道具有三个主要方面,我们可以在其中注入数据库元数据(表配置),包括:
上面的顺序还描述了它们在管道中处理的顺序,在内部这些属性对DbContext毫无意义,直到它们被内置(或自定义)约定解析,这些约定在内部使用Fluent API来配置DbContext。
不同的方案要求并允许不同的混合,通常在 Code First 方案中,与大量使用Fluent API相比,属性表示法更为可取。但是,外键约束(包括级联删除或诸如此类的多对多关系)通常在Fluent API中直接表达,因为其语法可能会更简单,并确保没有其他约定可以推翻我们在数据库中的预期实现。 / p>
属性表示法使您的表模式主要包含在类定义中,但仍允许应用程序运行时通过自定义或禁用内置约定来覆盖这些属性的解释。
如果可以使用属性来表示元数据,那么架构的整个规范将变得更加简洁,当在结构和关系非常重要的示例中在线展示代码解决方案时,这特别有用。如果您的在线示例仅需要表达这种关系,那么这些示例通常只会使用流利的表示法。 -如果您需要分别表示架构和关系映射,则可以实现示例代码。
如果使用属性符号,那么您的示例可以这样表示,并且 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>();
}
注意::在上面的
Project
类定义中,我为ProjectTechnologyLink
关系使用了内联初始化程序,我发现这种样式与 Attribute Notation非常吻合,因为默认值现在也与Property紧密相关,当仅在构造函数中定义初始化程序时,很容易忘记完全包含 init ,或者可以很难在代码中找到初始化逻辑。现在,快速的“转到定义”将显示默认实现以及与数据库模式相关的所有属性,而无需查找其他资源。
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中,将导航属性标记为 virtual 成员很重要。这将允许EF在生成从您的实体类继承的包装器类时执行延迟加载并执行这些属性的其他优化实现。即使您不打算支持 Lazy Load ,在其他情况下,EF也会包装您的类,并且无论哪种方式都不必考虑数据定义类或不知道可以做出的操作决策,并且在运行时根据您的上下文需要进行了更改。
公约: 前面的示例演示了纯属性符号。可以使用默认的 Conventions 替换您自己的默认主键和外键。这意味着从理论上讲完全没有任何属性或Fluent符号是可能的。我尝试不鼓励使用基于纯约定的方法,因为这样会使在大型或分布式模式定义中查找配置变得更加困难,这也是我不鼓励使用纯Fluent API方法所使用的相同参数,属性是实现逻辑的地方记录表或字段的预期用法。