给定一个包含两个表的数据库模式:Company
和Employee
,其中Employee
表具有Company
表的外键,正确的代码是什么?改变与特定员工相关的公司?
方法1:
company1.Employees.Remove(employee);
company2.Employees.Add(employee);
问题:
这似乎有效,但由于
company1.Employees
的引用加载了company1
的所有员工,并且对company2.Employees
的引用加载了{{1}的所有员工,因此非常低效。 }}
方法2:
company2
问题:
引发异常:违反了多重性约束。角色'公司'关系' XXX.FK_Employee_Companies'具有多重性1或0..1。
方法3:
employee.Company = company2;
问题:
对我来说,这不会改变数据库中
employee.CompanyId = company2.Id;
列的值。它仍具有以前的价值。
我在使用我的方法1 的大型应用程序中使用经过良好测试的代码,并且在找到性能问题后尝试对其进行优化。我知道可能有其他因素影响我得到的结果。我正在寻找能够更深入了解实体框架内部工作原理的人,以建议他们如何处理这一问题。
修改
我已经被不止一个人告知我的方法1 没有加载所有相关员工。但我已经对此进行了多次测试,表明它确实如此。我甚至创建了一个简单的测试应用程序。这是我的代码。
CompanyId
似乎有些人认为第二行只会从var website = context.Websites.FirstOrDefault();
var ranking = website.Rankings.FirstOrDefault();
表中加载一行。但是,以下是生成的查询:
Rankings
和
SELECT TOP (1)
[c].[ID] AS [ID],
[c].[Title] AS [Title],
[c].[URL] AS [URL],
[c].[AliasForID] AS [AliasForID],
[c].[ActiveState] AS [ActiveState],
[c].[Alert] AS [Alert]
FROM [dbo].[Website] AS [c]
注意第二个查询。它返回exec sp_executesql N'SELECT
[Extent1].[ID] AS [ID],
[Extent1].[WebsiteID] AS [WebsiteID],
[Extent1].[PageRank] AS [PageRank],
[Extent1].[AlexaRank] AS [AlexaRank],
[Extent1].[AlexaReach] AS [AlexaReach],
[Extent1].[DateAdded] AS [DateAdded]
FROM [dbo].[Ranking] AS [Extent1]
WHERE [Extent1].[WebsiteID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
表中链接到Ranking
实体的所有行。它不会过滤只返回第一行。以同样的方式,访问Website
以调用company1.Employees
方法将加载与Add()
相关的所有员工,因为company1
的类型为Website.Rankings
而不是{{1} }}
延迟加载完全不相关。延迟加载只是意味着在代码引用此集合(或引用相关表中的数据的任何其他属性)之前不会加载集合。但是,一旦代码引用此集合,就会加载所有相关实体。
我希望有人能告诉我这是不真实的,或者如何防止这种情况发生。到目前为止,只有那些说它不真实的人不愿意支持它,或者当我要求他们支持他们的要求时侮辱我。不了解其工作原理会导致严重的性能问题。
答案 0 :(得分:1)
我通常会执行方法3 ,如果您的实体关系是正确构建的,那么它是正确的。例如,看看这个结构:
public class Employee
{
public int Id { get; set; }
//.... Other employee properties like name, last name, etc...
public int CompanyId {get; set;}
[ForeignKey("CompanyId")]
public Company Company { get; set;}
}
public class Company
{
public int Id { get; set; }
//.... Other company properties like name, type, etc...
public virtual ICollection<Employee> Employees { get; set; }
}
在为员工更改公司后使用DbContext.SaveChanges()应该可以解决问题:
Employee employee = db.Employees.Find(123); // Assume CompanyId = 3 for this employee
employee.CompanyId = 5;
db.SaveChanges(); // Employee's CompanyId should be changed