最初,我是使用LINQ-to-SQL编写此查询的

var result = from w in PatternDataContext.Windows
    join cf in PatternDataContext.ControlFocus on w.WindowId equals cf.WindowId
    join p in PatternDataContext.Patterns on cf.CFId equals p.CFId
    join r in ResultDataContext.Results on p.PatternId equals r.PatternId
    join fi in ResultDataContext.IclFileInfos on r.IclFileId equals fi.IclFileId
    join sp in sessionProfileDataContext.ServerProfiles on fi.ServerProfileId equals sp.ProfileId
    join u in infrastructure.Users on sp.UserId equals u.Id
    where w.Process.Equals(processName)
    select u.DistributedAppId;

当我执行它并在QuickWatch中看到result时,它显示以下消息:



在谷歌搜索时,我在Stackoverflow本身上找到了this topic,在那里我学习了模拟跨上下文连接的方法,并且如此处所建议的,我对查询进行了一些更改:
var result = from w in PatternDataContext.Windows
    join cf in PatternDataContext.ControlFocus on w.WindowId equals cf.WindowId
    join p in PatternDataContext.Patterns on cf.CFId equals p.CFId
    join r in SimulateJoinResults() on p.PatternId equals r.PatternId
    join fi in SimulateJoinIclFileInfos() on r.IclFileId equals fi.IclFileId
    join sp in SimulateJoinServerProfiles() on fi.ServerProfileId equals sp.ProfileId
    join u in SimulateJoinUsers() on sp.UserId equals u.Id
    where w.Process.Equals(processName)
    select u.DistributedAppId;

该查询使用以下SimulateXyz方法:
private static IQueryable<Result> SimulateJoinResults()
{
  return from r in SessionDataProvider.Instance.ResultDataContext.Results select r;
}
private static IQueryable<IclFileInfo> SimulateJoinIclFileInfos()
{
  return from f in SessionDataProvider.Instance.ResultDataContext.IclFileInfos select f;
}
private static IQueryable<ServerProfile> SimulateJoinServerProfiles()
{
  return from sp in sessionProfileDataContext.ServerProfiles select sp;
}
private static IQueryable<User> SimulateJoinUsers()
{
  return from u in infrastructureDataContext.Users select u;
}

但是,即使这种方法也不能解决问题。我仍然在QuickWatch中收到此消息...:



这个问题有什么解决办法吗?除了解决方案,我还想知道为什么问题仍然存在,以及新解决方案如何将其彻底消除,以便下次可以自己解决此类问题。顺便说一下,我是LINQ的新手。

最佳答案

我以前必须这样做,并且有两种方法可以做到。

首先是将所有服务器移到单个上下文中。通过将LINQ-to-SQL指向单个服务器来执行此操作,然后在该服务器中为所有其他服务器创建linked servers。然后,您只需为其他服务器上感兴趣的任何表创建 View ,然后将这些 View 添加到您的上下文中。

第二个方法是通过从一个上下文中提取数据,并仅使用需要连接到另一个上下文中的属性来手动进行连接。例如,

int[] patternIds = SessionDataProvider.Instance.ResultDataContext.Results.Select(o => o.patternId).ToArray();
var results = from p in PatternDataContext.Patterns
              where patternIds.Contains(p.PatternId)
              select p;

尽管第一个更易于使用,但确实存在很多问题。问题是您依赖SQL Server在链接服务器上表现出色,这是众所周知的缺点。例如,考虑以下查询:
var results = from p in DataContext.Patterns
              join r in DataContext.LinkedServerResults on p.PatternId equals r.PatternId
              where r.userId = 10;

枚举此查询时,将发生以下情况(我们分别将普通服务器和链接服务器称为MyServerMyLinkedServer)
  • MyServerMyLinkedServer询问结果
  • MyLinkedServer将结果发送回MyServer
  • MyServer接收这些结果,将它们加入Patterns表中,并仅返回具有Results.userId = 10
  • 的结果。

    所以现在的问题是:何时过滤-在MyServerMyLinkedServer上?以我的经验,对于这样一个简单的查询,通常会在MyLinkedServer上完成。但是,一旦查询变得更加复杂,您会突然发现MyServer正在从MyLinkedServer请求整个结果表,并在联接后进行过滤!这会浪费带宽,并且如果“结果”表足够大,则可能会将50ms的查询变成50秒的查询!

    您可以使用存储过程来修复性能不佳的跨服务器联接,但是如果您执行许多复杂的跨服务器联接,则最终可能会为大多数查询编写存储过程,这是很多工作,并且无法达到目的。首先使用L2SQL的过程(不必编写大量SQL)。

    相比之下,以下代码将始终在包含“结果”表的服务器上执行过滤:
    int[] patternIds = (from r in SessionDataProvider.Instance.ResultDataContext.Results
                        where r.userId = 10
                        select r.PatternId).ToArray();
    var results = from p in PatternDataContext.Patterns
                  where patternIds.Contains(p.PatternId)
                  select p;
    

    哪种情况最适合您,取决于您的最佳判断。

    请注意,还有第三种可能的解决方案,我没有提到,因为它并不是真正的程序员解决方案:您可以要求服务器管理员设置replication task,以便每天/每周一次将必要的数据从MyLinkedServer复制到MyServer/月。这仅在以下情况下才是选择:
  • 您的程序可以使用MyLinkedServer的旧数据
  • 您只需要读,而不用写MyLinkedServer
  • 您所需的MyLinkedServers表不是很大的
  • 您有可用的空间/带宽
  • 您的数据库管理员不是小气/懒惰
  • 关于c# - 在LINQ-to-SQL中使用跨上下文联接,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5429469/

    10-11 17:25