繁体   English   中英

部分修改XML序列化文档

[英]Partially Modifying an XML serialized document

我有一个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);
    }
}

Xml不是固定宽度的记录格式。 在不重写内部文件的情况下很难(请阅读:不要打扰)。 在xml的大多数合理用例中,这都是可以预期的。 如果这是一个问题,请考虑使用另一种存储机制(很快就会想到数据库),并在需要时简单地导入/导出xml。

我认为您正在学习为什么要使用数据库。

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

我想它也应该将生成的文档提供给客户。

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

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

据我所知,您有2种方法。 一种是实施一种锁定机制,以防止一个以上的人一次修改任何给定的文件。 由于您正在询问如何允许他们同时进行更改,因此我假设您无法执行此操作。

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

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

祝好运...

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

一种可能性:

  • 打开文件时,将完整副本加载到内存或临时文件中。
  • 在编辑时,请跟踪更改,不要修改文件。
  • 经常(每分钟左右)加载对外部文件的任何更改。 如果文件系统支持,请监视文件的更改并在写入文件时立即刷新。
  • 保存后,锁定文件以进行写入,并检查对本地修改的部分没有任何冲突的外部更改。
  • 如果您清楚,请进行更改,然后释放文件。
  • 如果发生冲突,则无论如何都必须处理该冲突。

如果您的应用适合该应用,则可以通过自动保存并经常刷新本地副本来减少冲突。 如果要自动保存而无需用户干预,只需确保您具有出色的撤消功能即可。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM