使用 EF Core 的跨数据库查询 = 否/将上下文实体实例化为异步列表 = 有效 - 我应该采用什么真正的方法?
CrossDB queries with EFCore = no / instantiating context entites as async lists = works - What is the true approach should I be taking?
我的系统有两个正在使用的数据库服务器:
其中之一是数据库优先 - 由遗留企业应用程序管理的数据库,我无法完全控制更改数据库结构。
第二个是代码优先,我可以完全控制代码优先数据库来进行更改。
安全策略阻止我创建一个视图,该视图将代码优先数据库中的两个数据库服务器的表连接起来,根据我在 SO posts 上看到的内容,这可能是一种改进方法.
每个数据库都有一个上下文,因为它们是独立的。
代码优先表中的数据和结构旨在能够连接到非代码优先数据库,就好像它们都在一个数据库中一样。
我可以使用这组查询得到我需要的工作:
// Set up EF tables
var person = await _context1.Person.ToListAsync();
var labor = await _context1.Labor.ToListAsync();
var laborCraftRate = await context1.LaborCraftRate.ToListAsync();
var webUsers = await context2.WebUsers.ToListAsync();
var workOrders = await _context1.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC")
.ToListAsync();
var specialRequests = await _context1.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now)
.ToListAsync();
var distributionListQuery = (
from l in labor
from p in person.Where(p => p.Personid == l.Laborcode).DefaultIfEmpty()
from wu in webUsers.Where(wu => wu.Laborcode == l.Laborcode).DefaultIfEmpty()
from lcr in laborCraftRate.Where(lcr => lcr.Laborcode == l.Laborcode).DefaultIfEmpty()
select new
{
Laborcode = l.Laborcode,
Displayname = p.Displayname,
Craft = lcr.Craft,
Crew = l.Crewid,
Active = wu.Active,
Admin = wu.FrIsAdmin,
FrDistLocation = wu.FrDistLocation,
}).Where(r => r.Active == "Y" && (r.FrDistLocation == "IPC" || r.FrDistLocation == "IPC2" || r.FrDistLocation == "both"))
.OrderBy(r => r.Craft)
.ThenBy(r => r.Displayname);
// Build a subquery for the next query to use
var ptoSubQuery =
from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
select new
{
workOrder.Wonum,
Laborcode = workOrder.Wolablnk,
specialRequest.Requestdate
};
// Build the PTO query to join with the distribution list
var ptoQuery =
from a in ptoSubQuery
group a by a.Wonum into g
select new
{
Wonum = g.Key,
StartDate = g.Min(x => x.Requestdate),
EndDate = g.Max(x => x.Requestdate),
Laborcode = g.Min(x => x.Laborcode)
};
// Join the distribution list and the object list to return
// list items with PTO information
var joinedQuery = from dl in distributionListQuery
join fl in ptoQuery on dl.Laborcode equals fl.Laborcode
select new
{
dl.Laborcode,
dl.Displayname,
dl.Craft,
dl.Crew,
dl.Active,
dl.Admin,
dl.FrDistLocation,
fl.StartDate,
fl.EndDate
};
// There are multiple records that result from the join,
// strip out all but the first instance of PTO for all users
var distributionList = joinedQuery.GroupBy(r => r.Laborcode)
.Select(r => r.FirstOrDefault()).OrderByDescending(r => r.Laborcode).ToList();
再次,这有效并在一个合理但显然不是最佳的时间范围内取回我的数据,我可以在需要它的 UI 中使用它,方法是在需要数据之前预加载数据。不是最好的,但有效。
如果我将变量声明更改为非异步,我被告知我应该在另一个 SO post 中执行,这将变成跨数据库查询并且 netcore 说不:
// Set up EF tables
var person = _context1.Person;
var labor = _context1.Labor;
var laborCraftRate = context1.LaborCraftRate;
var webUsers = context2.WebUsers;
var workOrders = _context1.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC");
var specialRequests = _context1.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now);
添加 ToListAsync() 可以实现我需要的连接功能。
问 - 谁能详细说明我正在做的事情可能存在的缺点和问题?
谢谢你帮我理解!
并不是说调用ToList()
“不起作用”。问题是它实现了(我认为这是正确的词)查询和 returns 可能比客户端预期的数据量更大。任何进一步的 LINQ 操作都在客户端完成。这会增加数据库和网络的负载。在您的情况下,它有效 因为 您将所有数据带到了客户端。到那时,它是一个 cross-database 查询就不再重要了。
这是从 .NET Core 2.x 到 3.x 过渡期间经常出现的问题。如果无法在服务器端执行某个操作,.NET Core 2.x 将静默插入类似 ToList()
的内容。 (好吧,不是完全默默地。我认为它被记录在某个地方。但许多开发人员并没有意识到它。)3.x 停止这样做并会给你一个错误。当人们尝试升级到 3.x 时,他们经常发现很难将查询转换为可以 运行 服务器端的内容。人们拒绝输入明确的 ToList()
因为性能很差。但请记住,这就是它一直在做的事情。如果以前没有性能问题,那么现在就没有了。至少现在您知道它实际在做什么,并且可以在您确实需要时修复它。
我的系统有两个正在使用的数据库服务器:
其中之一是数据库优先 - 由遗留企业应用程序管理的数据库,我无法完全控制更改数据库结构。
第二个是代码优先,我可以完全控制代码优先数据库来进行更改。
安全策略阻止我创建一个视图,该视图将代码优先数据库中的两个数据库服务器的表连接起来,根据我在 SO posts 上看到的内容,这可能是一种改进方法.
每个数据库都有一个上下文,因为它们是独立的。
代码优先表中的数据和结构旨在能够连接到非代码优先数据库,就好像它们都在一个数据库中一样。
我可以使用这组查询得到我需要的工作:
// Set up EF tables
var person = await _context1.Person.ToListAsync();
var labor = await _context1.Labor.ToListAsync();
var laborCraftRate = await context1.LaborCraftRate.ToListAsync();
var webUsers = await context2.WebUsers.ToListAsync();
var workOrders = await _context1.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC")
.ToListAsync();
var specialRequests = await _context1.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now)
.ToListAsync();
var distributionListQuery = (
from l in labor
from p in person.Where(p => p.Personid == l.Laborcode).DefaultIfEmpty()
from wu in webUsers.Where(wu => wu.Laborcode == l.Laborcode).DefaultIfEmpty()
from lcr in laborCraftRate.Where(lcr => lcr.Laborcode == l.Laborcode).DefaultIfEmpty()
select new
{
Laborcode = l.Laborcode,
Displayname = p.Displayname,
Craft = lcr.Craft,
Crew = l.Crewid,
Active = wu.Active,
Admin = wu.FrIsAdmin,
FrDistLocation = wu.FrDistLocation,
}).Where(r => r.Active == "Y" && (r.FrDistLocation == "IPC" || r.FrDistLocation == "IPC2" || r.FrDistLocation == "both"))
.OrderBy(r => r.Craft)
.ThenBy(r => r.Displayname);
// Build a subquery for the next query to use
var ptoSubQuery =
from webUser in webUsers
join workOrder in workOrders on webUser.Laborcode equals workOrder.Wolablnk
join specialRequest in specialRequests on workOrder.Wonum equals specialRequest.Wonum
select new
{
workOrder.Wonum,
Laborcode = workOrder.Wolablnk,
specialRequest.Requestdate
};
// Build the PTO query to join with the distribution list
var ptoQuery =
from a in ptoSubQuery
group a by a.Wonum into g
select new
{
Wonum = g.Key,
StartDate = g.Min(x => x.Requestdate),
EndDate = g.Max(x => x.Requestdate),
Laborcode = g.Min(x => x.Laborcode)
};
// Join the distribution list and the object list to return
// list items with PTO information
var joinedQuery = from dl in distributionListQuery
join fl in ptoQuery on dl.Laborcode equals fl.Laborcode
select new
{
dl.Laborcode,
dl.Displayname,
dl.Craft,
dl.Crew,
dl.Active,
dl.Admin,
dl.FrDistLocation,
fl.StartDate,
fl.EndDate
};
// There are multiple records that result from the join,
// strip out all but the first instance of PTO for all users
var distributionList = joinedQuery.GroupBy(r => r.Laborcode)
.Select(r => r.FirstOrDefault()).OrderByDescending(r => r.Laborcode).ToList();
再次,这有效并在一个合理但显然不是最佳的时间范围内取回我的数据,我可以在需要它的 UI 中使用它,方法是在需要数据之前预加载数据。不是最好的,但有效。
如果我将变量声明更改为非异步,我被告知我应该在另一个 SO post 中执行,这将变成跨数据库查询并且 netcore 说不:
// Set up EF tables
var person = _context1.Person;
var labor = _context1.Labor;
var laborCraftRate = context1.LaborCraftRate;
var webUsers = context2.WebUsers;
var workOrders = _context1.Workorder
.Where(r => r.Status == "LAPPR" || r.Status == "APPR" || r.Status == "REC");
var specialRequests = _context1.SwSpecialRequest
.Where(r => r.Requestdate > DateTime.Now);
添加 ToListAsync() 可以实现我需要的连接功能。
问 - 谁能详细说明我正在做的事情可能存在的缺点和问题?
谢谢你帮我理解!
并不是说调用ToList()
“不起作用”。问题是它实现了(我认为这是正确的词)查询和 returns 可能比客户端预期的数据量更大。任何进一步的 LINQ 操作都在客户端完成。这会增加数据库和网络的负载。在您的情况下,它有效 因为 您将所有数据带到了客户端。到那时,它是一个 cross-database 查询就不再重要了。
这是从 .NET Core 2.x 到 3.x 过渡期间经常出现的问题。如果无法在服务器端执行某个操作,.NET Core 2.x 将静默插入类似 ToList()
的内容。 (好吧,不是完全默默地。我认为它被记录在某个地方。但许多开发人员并没有意识到它。)3.x 停止这样做并会给你一个错误。当人们尝试升级到 3.x 时,他们经常发现很难将查询转换为可以 运行 服务器端的内容。人们拒绝输入明确的 ToList()
因为性能很差。但请记住,这就是它一直在做的事情。如果以前没有性能问题,那么现在就没有了。至少现在您知道它实际在做什么,并且可以在您确实需要时修复它。