Linq извлекает элементы, которые изменились между наборами и элементами, не входящими в набор - PullRequest
0 голосов
/ 07 октября 2011

Вот мои занятия:

public class XDetail
{
    public string Name { get; set; }
    public int ID { get; set; }
}

public class X
{
    public int XID { get; set; }
    public int ID { get; set; }
}

Идентификатор разделяется между ними для связи X и XDetail (отношение один ко многим), а X и XDetail действительно являются типизированными DataRows. Я читаю в файле, используя следующий запрос linq и формирую анонимный тип:

var results = (from line in File.ReadAllLines(file)
              select new
              {
                  XID = int.Parse(line.Substring(0, 8).TrimStart('0')),
                  Name = line.Substring(8, 255).Trim()
              }).ToList();

Эти данные используются для проверки существующего X / XDetail для внесения соответствующих изменений или добавления новых записей. Я обертываю результаты в проверку, чтобы увидеть, бросает ли он .ToList (), когда последовательность не имеет результатов. XList - это список, а XDetailList - это список.

Оттуда я пытаюсь выполнить причудливый запрос linq, чтобы найти соответствующие элементы:

var changedData = from x in XList
                  join xDetail in XDetailList on x.ID equals xDetail.ID
                  where 
                  (!results.Any(p => p.XID.Equals(x.XID))
                  || !results.Any(p => p.Name.Equals(xDetail.Name)))                   
                  select new
                  {                       
                      XValue = x,
                      XDetailValue = xDetail,
                      Result = (from result in results
                               where result.Name.Equals(xDetail.Name)
                               select result).SingleOrDefault()
                  };

Моя новая проблема заключается в том, что этот запрос предоставит мне только то, что изменилось в X / XDetail, а не то, что является новым. Чтобы получить что-то новое, мне нужно выполнить другой запрос, который выглядел достаточно хорошо при тестировании небольших наборов данных (3 существующих записи X / XDetail), но когда я попытался создать реальный файл и перебрать его через ~ 7700 записей, мне кажется, иметь бесконечную обработку.

Для примера набора данных следующего, уже содержащегося в X / XDetail:
XID: 1, имя: Боб, ID: 10
XID: 2, имя: Джо, ID: 20
XID: 3, имя: Сэм, ID: 30

С файлом результатов, содержащим:
XID: 2, имя: Bob2
XID: 3, имя: NotSam
XID: 4, имя: NewGuy
XID: 5, имя: NewGuy2

Я бы хотел получить набор результатов, содержащий:
{XID: 2, имя: Bob2}, x, xDetail
{XID: 3, имя: NotSam}, x, xDetail
{XID: 4, имя: NewGuy}, x, xDetail
{XID: 5, имя: NewGuy2}, x, xDetail

Я хотел бы, чтобы x и xDetail были частью набора результатов, чтобы я мог использовать эти строки типизированных данных для внесения необходимых изменений.

Я попробовал сделать такой запрос:

var newData = from result in results
              join x in XList on result.XID equals x.XID
              join xDetail in XDetailList on x.ID equals xDetail.ID
                      where
                      (x.XID == result.XID && xDetail.Name != result.Name)
                      select new
                      {
                          XValue = x,
                          XDetailValue = xDetail,
                          Result = result
                      };

Поскольку объединения указывают, что я когда-либо собираюсь получать измененные элементы в данных, я действительно хочу иметь возможность добавлять эти данные, которых нет в X / XDetail, и останавливать мою систему, которая обрабатывает мои ~ 7700 файлов изменений за последние 2,5 часа. Мне кажется, я слишком долго смотрел на этот и другие связанные запросы, чтобы определить, что мне следует делать, чтобы правильно сформировать для него предложение where.

Есть ли способ структурировать запрос linq, чтобы найти измененные данные и данные, которых нет в X / XDetail, и вернуть их в новый набор результатов для обработки?

1 Ответ

2 голосов
/ 08 октября 2011

Я думаю, что ваши проблемы с производительностью связаны со сложностью ваших запросов, которые могут быть около O(n^2).

Следовательно, сначала я предлагаю вам установить текущие данные в структуре поиска, например так (*):

var joinedByXID = (from x in XList
                    join xDetail in XDetailList on x.ID equals xDetail.ID
                    select new { X = x, XDetail = xDetail })
                    .ToLookup(x => x.X.ID);

Теперь я не уверен, но я предполагаю, что, говоря «измененные данные», вы имеете в виду список записей, в которых XID уже существует, но с новым именем, верно?
Если это так, вы можете получить «измененные данные», используя этот запрос:

var changedData = results
.Where(r => joinedByXID.Contains(r.XID))
.SelectMany(r => joinedByXID[r.XID]
            .Where(x => x.XDetail.Name != r.Name)
            .Select(old => new {XValue=old.X, XDetailValue=old.XDetail, Result=r}));

Затем, если под «новыми данными» вы подразумеваете список записей с новым XID (XID в настоящее время не представлен в XList /XDetailList), ну, вы не можете сопоставить их с элементами X / Xdetail, потому что, ну, в общем, их нет, так что просто:

var newData = results
.Where(r => !joinedByXID.Contains(r.XID));

(*)
На самом деле, чтобыеще быстрее, вы можете расположить свои данные в словаре словаря, где внешним ключом является XID, а внутренним ключом является Имя.

...