无外键使用EF Core Code First

时间:2018-11-05 15:46:14

标签: entity-framework foreign-keys migration code-first ef-core-2.1

我正在尝试使用代码优先方法使用EF Core 2.1设计应用程序,但是我不希望在数据库中使用外键。我发现使用FK确实很痛苦,尤其是在尝试运行dotnet ef database drop时。运行此命令时出错,因为我的数据库中有外键。

我只想不必担心外键,而只需将具有Id属性的表用于相关项目即可。如果/当我需要相关信息时,我将从数据库中获取相关项目。

   public class Employer : BaseEntity
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public ICollection<Employee> Employees { get; set; }
        public ICollection<Client> Clients { get; set; }
    }

public class Employee : BaseEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public EmployeeType EmployeeType { get; set; }
    public Guid EmployerId { get; set; }
    public Employer Employer { get; set; }
}
public class Client : BaseEntity
    {
        public string Name { get; set; }
        public int EmployerId { get; set; }
        public Employer Employer { get; set; }

    }

1 个答案:

答案 0 :(得分:1)

使用关系数据库时,数据库中的每个对象都需要一个ID,如果该对象“属于”另一个表中的另一个对象,则需要一个外键。你不能没有它。

幸运的是,实体框架足够聪明,因此,如果设计合理,您将很少使用外键执行(组)联接。您通常使用虚拟属性。

  

在实体框架中,类表示数据库的表。表中的列由类的非虚拟属性表示。虚拟属性表示类之间的关系。

因此,您有一些BaseEntity和三种特殊类型的BaseEntitiesEmployersEmployeesClients

每个Employer的零个或多个Employees;每个Employee都有一个Employer:一个简单的一对多关系。为此,无论您是否愿意,每个Employee都需要一个外键指向它所属的Employer。幸运的是,您不必使用它。

类似地,EmployerClient之间存在一对多的关系:每个Employer的零个或多个Clients,每个Client都属于正好是一个Employer,并使用外键。

使用实体framework code first conventions,您的类将需要稍作更改:表之间的关系应标记为虚拟。

class Employer : BaseEntity
{
    public int Id {get; set;}

    // every Employer has zero or more Employees:
    public virtual ICollection<Employee> Employees { get; set; }

    // every Employer has zero or more Clients:
    public virtual ICollection<Client> Clients { get; set; }

    ...
}

class Employee : BaseEntity
{
    public int Id {get; set;}

    // every Employee belongs to exactly one Employer, using a foreign key:
    public int EmployerId {get; set;}
    public virtual Employer Employer { get; set; }

    ...
}

class Client : BaseEntity
{
    public int Id {get; set;}

     // Every Client belongs to exactly one Employer, using foreign key:
     public int EmployerId { get; set; }
     public virtual  Employer Employer { get; set; }

     ...
}

重要的变化是表之间的关系被标记为虚拟

注意:这可能是您不希望整数用作主键。那样不会改变想法。

要执行(分组)联接表的查询,则无需使用外键,

GroupJoin

  

将所有(或部分)雇主和所有(或部分)他的客户和雇员给我。

为此,您将使用GroupJoin:每个获取的雇主都将有一个“客户和雇员”子集合。使用实体框架,您不必自己加入:

var result = dbContext.Employers    // From every employer in the sequence ofall Employers
    .Where(employer => ...)         // or only some of them
    .Select(employer => new         // make one new object
    {
        // select only the properties you actually plan to use
        Id = employer.Id,
        Name = employer.Name,

        Employees = employer.Employees
            .Where(employee => ...)         // only if you do not want all Employees
            .Select(employee => new
            {    
                 // again, select only the properties you plan to use
                 Id = employee.Id,
                 Name = employee.Name,
                 ...
            })
            .ToList(),
        Clients = employer.Clients
            .Where(client=> ...)         // only if you do not want all Clients
            .Select(client => new
            {
                 ...
            })
            .ToList(),
    });

实体框架知道您设计了一对多并会为您做适当的连接。尽管您没有提到任何外键,但是实体框架知道涉及哪些外键。

请注意,通过这种方式,您还将获得没有客户或雇员的雇主

内部加入

如果您不希望“带有其子对象的对象”(GroupJoin),而又希望获得平坦的结果(更像是Join),请从子对象开始:

  

将所有(或部分)客户与他们的雇主给我

var result = dbContext.Clients.Select(client => new
{
     Id = client.Id,
     Name = client.Name,
     ...

     Employer = new
     {
          Id = client.Employer.Id,
          ...
     },
});

实体框架知道您设计了一对多,并将为您做适当的连接。

请注意,没有客户,您将无法找到雇主。