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

时间:2011-03-25 07:00:27

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

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

4 个答案:

答案 0 :(得分:7)

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

首先是将所有服务器移动到单个上下文中。您可以通过将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
  4. 的结果。

    现在问题是:何时完成过滤 - 在MyServerMyLinkedServer?根据我的经验,对于这样一个简单的查询,通常会在MyLinkedServer上完成。但是,一旦查询变得更复杂,您会突然发现MyServer正在从MyLinkedServer请求整个结果表并在之后执行过滤 >加入!这会浪费带宽,如果结果表足够大,可以将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;
    

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


    请注意,我没有提及第三种可能的解决方案,因为它不是真正的程序员解决方案:您可以要求服务器管理员设置replication task以从{{复制必要的数据每天/每周/每月1}}到MyLinkedServer。如果出现以下情况,这只是 选项:

    • 您的程序可以使用来自MyServer
    • 的稍微陈旧的数据
    • 您只需阅读{永远不要写'MyLinkedServer
    • MyLinkedServer所需的表格并不是非常庞大
    • 您有空间/带宽可用
    • 您的数据库管理员不是吝啬/懒惰

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

您可以通过在第二个上下文中“从Linq转移到SQL”来解决它,即在.ToList()ResultDataContext.Results上调用实例ResultDataContext.IclFileInfos,以便您的查询最终查找像:

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以获取“违规”上下文。

答案 3 :(得分:0)

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

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

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