Частичное изменение сериализованного документа XML - PullRequest
1 голос
/ 19 марта 2010

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

Если один пользователь вносит изменения, пока другой пользователь находится в процессе внесения изменений, изменения второго перезапишут первое.

Мне нужно иметь возможность запрашивать объекты из XML-файлов, изменять их, а затем отправлять изменения обратно в XML-файл без перезаписи всего файла. Я разместил здесь весь свой класс доступа xml (который был создан благодаря замечательной помощи от stackoverflow!)

using System;
using System.Linq;

using System.Collections;
using System.Collections.Generic;

namespace Repositories
{
    /// <summary>
    /// A file base repository represents a data backing that is stored in an .xml file.
    /// </summary>
    public partial class Repository<T> : IRepository
    {
        /// <summary>
        /// Default constructor for a file repository
        /// </summary>
        public Repository() { }

        /// <summary>
        /// Initialize a basic repository with a filename. This will have to be passed from a context to be mapped.
        /// </summary>
        /// <param name="filename"></param>
        public Repository(string filename)
        {
            FileName = filename;
        }

        /// <summary>
        /// Discovers a single item from this repository.
        /// </summary>
        /// <typeparam name="TItem">The type of item to recover.</typeparam>
        /// <typeparam name="TCollection">The collection the item belongs to.</typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public TItem Single<TItem, TCollection>(Predicate<TItem> expression)
            where TCollection : IDisposable, IEnumerable<TItem>
        {
            using (var list = List<TCollection>())
            {
                return list.Single(i => expression(i));
            }
        }

        /// <summary>
        /// Discovers a collection from the repository,
        /// </summary>
        /// <typeparam name="TCollection"></typeparam>
        /// <returns></returns>
        public TCollection List<TCollection>() 
            where TCollection : IDisposable
        {
            using (var list = System.Xml.Serializer.Deserialize<TCollection>(FileName))
            {
                return (TCollection)list;
            }
        }

        /// <summary>
        /// Discovers a single item from this repository.
        /// </summary>
        /// <typeparam name="TItem">The type of item to recover.</typeparam>
        /// <typeparam name="TCollection">The collection the item belongs to.</typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public List<TItem> Select<TItem, TCollection>(Predicate<TItem> expression)
            where TCollection : IDisposable, IEnumerable<TItem>
        {
            using (var list = List<TCollection>())
            {
                return list.Where( i => expression(i) ).ToList<TItem>();
            }
        }

        /// <summary>
        /// Attempts to save an entire collection.
        /// </summary>
        /// <typeparam name="TCollection"></typeparam>
        /// <param name="collection"></param>
        /// <returns></returns>
        public Boolean Save<TCollection>(TCollection collection)
        {
            try
            {
                // load the collection into an xml reader and try to serialize it.
                System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();
                xDoc.LoadXml(System.Xml.Serializer.Serialize<TCollection>(collection));

                // attempt to flush the file
                xDoc.Save(FileName);

                // assume success
                return true;
            }
            catch
            {
                return false;
            }
        }


        internal string FileName { get; private set; }
    }

    public interface IRepository
    {
        TItem Single<TItem, TCollection>(Predicate<TItem> expression) where TCollection : IDisposable, IEnumerable<TItem>;
        TCollection List<TCollection>() where TCollection : IDisposable;
        List<TItem> Select<TItem, TCollection>(Predicate<TItem> expression) where TCollection : IDisposable, IEnumerable<TItem>;

        Boolean Save<TCollection>(TCollection collection);
    }
}

Ответы [ 5 ]

2 голосов
/ 19 марта 2010

Xml не является форматом записи фиксированной ширины. Очень трудно (читай: не беспокойтесь) редактировать внутренности, не переписывая их. В наиболее разумных случаях использования XML это нормально и ожидаемо. Если это проблема, рассмотрите альтернативный механизм хранения (на ум приходит база данных) и просто импортируйте / экспортируйте xml при необходимости.

1 голос
/ 19 марта 2010

Если у вас есть некоторая гибкость с форматом документа XML, то есть несколько вещей, которые вы можете сделать.

Одна возможность:

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

Если ваше приложение поддается ему, вы можете уменьшить конфликты, автоматически сохраняя и обновляя локальную копию. Просто убедитесь, что у вас есть исключительная возможность отмены, если вы собираетесь автоматически сохранять без вмешательства пользователя.

1 голос
/ 19 марта 2010

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

Другой путь уродлив, очень уродлив. Но это должно работать. По сути, превратите ваш репозиторий в одноэлементный (только одна статическая ссылка для всех пользовательских запросов) и «отредактируйте» файл в памяти. После каждого набора изменений сохраняйте изменения на диск. Поскольку вы работаете с одной копией информации в памяти, один пользователь не будет перезаписывать другую. (Если они оба не меняют компонент)

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

Удачи ...

1 голос
/ 19 марта 2010

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

Класс, который вы опубликовали, выглядит для меня также довольно странно: почему вы сериализуетесь в XmlDocument, чтобы впоследствии записать его на диск? Почему бы сразу не сериализовать диск? Ловить исключения и просто игнорировать их - очень плохой стиль!

1 голос
/ 19 марта 2010

Я думаю, вы изучаете, почему использовать базу данных.

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

Полагаю, он также должен предоставлять полученный документ клиентам.

...