繁体   English   中英

在LINQ-to-SQL中使用交叉上下文连接

[英]Working with Cross Context Joins in LINQ-to-SQL

最初我使用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本身找到了这个主题 ,在那里我学习了模拟交叉上下文连接,并在那里建议,我改变了我的查询到这一点:

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指向单个服务器,然后在该服务器中为所有其他服务器创建链接服务器。 然后,您只需为其他服务器感兴趣的表创建视图,并将这些视图添加到您的上下文中。

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

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 根据我的经验,对于这样一个简单的查询,它通常会在MyLinkedServerMyLinkedServer 但是,一旦查询变得更复杂,您将突然发现MyServer正在从MyLinkedServer请求整个 Results表并在加入进行过滤! 这会浪费带宽,如果结果表足够大,可以将50ms查询转换为50秒查询!

您可以使用存储过程修复不可信的跨服务器连接,但是如果您执行大量复杂的跨服务器连接,您可能最终会为大多数查询编写存储过程,这是很多工作并且会破坏部分目的首先使用L2SQL (不必编写大量SQL)

相比之下,以下代码将始终在包含Results表的服务器上执行过滤:

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;

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


请注意,我没有提到第三种可能的解决方案,因为它不是真正的程序员解决方案:您可以要求服务器管理员设置复制任务,以便每天/每周将必要的数据从MyLinkedServer复制到MyServer /月。 只是一个选项,如果:

  • 您的程序可以使用来自MyLinkedServer稍微陈旧的数据
  • 您只需要阅读,永远不要写入MyLinkedServer
  • MyLinkedServers所需的表格并不过分
  • 您有可用的空间/带宽
  • 您的数据库管理员不吝啬/懒惰

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

数据库级别的跨上下文连接是IMO无法实现的。 您可以使用不同的连接,不同的连接字符串和不同的服务器等.Linq-to-sql不处理此问题。

您可以通过在第二个上下文中“从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以获取“违规”上下文。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM