部分修改XML序列化文档

时间:2010-03-19 19:41:35

标签: c# .net c#-3.0 xml-serialization

我有一个XML文档,实际上有几个,可以通过前端UI进行编辑。我发现这种方法存在问题(除了它使用的是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 个答案:

答案 0 :(得分:2)

Xml不是固定宽度的记录格式。编辑内部结构而不重写它是非常困难的(阅读:不要打扰)。在大多数明智的xml用例中,这很好并且是预期的。如果这是一个问题,请考虑另一种存储机制(数据库跳跃到想法),并在需要时简单地导入/导出xml。

答案 1 :(得分:1)

我认为您正在学习使用数据库的原因。

您不能让多个线程在磁盘上写入相同的XML文档。您可以做的最好的事情是进行一些设置,您可以在其中提交对某些中央服务的更改,这些服务将维护磁盘文档的内存中副本。它会将每个更改更新到内存中,然后将其刷新到磁盘。由于它将是此的中央服务,它可以序列化更新请求,确保一致的文档。

我认为它也应该将结果文档提供给客户。

答案 2 :(得分:1)

我不认为你的问题有一个简单的解决方案,因为有很多问题:你不能在不重写整个文件的情况下更新XML文件,但如果有多个用户你必须关心资源锁定同时访问您的数据。这个问题没有简单的解决方案!

您发布的课程对我来说也很奇怪:为什么要序列化到XmlDocument以便之后将其写入光盘?为什么不立即序列化到光盘?捕捉异常并忽略它们是非常糟糕的风格!

答案 3 :(得分:1)

就我所见,你有2种方法可以走。一,实现锁定机制,以防止多个人一次修改任何给定的文件。既然你问如何允许他们同时进行更改,我会假设你不能这样做。

另一种方式是丑陋,真实丑陋。但它应该工作。基本上,将您的存储库转换为单例(在所有用户请求中只有一个静态引用),并在内存中“编辑”该文件。设置每个更改后,将更改保存到磁盘。由于您正在处理内存中信息的一个副本,因此一个用户不会覆盖另一个用户。 (除非他们都改变了组件)

您必须实现锁定才能生效。 (您基本上将每个请求视为一个线程,并在多线程环境中编程。

祝你好运......

答案 4 :(得分:1)

如果您对XML文档格式有一定的灵活性,那么您可以做一些事情。

一种可能性:

  • 打开文件时,请加载完整文件 复制,或者复制到内存中,或复制到内存中 临时文件。
  • 编辑时,请跟踪更改,不要修改文件。
  • 经常(每分钟左右)加载外部文件的任何更改。如果文件系统支持它,请监视文件以进行更改,并在写入文件时立即刷新。
  • 在保存时,锁定文件以进行写入,检查本地修改的部分是否存在任何冲突的外部更改。
  • 如果您已清除,请进行更改并释放文件。
  • 如果发生冲突,无论如何都要以某种方式处理。

如果您的应用程序适合它,您可以通过自动保存和经常刷新本地副本来减少冲突。如果您要在没有用户干预的情况下自动保存,请确保您具有出色的撤消功能。