Dav*_*ghi 8 xml design-patterns repository linq-to-xml
我正在寻找一个简单的Xml存储库(GetAll,Add,Update,Delete)示例.
每个人都说"使用存储库模式是一个好主意,因为你可以交换你的数据存储位置......"现在我需要将我的数据存储在xml文件中,并且不知道如何实现XML存储库.我搜索了整个谷歌,无法找到它.
如果可能,请发送包含关系数据句柄的示例.就像在EF上保存产品实体一样,所有依赖于产品的实体也都是持久的.
Dav*_*ghi 11
那么,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;
        }
    }
}
然后,我可以为不同的实体提供许多存储库,在单个数据上下文中添加挂起持久性操作.
我已经为我的应用程序进行了测试,该应用程序使用mocks使用此存储库并且运行良
在我的IoC配置中,我必须为XDocumentProvider设置Default.如有必要,我们可以通过构造函数而不是静态"Default"属性传递XDocumentProvider
您如何看待我的实施?
谢谢