У меня возникают проблемы при попытке распараллелить дорогостоящую интеграцию API.
Интеграция параллельно запрашивает API и заполняет коллекцию ConcurrentBag
. Выполняется некоторая обработка, а затем она передается в Parallel.ForEach()
, в котором она взаимодействует с базой данных с помощью LINQ To Sql.
Есть:
один внешний цикл, который работает параллельно для курсов
внутренний цикл через дисциплины
внутри него еще один цикл, повторяющий уроки.
Проблема, с которой я столкнулся, заключается в следующем: поскольку любой урок может принадлежать более чем одному курсу, параллельный цикл по курсам означает, что иногда урок будет вставлен более одного раза.
В настоящее время код выглядит так:
(externalCourseList
– это коллекция типа ConcurrentBag<ExternalCourse>
.)
Parallel.ForEach(externalCourseList, externalCourse =>
{
using ( var context = new DataClassesDataContext() )
{
var dbCourse = context.Courses.Single( x => x.externalCourseId == externalCourse.courseCode.ToString() );
dbCourse.ShortDesc = externalCourse.Summary;
//dbCourse.LongDesc = externalCourse.MoreInfo;
//(etc)
foreach (var externalDiscipline in externalCourse.Disciplines)
{
var dbDiscipline = context.Disciplines.Where(
x => x.ExternalDisciplineID == externalDiscipline.DisciplineCode.ToString() ).SingleOrDefault();
if (dbDiscipline == null)
dbDiscipline = new Linq2SQLEntities.Discipline();
dbDiscipline.Title = externalDiscipline.Name;
//(etc)
dbDiscipline.ExternalDisciplineID = externalDiscipline.DisciplineCode.ToString();
if (!dbDiscipline.IsLoaded)
context.Disciplines.InsertOnSubmit(dbDiscipline);
// relational table used as one-to-many relationship for legacy reasons
var courseDiscipline = dbDiscipline.Course_Disciplines.SingleOrDefault(x => x.CourseID == dbCourse.CourseID);
if (courseDiscipline == null)
{
courseDiscipline = new Course_Discipline
{
Course = dbCourse,
Discipline = dbDiscipline
};
context.Course_Disciplines.InsertOnSubmit(courseDiscipline);
}
foreach (var externalLesson in externalDiscipline.Lessons)
{
/// The next statement throws an exception
var dbLesson = context.Lessons.Where(
x => x.externalLessonID == externalLesson.LessonCode).SingleOrDefault();
if (dbLesson == null)
dbLesson = new Linq2SQLEntities.Lesson();
dbLesson.Title = externalLesson.Title;
//(etc)
dbLesson.externalLessonID = externalLesson.LessonCode;
if (!dbLesson.IsLoaded)
context.Lessons.InsertOnSubmit(dbLesson);
var disciplineLesson = dbLesson.Discipline_Lessons.SingleOrDefault(
x => x.DisciplineID == dbDiscipline.DisciplineID && x.LessonID == dbLesson.LessonID);
if (disciplineLesson == null)
{
disciplineLesson = new Discipline_Lesson
{
Discipline = dbDiscipline,
Lesson = dbLesson
};
context.Discipline_Lessons.InsertOnSubmit(disciplineLesson);
}
}
}
context.SubmitChanges();
}
}
);
(IsLoaded
реализовано, как описано здесь.)
Исключение создается для строки, которой предшествует ///
, поскольку один и тот же урок часто вставляется несколько раз, а вызов .SingleOrDefault()
на context.Lessons.Where(x => x.externalLessonID == externalLesson.LessonCode)
завершается ошибкой.
Что было бы лучшим способом решить эту проблему?
Спасибо.
GroupBy()
и попытайтесь создать блоки, которые не конфликтуют в нужной точке. Другая возможность (которую я никогда не использовал, поэтому не знаю, работает ли она) — использоватьPartitioner
.Parallel.ForEach
принимает его как дополнительный параметр и также помогает сгруппировать данные осмысленным образом для распараллеливания. - person Oliver   schedule 13.12.2018