使用 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() 因为性能很差。但请记住,这就是它一直在做的事情。如果以前没有性能问题,那么现在就没有了。至少现在您知道它实际在做什么,并且可以在您确实需要时修复它。