Импорт XML к базе данных с помощью LINQ к объектам


Я импортировать XML-данные в базе данных MySQL, используя LINQ к объектам. Данные представляет, какие студенты, в каких классах, и выглядит как...

<studentClassesData>
  <studentClassEntry>
    <upn>W432980573452</upn>
    <className>8bcDn4</className>
  </studentClassEntry>
  <studentClassEntry>
    <upn>W234525211334</upn>
    <className>8bcPe1</className>
  </studentClassEntry>
  <!-- etc... -->
</studentClassesData>

Каждая запись представляет один студент в одном классе. Я знаю, что было бы лучше, если бы формат был иерархические (так что каждый элемент дает нам все ученики в классе), но XML генерируется извне, поэтому мы должны использовать его.

Получения этой информации являются двумя сущностями класс и ученик, которого связаны многие-ко-многим (например, класса.Студентов). Все учащиеся и классы были уже импортированы в базу данных.

Обратите внимание, что студент.УПН и класса.LongName уникальные ключи, которые соответствуют значениям, приведенным в XML. Но первичные ключи студентом.Идентификатор и класс.ИД.

Вот мой текущий код, с многословными комментариями для пояснения.

public static string ProcessStudentClasses(string path) {
  var sb = new StringBuilder();

  using (var ctx = new Ctx()) {

    // Create a dictionary of all classes, to be accessed by their LongName
    // (attach the classes, but don't bother loading their properties)
    var classes = ctx.Classes.Select(o => new { o.LongName, o.Id })
          .ToDictionary(o => o.LongName, o => new Class { Id = o.Id });
    foreach (var cls in classes.Values) ctx.Classes.Attach(cls);

    // Similarly for students
    var students = ctx.Students.Select(o => new { o.Upn, o.Id })
          .ToDictionary(o => o.Upn, o => new Student { Id = o.Id });
    foreach (var stu in students.Values) ctx.Students.Attach(stu);

    // Create a lookup of which classes already have which students
    var studentsInClasses =
          ctx.Classes.SelectMany(c => c.Students,
                                 (c, s) => new { ClassId = c.Id, StudentId = s.Id }
                                ).ToLookup(o => o.ClassId, o => o.StudentId);

    using (var reader = XmlReader.Create(path)) {

      var nodeName = String.Empty;
      var upn = String.Empty;
      var className = String.Empty;

      Student thisStudent = null;
      Class thisClass = null;

      while (reader.Read()) {
        switch (reader.NodeType) {

          case XmlNodeType.Element:
            // We're reading an XML element; note which element it is
            nodeName = reader.Name;
            break;

          case XmlNodeType.Text:
            // We're reading text (what's between the <nodeName> and </nodeName> tags)
            var trimmed = reader.Value.Trim();
            if (trimmed.Length == 0) continue;

            switch (nodeName) {
              case "upn":
                upn = trimmed;
                students.TryGetValue(upn, out thisStudent); // set 'thisStudent'
                break;
              case "className":
                className = trimmed;
                classes.TryGetValue(className, out thisClass); // set 'thisClass'
                break;
            }
            break;

          case XmlNodeType.EndElement:
            if (reader.Name != "studentClassEntry") continue;

            // We've got to the end of a studentClassEntry

            // If the student wasn't set...
            if (thisStudent == null) {
              sb.AppendFormat("Skipped: {0} - {1} (unknown UPN)<br />", upn, className);

            // If the class wasn't set...
            } else if (thisClass == null) {
              sb.AppendFormat("Skipped: {0} - {1} (unknown class)<br />", upn, className);

            // If the student's already in the class...
            // (Note, this only checks against the original list of students in classes;
            // an error will rightly be thrown if the imported XML contains duplicates)
            } else if (studentsInClasses[thisClass.Id].Contains(thisStudent.Id)) {
              sb.AppendFormat("Skipped: {0} - {1} (already exists)<br />", upn, className);

            // Otherwise (we're good to add the student to the class)...
            } else {
              thisClass.Students.Add(thisStudent);
              sb.AppendFormat("Imported: {0} - {1}<br />", upn, className);
            }

            // Reset everything, ready for the next entry
            thisStudent = null;
            thisClass = null;
            upn = String.Empty;
            className = String.Empty;
            break;
        }
      }
    }
    ctx.SaveChanges();
  }
  return sb.ToString();
}

Он принимает 13.1 сек, чтобы процесс просто более 21000 студент-класс записи.

Я бы очень признателен за любые предложения.



10931
2
задан 17 октября 2011 в 11:10 Источник Поделиться
Комментарии
2 ответа

Трудно сказать, что вызывает дополнительную нагрузку здесь. У меня была задача похожую, которая изначально заняла очень много времени, но с некоторыми корректировками и использование хэш-таблицы, я получил его для запуска в секунду (3000 сущности, созданные из 80Мб большой XML-файл).

Однако я использовать XDocument используется (для LINQ-на-XML) для задач. Взгляните на мой вопрос относительно производительности: https://stackoverflow.com/questions/1629596/xpathselectelement-vs-descendants

Также обратите внимание, что вы можете воспользоваться предварительно скомпилирована запросы и другие полезные эксплуатационные вещи, когда он приходит с LINQ в XML.

http://blog.dreamlabsolutions.com/post/2008/12/04/LINQ-to-XML-and-LINQ-to-XML-with-XPath-performance-review.aspx

Удачи!
Маттиас

1
ответ дан 27 октября 2011 в 09:10 Источник Поделиться

Если бы это было мне я бы попробуйте закомментировать базе вставить элемент кода, и времени просто на чтение XML-файла.

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

Код выглядит хорошо - один момент я хотел отметить, вы пытаетесь избежать дубликатов с studentsInClasses поиска, но если вставить новую базу данных в коде, вы должны действительно обновить studentsInClasses , чтобы отразить это, или вы можете получить повторяющиеся значения.

LINQ для XML-файле

Я бы, конечно, сказать, дать по LINQ к XML попробовать: это позволит уменьшить объем кода, даже если он не может решить ваши проблемы с производительностью!

Это все чтения XML немного перевел для вас. Вам понадобится "с помощью системы.В формате XML.В LINQ;" на самом верху.

        // CHANGED: try LINQ to XML
var doc = XDocument.Load(path);

// find child elements
foreach (XElement e in doc.Descendants("studentClassEntry"))
{
// get values
string upn = e.Element("upn").Value;
string className = e.Element("className").Value;

Я хотел бы повторить предупреждение о больших файлов. Смотрите в этой статье Майк Taulty. Он также имеет превосходную видео-учебник здесь.

1
ответ дан 18 января 2012 в 02:01 Источник Поделиться