我有一个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);
}
}
答案 0 :(得分:2)
Xml不是固定宽度的记录格式。编辑内部结构而不重写它是非常困难的(阅读:不要打扰)。在大多数明智的xml用例中,这很好并且是预期的。如果这是一个问题,请考虑另一种存储机制(数据库跳跃到想法),并在需要时简单地导入/导出xml。
答案 1 :(得分:1)
我认为您正在学习使用数据库的原因。
您不能让多个线程在磁盘上写入相同的XML文档。您可以做的最好的事情是进行一些设置,您可以在其中提交对某些中央服务的更改,这些服务将维护磁盘文档的内存中副本。它会将每个更改更新到内存中,然后将其刷新到磁盘。由于它将是此的中央服务,它可以序列化更新请求,确保一致的文档。
我认为它也应该将结果文档提供给客户。
答案 2 :(得分:1)
我不认为你的问题有一个简单的解决方案,因为有很多问题:你不能在不重写整个文件的情况下更新XML文件,但如果有多个用户你必须关心资源锁定同时访问您的数据。这个问题没有简单的解决方案!
您发布的课程对我来说也很奇怪:为什么要序列化到XmlDocument以便之后将其写入光盘?为什么不立即序列化到光盘?捕捉异常并忽略它们是非常糟糕的风格!
答案 3 :(得分:1)
就我所见,你有2种方法可以走。一,实现锁定机制,以防止多个人一次修改任何给定的文件。既然你问如何允许他们同时进行更改,我会假设你不能这样做。
另一种方式是丑陋,真实丑陋。但它应该工作。基本上,将您的存储库转换为单例(在所有用户请求中只有一个静态引用),并在内存中“编辑”该文件。设置每个更改后,将更改保存到磁盘。由于您正在处理内存中信息的一个副本,因此一个用户不会覆盖另一个用户。 (除非他们都改变了组件)
您必须实现锁定才能生效。 (您基本上将每个请求视为一个线程,并在多线程环境中编程。
祝你好运......
答案 4 :(得分:1)
如果您对XML文档格式有一定的灵活性,那么您可以做一些事情。
一种可能性:
如果您的应用程序适合它,您可以通过自动保存和经常刷新本地副本来减少冲突。如果您要在没有用户干预的情况下自动保存,请确保您具有出色的撤消功能。