我正在寻找一个简单的Xml存储库(GetAll,Add,Update,Delete)示例。
每个人都说“使用存储库模式是一个好主意,因为你可以交换你的数据存储位置......”现在我需要将我的数据存储在xml文件中,并且不知道如何实现XML存储库。我在谷歌搜索过,找不到它。
如果可能,发送包含关系数据句柄的示例。就像在EF上保存产品实体一样,所有依赖于产品的实体也都是持久的。
答案 0 :(得分:10)
好吧,Petter解决方案很好。
为了分享我的实施,我将再次回答我的问题,我希望这对某人有用。请评价和评论。
public interface IRepository<T>
{
IEnumerable<T> GetAll();
IEnumerable<T> GetAll(object parentId);
T GetByKey(object keyValue);
void Insert(T entidade, bool autoPersist = true);
void Update(T entidade, bool autoPersist = true);
void Delete(T entidade, bool autoPersist = true);
void Save();
}
XML存储库的基类
public abstract class XmlRepositoryBase<T> : IRepository<T>
{
public virtual XElement ParentElement { get; protected set; }
protected XName ElementName { get; private set; }
protected abstract Func<XElement, T> Selector { get; }
#endregion
protected XmlRepositoryBase(XName elementName)
{
ElementName = elementName;
// clears the "cached" ParentElement to allow hot file changes
XDocumentProvider.Default.CurrentDocumentChanged += (sender, eventArgs) => ParentElement = null;
}
#region
protected abstract void SetXElementValue(T model, XElement element);
protected abstract XElement CreateXElement(T model);
protected abstract object GetEntityId(T entidade);
#region IRepository<T>
public T GetByKey(object keyValue)
{
// I intend to remove this magic string "Id"
return XDocumentProvider.Default.GetDocument().Descendants(ElementName)
.Where(e => e.Attribute("Id").Value == keyValue.ToString())
.Select(Selector)
.FirstOrDefault();
}
public IEnumerable<T> GetAll()
{
return ParentElement.Elements(ElementName).Select(Selector);
}
public virtual IEnumerable<T> GetAll(object parentId)
{
throw new InvalidOperationException("This entity doesn't contains a parent.");
}
public virtual void Insert(T entity, bool autoPersist = true)
{
ParentElement.Add(CreateXElement(entity));
if (autoPersist)
Save();
}
public virtual void Update(T entity, bool autoPersist= true)
{
// I intend to remove this magic string "Id"
SetXElementValue(
entity,
ParentElement.Elements().FirstOrDefault(a => a.Attribute("Id").Value == GetEntityId(entity).ToString()
));
if (persistir)
Save();
}
public virtual void Delete(T entity, bool autoPersist = true)
{
ParentElement.Elements().FirstOrDefault(a => a.Attribute("Id").Value == GetEntityId(entity).ToString()).Remove();
if (autoPersist)
Save();
}
public virtual void Save()
{
XDocumentProvider.Default.Save();
}
#endregion
#endregion
}
还有2个抽象类,一个是独立实体,另一个是子实体。为了避免每次都读取Xml文件,我做了一种缓存控制
public abstract class EntityXmlRepository<T> : XmlRepositoryBase<T>
{
#region cache control
private XElement _parentElement;
private XName xName;
protected EntityXmlRepository(XName entityName)
: base(entityName)
{
}
public override XElement ParentElement
{
get
{
// returns in memory element or get it from file
return _parentElement ?? ( _parentElement = GetParentElement() );
}
protected set
{
_parentElement = value;
}
}
/// <summary>
/// Gets the parent element for this node type
/// </summary>
protected abstract XElement GetParentElement();
#endregion
}
现在实现子类型
public abstract class ChildEntityXmlRepository<T> : XmlRepositoryBase<T>
{
private object _currentParentId;
private object _lastParentId;
private XElement _parentElement;
public override XElement ParentElement
{
get
{
if (_parentElement == null)
{
_parentElement = GetParentElement(_currentParentId);
_lastParentId = _currentParentId;
}
return _parentElement;
}
protected set
{
_parentElement = value;
}
}
/// <summary>
/// Defines wich parent entity is active
/// when this property changes, the parent element field is nuled, forcing the parent element to be updated
/// </summary>
protected object CurrentParentId
{
get
{
return _currentParentId;
}
set
{
_currentParentId = value;
if (value != _lastParentId)
{
_parentElement = null;
}
}
}
protected ChildEntityXmlRepository(XName entityName) : base(entityName){}
protected abstract XElement GetParentElement(object parentId);
protected abstract object GetParentId(T entity);
public override IEnumerable<T> GetAll(object parentId)
{
CurrentParentId = parentId;
return ParentElement.Elements(ElementName).Select(Selector);
}
public override void Insert(T entity, bool persistir = true)
{
CurrentParentId = GetParentId(entity);
base.Insert(entity, persistir);
}
public override void Update(T entity, bool persistir = true)
{
CurrentParentId = GetParentId(entity);
base.Update(entity, persistir);
}
public override void Delete(T entity, bool persistir = true)
{
CurrentParentId = GetParentId(entity);
base.Delete(entity, persistir);
}
}
现在,实际的实施
public class RepositorioAgendamento : EntityXmlRepository<Agendamento>, IRepositorioAgendamento
{
protected override Func<XElement, Agendamento> Selector
{
get
{
return x => new Agendamento() {
Id = x.Attribute("Id").GetGuid(),
Descricao = x.Attribute("Descricao").Value,
TipoAgendamento = x.Attribute("TipoAgendamento").GetByte(),
Dias = x.Attribute("Dias").GetByte(),
Data = x.Attribute("Data").GetDateTime(),
Ativo = x.Attribute("Ativo").GetBoolean(),
};
}
}
protected override XElement CreateXElement(Agendamento agendamento)
{
agendamento.Id = Guid.NewGuid();
return new XElement(ElementName,
new XAttribute("Id", agendamento.Id),
new XAttribute("Descricao", agendamento.Descricao),
new XAttribute("TipoAgendamento", agendamento.TipoAgendamento),
new XAttribute("Dias", agendamento.Dias),
new XAttribute("Data", agendamento.Data),
new XAttribute("Ativo", agendamento.Ativo),
new XElement(XmlNames.GruposBackup),
new XElement(XmlNames.Credenciais)
);
}
protected override void SetXElementValue(Agendamento modelo, XElement elemento)
{
elemento.Attribute("Descricao").SetValue(modelo.Descricao);
elemento.Attribute("TipoAgendamento").SetValue(modelo.TipoAgendamento);
elemento.Attribute("Dias").SetValue(modelo.Dias);
elemento.Attribute("Data").SetValue(modelo.Data);
elemento.Attribute("Ativo").SetValue(modelo.Ativo);
}
public RepositorioAgendamento() : base(XmlNames.Agendamento)
{
}
protected override XElement GetParentElement()
{
return XDocumentProvider.Default.GetDocument().Elements(XmlNames.Agendamentos).First();
}
protected override object GetEntityId(Agendamento entidade)
{
return entidade.Id;
}
public IEnumerable<Agendamento> ObterAtivos()
{
return ParentElement.Elements()
.Where(e => e.Attribute("Ativo").GetBoolean())
.Select(Selector);
}
}
现在,XDocumentProvider。它的功能是抽象对xml文件的访问,并统一到所有存储库,XDocument是数据上下文。 这可以命名为UnitOfWork ?
public abstract class XDocumentProvider
{
// not thread safe yet
private static bool pendingChanges;
private bool _documentLoadedFromFile;
FileSystemWatcher fileWatcher;
public static XDocumentProvider Default;
public event EventHandler CurrentDocumentChanged;
private XDocument _loadedDocument;
public string FileName { get; set; }
protected XDocumentProvider()
{
fileWatcher = new FileSystemWatcher();
fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileWatcher.Changed += fileWatcher_Changed;
}
void fileWatcher_Changed(object sender, FileSystemEventArgs e)
{
if (_documentLoadedFromFile && !pendingChanges)
{
GetDocument(true);
}
}
/// <summary>
/// Returns an open XDocument or create a new document
/// </summary>
/// <returns></returns>
public XDocument GetDocument(bool refresh = false)
{
if (refresh || _loadedDocument == null)
{
// we need to refactor it, but just to demonstrate how should work I will send this way ;P
if (File.Exists(FileName))
{
_loadedDocument = XDocument.Load(FileName);
_documentLoadedFromFile = true;
if (fileWatcher.Path != Environment.CurrentDirectory)
{
fileWatcher.Path = Environment.CurrentDirectory;
fileWatcher.Filter = FileName;
fileWatcher.EnableRaisingEvents = true;
}
}
else
{
_loadedDocument = CreateNewDocument();
fileWatcher.EnableRaisingEvents = false;
_documentLoadedFromFile = false;
}
if(CurrentDocumentChanged != null) CurrentDocumentChanged(this, EventArgs.Empty);
}
return _loadedDocument;
}
/// <summary>
/// Creates a new XDocument with a determined schemma.
/// </summary>
public abstract XDocument CreateNewDocument();
public void Save()
{
if (_loadedDocument == null)
throw new InvalidOperationException();
try
{
// tells the file watcher that he cannot raise the changed event, because his function is to capture external changes.
pendingChanges = true;
_loadedDocument.Save(FileName);
}
finally
{
pendingChanges = false;
}
}
}
然后,我可以为不同的实体提供许多存储库,在单个数据上下文中添加挂起持久性操作。
我已经对我的应用程序进行了测试,该应用程序使用这个存储库使用模拟并运行良好。
在我的IoC配置中,我必须为XDocumentProvider设置Default。如有必要,我们可以通过构造函数而不是静态“默认”属性传递XDocumentProvider
您如何看待我的实施?
由于
答案 1 :(得分:2)
一位同事和我实现了这样一个XML存储库,它被称为XmlRepository: - )。
它内部使用linq to XML构建,外部访问类似于使用linq进行nhibernate的方式。它通过linq to objects完成,客户端代码中的用法非常简单,易于理解,因为XML注释界面简单易懂。
当前版本(程序集)没有为子类或1:n关系内置支持,但是您可以在上面的站点上找到的当前开发源代码都内置了它们。
它还没有完全准备好发布 - 它可能有一些小错误,但尝试一下,如果你愿意,可以采取源代码并改进它。它是开源的。
对开源(阅读:唯一来源)项目的任何评论,愿望,建设性批评和补丁将使我的同事(Golo Roden)和我高兴并将项目带到更好的状态。
示例应用程序可用here(文本为德语)。