簡體   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