2011-01-27 87 views
8

我正在尋找一個簡單的Xml存儲庫(GetAll,Add,Update,Delete)示例。Xml存儲庫實現

大家都說「使用存儲庫模式是個好主意,因爲您可以交換數據存儲位置......」現在我需要將我的數據存儲在xml文件中,而不知道如何實現XML存儲庫。我搜遍了谷歌,找不到它。

如果可能,發送包含關係數據句柄的示例。就像在EF上保存產品實體一樣,並且所有產品相關實體也都保留下來。

回答

8

那麼,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; 
     } 
    } 
} 

}

然後我可以有diferent實體將懸掛持久單個數據方面行動過很多版本庫。

我已經爲我的應用程序做了測試,使用這個庫使用mocks並運行良好。

在我的IoC配置上,我必須設置XDocumentProvider的默認值。如果有必要,我們可以通過XDocumentProvider通過構造函數而不是這個靜態的「默認」屬性

你對我的實現有什麼看法?

感謝

+2

我只想讓它清楚,這是實現了一個很久以前和現在有更好的方法來做到這一點,就像使用LINQ to XML。 今天回顧這個答案,我發現我現在可以做得更好。如果有一天我需要它,我會執行並在這裏發佈。感謝您的支持 – 2013-01-28 22:33:56

2

一位同事和我實現了這樣一個XML存儲庫,它被稱爲XmlRepository :-)。

它內部使用linq創建XML,外部訪問類似於您如何使用linq for nhibernate。它使用linq完成對象,由於簡單的XML註釋接口,客戶端代碼中的用法非常簡單,易於理解。

當前版本(組裝)已經不支持內置的子類或1:n的關係,但目前的開發源代碼,您還可以找到上面的網站上,有他們兩個內置

它還沒有完全準備好發佈 - 它可能有一些小錯誤,但是如果你喜歡,可以嘗試一下,獲取源代碼並改進它。它是開源的。

任何對開源項目(只讀源代碼)的評論,願望,建設性批評和補丁都會使我的同事(Golo Roden)和我感到高興,並使項目進入更好的狀態。

示例應用程序可用here(文本是德語)。