2011-01-27 10 views
8

私は単純なXmlリポジトリ(GetAll、Add、Update、Delete)の例を探しています。Xmlリポジトリの実装

データストアの場所を入れ替えることができるため、リポジトリパターンを使用することをお勧めします。これでXMLファイルにデータを格納し、XMLリポジトリを実装する方法はわかりません。私はGoogleのすべてを検索し、それを見つけることができません。

可能であれば、リレーショナルデータハンドルを含むサンプルを送信します。 EFに製品エンティティを保存し、すべての製品依存エンティティも保存されている場合と同様です。

答えて

8

まあ、ペッターの解決策は素晴らしいです。

私の実装を共有するだけで、私の質問に再び答えてくれるはずです。評価し、コメントしてください。

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エンティティのための多くのリポジトリを持つことができます。

私は、このレポジトリをモックを使用してうまく機能するアプリケーションのテストを行いました。

私のIoC設定では、XDocumentProviderのデフォルトを設定する必要があります。必要に応じて、この静的な "Default"プロパティの代わりにXDocumentProvider throughtコンストラクタを渡すことができます

私の実装についてどう思いますか?

おかげ

私はちょうどそれを明確に、この実装は時間前に行われたと、今日XMLにLINQを使用してのように、それを行うには良い方法があることをできるようにしたい
+2

。 今日、この回答を確認したところ、今すぐ改善できることがわかりました。いつか私がそれを必要とすれば、私はここで実装し、投稿します。ご支援ありがとうございます –

2

同僚と私はそのようなXMLリポジトリを正確に実装しました。それはXmlRepository :-)と呼ばれています。

これは内部的にXMLにlinqで構築されており、外部アクセスはnhibernateにlinqをどのように使用するかと似ています。これは、オブジェクトへのlinqで行われ、クライアントコードの使い方は、単純なXMLコメントのインターフェイスのため、非常に簡単で、簡単で分かりやすいものです。

現在のリリース(組み立て)サブクラスまたは1のための内蔵をサポートしていません:nの関係がありますが、上記のサイトでも見つけることができる現在の開発ソースコードは、内蔵のそれらの両方を持っている

完全にリリースする準備が整っていません。軽微なバグがありますが、試してみてください。ソースコードを入手して改善してください。オープンソースです。

オープンソース(読み込み専用ソース)プロジェクトのコメント、希望、建設的な批評、パッチは、私の同僚(Golo Roden)と私を幸せにし、プロジェクトをより良い状態にするでしょう。

例のアプリケーションはhere(テキストはドイツ語)です。

関連する問題