Entity framework EF core 3使用没有声明性引用完整性的数据库

Entity framework EF core 3使用没有声明性引用完整性的数据库,entity-framework,entity-framework-core,Entity Framework,Entity Framework Core,当我的主数据库没有任何FK时,我们是否有一种简单的方法在代码模型中创建关系,仅用于在我的服务中使用导航或包含在LINQ中!它是由一个旧程序使用的旧数据库,所以我不能在SQL中添加FK ou PK。我使用Scaffold检索我的表: public partial class UsagerEw { public string Code { get; set; } public string Nom { get; set; } public string Email { get

当我的主数据库没有任何FK时,我们是否有一种简单的方法在代码模型中创建关系,仅用于在我的服务中使用导航或包含在LINQ中!它是由一个旧程序使用的旧数据库,所以我不能在SQL中添加FK ou PK。我使用Scaffold检索我的表:

public partial class UsagerEw
{
    public string Code { get; set; }
    public string Nom { get; set; }
    public string Email { get; set; }
    public string ModeLogin { get; set; }
    public string Role { get; set; }
    public string ParamRole { get; set; }
    public string EmpContact { get; set; }
    public byte RestreintCommContrat { get; set; }
    public byte RestreintProjet { get; set; }
    public string Password { get; set; }
    public byte Inactif { get; set; }
    public byte Administrateur { get; set; }
    public string PasswordTemp { get; set; }
    public DateTime? PasswordTempExp { get; set; }
    public int CodeInt { get; set; }
    public int? IdSuperviseur { get; set; }
    public byte? EstSuperviseur { get; set; }
    public byte? LectureSeule { get; set; }
    public byte? DoitChangerPw { get; set; }
    public string PermsGrps { get; set; }
    public string ParamRole2 { get; set; }
    public Guid? Adguid { get; set; }
}

public partial class RefreshToken
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public string Token { get; set; }
    public DateTime Expires { get; set; }
    public DateTime Created { get; set; }
    public string CreatedByIp { get; set; }
    public DateTime? Revoked { get; set; }
    public string RevokedByIp { get; set; }
    public string ReplaceByToken { get; set; }
}
我在考虑使用流畅的代码创建“模型方”关系,如:

modelBuilder.Entity<RefreshToken>(entity =>
        {
            entity.Property(e => e.CreatedByIp)
                .IsRequired()
                .HasMaxLength(50);

            entity.Property(e => e.ReplaceByToken).HasMaxLength(400);

            entity.Property(e => e.RevokedByIp).HasMaxLength(50);

            entity.Property(e => e.Token)
                .IsRequired()
                .HasMaxLength(400);

            entity.HasOne<UsagerEw>(r => r.UsagerEW).WithMany(u => u.RefreshTokens).HasForeignKey(r => r.UsagerEWId);
        });
来匹配我流畅的关系。我得到:SqlException:无效的列名“UsagerEWId”

但我想我需要手动添加一些虚拟属性 实体表?这是一个正确的方法吗

如果不打算使用延迟加载,则不需要为属性添加
virtual
。 脚手架生成部分类,所以只需在同一模型的项目的其他文件中创建相同的类,并添加所需的导航属性

脚手架将其添加到OnModelCreating中,但如果我理解正确, 当我将代码迁移回DB时调用此函数

不,为了正确工作,EF调用了这个函数来配置模型,您需要这个调用。如果您不添加迁移或通过EF函数重新创建数据库,例如
context.database.EnsureCreated()
,则不会发生任何Schama更改。
因此,
OnModelCreating
函数很重要。如果下一个Scaffold删除了您的代码-您必须以某种方式将其返回(不是EF方面的大专家,也许社区可以提出更好的解决方案)。

实体框架需要配置一个键,以了解如何唯一地标识行。这实际上不需要是表上的主键,它可以由可以唯一区分行和行的列组成。因此,在您的USAGREW表中,如果电子邮件在用户之间是唯一的(他们登录的方式),那么可以将其定义为密钥,即使它不是数据库中的PK。如果它采用列的组合,则可以将all定义为键并指定列顺序。(即使用电子邮件和代码)

导航属性用于实体到实体的关系。这些对于LINQ查询在实体之间建立关系(FK)非常有用,因此您可以在实体之间进行查询,而无需显式连接表达式。如果你只关心关系,它们是可选的

例如,如果我有一个订单,并且订单有一个订单状态:

我可以将订单定义为:

public class Order
{
    [Key]
    public int OrderId { get; set; }
    // ...

    [ForeignKey("OrderStatus")]
    public int OrderStatusId { get; set; }
    public OrderStatus Status { get; set; }
}
通过这种方式,我可以在订单中加载OrderStatus:

var order = context.Orders.Include(x => x.OrderStatus).Single(x => x.OrderId == orderId);
。。。这样我就可以获得OrderStatus属性,比如
order.OrderStatus.Name
。导航属性不需要是
虚拟的
,但这意味着如果我尝试访问OrderStatus而不立即加载它(`.Include(x=>x.OrderStatus)),那么我可能会得到一个NullReferenceException。如果EF碰巧缓存了OrderStatus,它将填充OrderStatus。这可能会导致一些不一致和有条件的异常

通过将属性声明为
virtual
,如果我们忘记使用
Include
并访问该属性,EF将在必要时再次转到DB,前提是Order对象仍在其DbContext的范围内。(上下文未处理)这有助于避免意外的异常,但如果滥用,通常会对性能造成重大影响。(懒散的负载)

默认情况下,我建议将属性声明为虚拟属性,因为通常最好知道代码应该一致地工作(并调查性能问题),而不是让它在异常不一致的情况下可能失败。总的来说,我建议使用Linq投影来填充视图模型/DTO,使用
Select
哪个EF将自动组成相关连接,而无需急加载或延迟加载

如果我们真的不关心通过订单从OrderStatus访问属性,我们可以只映射FK,而不使用导航属性。对于OrderStatus之类的东西,我们可以使用一个
enum
,它在代码中具有足够的描述性,如果只有少数几个状态,这些状态可以由应用程序预加载和缓存,以便在需要时查询,如描述等

public class Order
{
    [Key]
    public int OrderId { get; set; }
    //...

    public int OrderStatusId { get; set; }
}
对于现有数据库的配置,您有3个选项:

  • 注释/w实体定义中的属性
  • DbContext.OnModelCreating
  • 使用
    IEntityTypeConfiguration
    实现类并将它们注册到
    DbContext.OnModelCreating

  • 对于简单模式和EF管理模式,属性通常就足够了,modelBuilder处理非默认内容。对于具有大量关系的更大或更复杂的模式,我更喜欢使用
    IEntityTypeConfiguration
    ,因为它有助于保持配置的有序性。

    Ok,但我尝试在OnMOdelCreating中设置一个断点,然后在startup.cs文件中使用服务实例化我的上下文。AddDbContext(…),然后使用和一个对DB的小查询。从未调用断点。您认为使用include和virtual属性会对性能产生很大影响吗?您更喜欢使用JOIN吗?仅仅拥有虚拟或其他导航属性本身不会对性能产生任何影响。人们被抓住的地方不是有意就是无意地依赖于惰性加载。(导致“急切加载所有内容”和/或“关闭延迟加载”等决策)如果您在选择时正确使用投影,则导航属性将导致非常高效且性能良好的查询/w它们各自的连接将自动为您完成。我默认确保nav属性是虚拟的,以避免异常,我使用分析器进行调试,这有助于突出显示查询问题。我试图添加导航属性,但出现了错误。见第1版。有什么建议吗?感谢实体中的字段需要映射到数据表中的列。RefreshToken有一个UserId列,但不清楚这是如何实现的
    var order = context.Orders.Include(x => x.OrderStatus).Single(x => x.OrderId == orderId);
    
    public class Order
    {
        [Key]
        public int OrderId { get; set; }
        //...
    
        public int OrderStatusId { get; set; }
    }