Работа с перекрестными коннектиками в 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;
И когда я его выполнил и увидел result
в QuickWatch.., он показал это сообщение:
запрос содержит ссылки на элементы, определенные в другом контексте данных
В googling я нашел этот раздел в 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.
Ответы
Ответ 1
Мне приходилось делать это раньше, и есть два способа сделать это.
Во-первых, переместить все серверы в один контекст. Вы делаете это, указывая LINQ-to-SQL на один сервер, а затем на этом сервере создайте задача репликации, чтобы скопировать необходимые данные из MyLinkedServer
в MyServer
один раз в день/неделю/месяц. Это только вариант, если:
- Ваша программа может работать со слегка устаревшими данными из
MyLinkedServer
- Вам нужно читать, никогда не писать,
MyLinkedServer
- Таблицы, которые вам нужны от
MyLinkedServers
, не являются чрезмерно большими
- У вас есть доступное пространство/пропускная способность
- Администраторы базы данных не скупые/ленивые
Ответ 2
Ваш SimulateJoins не может работать, потому что они возвращают IQueryable
. Ваше текущее решение в точности совпадает с вашим предыдущим, и именно поэтому вы получаете то же исключение. Если вы снова проверите связанный вопрос, вы увидите, что их вспомогательные методы возвращают IEnumerable
, что является единственным способом выполнения кросс-контекстных операций. Как вы, наверное, уже знаете, это означает, что соединение будет выполняться в памяти на сервере приложений вместо сервера базы данных = он вытащит все данные из ваших частичных запросов и выполнит объединение как linq-to-objects.
Перекрестное контекстное соединение на уровне базы данных IMO невозможно. У вас могут быть разные подключения, разные строки подключения с разными серверами и т.д. Linq-to-sql не справляется с этим.
Ответ 3
Вы можете обойти это путем "ускользания от" 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 в SQL и в Linq к объектам для контекста "оскорбления".
Ответ 4
Старый вопрос, но поскольку у меня была такая же проблема, моим решением было передать связанный вручную кросс-серверный запрос T-SQL (со связанными серверами) непосредственно поставщику с помощью метода ExecuteQuery в первом контексте:
db.ExecuteQuery(Of cTechSupportCall)(strSql).ToList
Это просто избавляет вас от необходимости создавать сервер на стороне просмотра, а Linq to SQL по-прежнему сопоставляет результаты с соответствующим типом. Это полезно, когда есть один запрос, который просто невозможно сформулировать в Linq.