在 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.. 中看到结果
时,它显示了以下消息:
查询包含对不同数据上下文中定义的项目的引用
在谷歌搜索上,我发现这个主题< /a> 在 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 新手。
Initially I had written this query using 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;
And when I executed it, and saw result
in the QuickWatch.., it showed this message:
the query contains references to items defined on a different data context
On googling, I found this topic at Stackoverflow itself, where I learned simulating cross context joins and as suggested there, I changed my query a bit to this:
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;
This query is using these SimulateXyz methods:
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;
}
But even this approach didn't solve the problem. I'm still getting this message in QuickWatch...:
the query contains references to items defined on a different data context
Any solution for this problem? Along with the solution, I would also want to know why the problem still exists, and how exactly the new solution removes it, so that from next time I could solve such problems myself. I'm new to LINQ, by the way.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我以前必须这样做,有两种方法可以做到。
第一个是将所有服务器移至单个上下文中。为此,您可以将 LINQ-to-SQL 指向单个服务器,然后在该服务器中创建 链接服务器到所有其他服务器。然后,您只需从其他服务器为您感兴趣的任何表创建视图,并将这些视图添加到您的上下文中。
第二种是自己手动执行连接,方法是从一个上下文中提取数据,然后仅使用连接到另一个上下文所需的属性。例如,
虽然第一个更容易使用,但它也存在一些问题。问题在于您依赖 SQL Server 来提高链接服务器的性能,而这是众所周知的它的不擅长之处。例如,考虑以下查询:
当您枚举此查询时,将发生以下情况(让我们分别将普通服务器和链接服务器称为
MyServer
和MyLinkedServer
)MyServer
向MyLinkedServer
请求结果MyLinkedServer
将结果发送回MyServer
MyServer
获取这些结果,加入它们位于 Patterns 表中,并且仅返回 Results.userId = 10 的结果。现在的问题是:过滤何时完成 - 在
MyServer
或MyLinkedServer
上?根据我的经验,对于这样一个简单的查询,通常会在MyLinkedServer
上完成。但是,一旦查询变得更加复杂,您会突然发现MyServer
正在从MyLinkedServer
请求整个结果表并执行过滤< em>加入后!这会浪费带宽,并且如果结果表足够大,可能会将 50 毫秒的查询变成 50 秒的查询!您可以使用存储过程修复性能不佳的跨服务器联接,但如果您执行大量复杂的跨服务器联接,您最终可能会为大多数查询编写存储过程,这是一项繁重的工作,并且无法达到部分目的首先使用 L2SQL(不必编写大量 SQL)。
相比之下,以下代码总是在包含结果表的服务器上执行过滤:
哪种最适合您的情况取决于您的最佳判断。
请注意,我没有提到第三种潜在的解决方案,因为它并不是真正的程序员解决方案:您可以要求服务器管理员设置 复制任务,每天/每周一次将必要的数据从
MyLinkedServer
复制到MyServer
月。在以下情况下,这仅是一个选项:MyLinkedServer
的稍微陈旧的数据MyLinkedServer
MyLinkedServers
获取的表并不是太大I've had to do this before, and there are two ways to do it.
The first is to move all the servers into a single context. You do this by pointing LINQ-to-SQL to a single server, then, in that server, create linked servers to all the other servers. Then you just create views for any tables you're interested from the other servers, and add those views to your context.
The second is to manually do the joins yourself, by pulling in data from one context, and using just the properties you need to join into another context. For example,
Though the first is easier to work with, it does have its share of problems. The problem is that you're relying on SQL Server to be performant with linked servers, something it is notoriously bad at. For example, consider this query:
When you enumerate this query, the following will occur (let's call the normal and linked servers
MyServer
andMyLinkedServer
, respectively)MyServer
asksMyLinkedServer
for the ResultsMyLinkedServer
sends the Results back toMyServer
MyServer
takes those Results, joins them on the Patterns table, and returns only the ones with Results.userId = 10So now the question is: When is the filtering done - on
MyServer
orMyLinkedServer
? In my experience, for such a simple query, it will usually be done onMyLinkedServer
. However, once the query gets more complicated, you'll suddenly find thatMyServer
is requesting the entire Results table fromMyLinkedServer
and doing the filtering after the join! This wastes bandwidth, and, if the Results tables is large enough, could turn a 50ms query into a 50 second query!You could fix unperformant cross-server joins using stored procedures, but if you do a lot of complex cross-server joins, you may end up writing stored procedures for most of your queries, which is a lot of work and defeats part of the purpose of using L2SQL in the first place (not having to write a lot of SQL).
In comparison, the following code would always perform the filtering on the server containing the Results table:
Which is best for your situation is up to your best judgement.
Note that there is a third potential solution which I did not mention, as it is not really a programmer-solution: you could ask your server admins to set up a replication task to copy the necessary data from
MyLinkedServer
toMyServer
once a day/week/month. This is only an option if:MyLinkedServer
MyLinkedServer
MyLinkedServers
are not exorbitantly huge您的 SimulateJoins 无法工作,因为它们返回
IQueryable
。您当前的解决方案与以前的解决方案完全相同,这就是您得到相同异常的原因。如果您再次检查链接的问题,您将看到它们的辅助方法返回 IEnumerable ,这是进行跨上下文操作的唯一方法。您可能已经知道,这意味着联接将在应用程序服务器而不是数据库服务器的内存中执行=它将从部分查询中提取所有数据并作为 linq-to-objects 执行联接。在我看来,数据库级别的跨上下文连接是不可能的。您可以对不同的服务器有不同的连接、不同的连接字符串等。Linq-to-sql 不处理此问题。
Your SimulateJoins can't work because they return
IQueryable
. Your current solution is exactly the same as your former one and that is the reason why you get the same exception. If you check the linked question again you will see that their helper methods returnIEnumerable
which is the only way to make cross context operations. As you probably already know it means that join will be performed in memory on the application server instead of the database server = it will pull all data from your partial queries and execute join as linq-to-objects.Cross context join on database level is IMO not possible. You can have different connections, different connection strings with different servers, etc. Linq-to-sql does not handle this.
您可以通过在第二个上下文上“转义”Linq to SQL 来解决此问题,即在
ResultDataContext.Results
和ResultDataContext 上调用
以便您的查询最终看起来像:.ToList()
实例.IclFileInfos或者
AsEnumerable()
,只要您“退出”Linq to SQL 并进入 Linq to Objects 以获得“有问题的”上下文。You could work around it by "escaping from" Linq to SQL on the second context, i.e., calling for instance
.ToList()
onResultDataContext.Results
andResultDataContext.IclFileInfos
so that your query ended up looking like:Or
AsEnumerable()
as long as you "get out" of Linq to SQL and into Linq to Objects for the "offending" context.老问题,但由于我碰巧遇到了同样的问题,我的解决方案是通过第一个上下文的 ExecuteQuery 方法将手动制作的 T-SQL 跨服务器查询(带有链接服务器)直接传递给提供者:
这只是节省了你由于必须创建视图服务器端,Linq to SQL 仍然将结果映射到正确的类型。当存在一个无法在 Linq 中制定的查询时,这非常有用。
Old question, but as I happened to have the same problem, my solution was to pass the manually crafted T-SQL cross-server query (with linked servers) directly to the provider through the ExecuteQuery method of the first context:
This just saves you from having to create a view server side, and Linq to SQL still maps the results to the proper type. This is useful when there is that one query that is just impossible to formulate in Linq.