【问题标题】:Working with Cross Context Joins in LINQ-to-SQL在 LINQ-to-SQL 中使用跨上下文联接
【发布时间】:2011-07-22 17:11:40
【问题描述】:

最初我使用 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 的新手。

【问题讨论】:

    标签: c# database linq linq-to-sql datacontext


    【解决方案1】:

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

    第一个是将所有服务器移动到一个上下文中。为此,您可以将 LINQ-to-SQL 指向单个服务器,然后在该服务器中为所有其他服务器创建 linked servers。然后,您只需从其他服务器为您感兴趣的任何表创建视图,并将这些视图添加到您的上下文中。

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

    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

    1. MyServerMyLinkedServer 询问结果
    2. MyLinkedServer 将结果发送回MyServer
    3. MyServer 获取这些结果,将它们连接到 Patterns 表中,并仅返回 Results.userId = 10 的结果

    所以现在的问题是:过滤何时完成 - 在 MyServerMyLinkedServer 上?根据我的经验,对于这样一个简单的查询,通常会在MyLinkedServer 上完成。但是,一旦查询变得更复杂,您会突然发现MyServer 正在从MyLinkedServer 请求整个结果表,并在连接之后进行过滤!这会浪费带宽,而且如果结果表足够大,可能会将 50 毫秒的查询变成 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 提供的您需要的表格并不算太大
    • 您有可用的空间/带宽
    • 您的数据库管理员并不吝啬/懒惰

    【讨论】:

    • 请注意,如果 patternId-array 超过 2000 个参数,SQL Server 将向您抛出异常。
    【解决方案2】:

    您的 SimulateJoins 无法工作,因为它们返回 IQueryable。您当前的解决方案与您以前的解决方案完全相同,这就是您遇到相同异常的原因。如果您再次检查链接的问题,您将看到他们的辅助方法返回IEnumerable,这是进行跨上下文操作的唯一方法。您可能已经知道,这意味着连接将在应用程序服务器而不是数据库服务器上的内存中执行 = 它将从您的部分查询中提取所有数据并作为 linq-to-objects 执行连接。

    数据库级别的跨上下文连接在 IMO 是不可能的。你可以有不同的连接,不同的服务器有不同的连接字符串等。Linq-to-sql 不处理这个。

    【讨论】:

      【解决方案3】:

      您可以通过在第二个上下文中“从”Linq“转义”到 SQL 来解决这个问题,例如,在 ResultDataContext.ResultsResultDataContext.IclFileInfos 上调用 .ToList(),以便您的查询最终看起来像:

      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.ToList() 
              on p.PatternId equals r.PatternId
          join fi in ResultDataContext.IclFileInfos.ToList() 
              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;
      

      AsEnumerable(),只要您“退出” Linq to SQL 并进入 Linq to Objects 以获取“违规”上下文。

      【讨论】:

        【解决方案4】:

        老问题,但我碰巧有同样的问题,我的解决方案是通过第一个上下文的 ExecuteQuery 方法将手动制作的 T-SQL 跨服务器查询(带有链接服务器)直接传递给提供者:

        db.ExecuteQuery(Of cTechSupportCall)(strSql).ToList
        

        这只是让您不必创建视图服务器端,Linq to SQL 仍然将结果映射到正确的类型。当有一个查询无法在 Linq 中制定时,这很有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-18
          • 2010-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-09
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多