我正在尝试使用代码优先方法使用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; }
}
答案 0 :(得分:1)
使用关系数据库时,数据库中的每个对象都需要一个ID,如果该对象“属于”另一个表中的另一个对象,则需要一个外键。你不能没有它。
幸运的是,实体框架足够聪明,因此,如果设计合理,您将很少使用外键执行(组)联接。您通常使用虚拟属性。
在实体框架中,类表示数据库的表。表中的列由类的非虚拟属性表示。虚拟属性表示类之间的关系。
因此,您有一些BaseEntity
和三种特殊类型的BaseEntities
:Employers
,Employees
和Clients
。
每个Employer
的零个或多个Employees
;每个Employee
都有一个Employer
:一个简单的一对多关系。为此,无论您是否愿意,每个Employee
都需要一个外键指向它所属的Employer
。幸运的是,您不必使用它。
类似地,Employer
和Client
之间存在一对多的关系:每个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:每个获取的雇主都将有一个“客户和雇员”子集合。使用实体框架,您不必自己加入:
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,
...
},
});
实体框架知道您设计了一对多,并将为您做适当的连接。
请注意,没有客户,您将无法找到雇主。