Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Well, Petter solution is nice.</p> <p>Just to share my implementation I will answer my question again, I hope that can be usefull to someone. Please, rate and comment.</p> <pre class="lang-cs prettyprint-override"><code>public interface IRepository&lt;T&gt; { IEnumerable&lt;T&gt; GetAll(); IEnumerable&lt;T&gt; 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(); } </code></pre> <p>And the base class for XML Repositories</p> <pre class="lang-cs prettyprint-override"><code>public abstract class XmlRepositoryBase&lt;T&gt; : IRepository&lt;T&gt; { public virtual XElement ParentElement { get; protected set; } protected XName ElementName { get; private set; } protected abstract Func&lt;XElement, T&gt; Selector { get; } #endregion protected XmlRepositoryBase(XName elementName) { ElementName = elementName; // clears the "cached" ParentElement to allow hot file changes XDocumentProvider.Default.CurrentDocumentChanged += (sender, eventArgs) =&gt; 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&lt;T&gt; public T GetByKey(object keyValue) { // I intend to remove this magic string "Id" return XDocumentProvider.Default.GetDocument().Descendants(ElementName) .Where(e =&gt; e.Attribute("Id").Value == keyValue.ToString()) .Select(Selector) .FirstOrDefault(); } public IEnumerable&lt;T&gt; GetAll() { return ParentElement.Elements(ElementName).Select(Selector); } public virtual IEnumerable&lt;T&gt; 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 =&gt; a.Attribute("Id").Value == GetEntityId(entity).ToString() )); if (persistir) Save(); } public virtual void Delete(T entity, bool autoPersist = true) { ParentElement.Elements().FirstOrDefault(a =&gt; a.Attribute("Id").Value == GetEntityId(entity).ToString()).Remove(); if (autoPersist) Save(); } public virtual void Save() { XDocumentProvider.Default.Save(); } #endregion #endregion } </code></pre> <p>And 2 more abstract classes, one to Independent entities and other to child entities. To avoid reading the Xml file every time, I've made a kind of cache control</p> <pre class="lang-cs prettyprint-override"><code>public abstract class EntityXmlRepository&lt;T&gt; : XmlRepositoryBase&lt;T&gt; { #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; } } /// &lt;summary&gt; /// Gets the parent element for this node type /// &lt;/summary&gt; protected abstract XElement GetParentElement(); #endregion } </code></pre> <p>Now the implementation for child types</p> <pre class="lang-cs prettyprint-override"><code>public abstract class ChildEntityXmlRepository&lt;T&gt; : XmlRepositoryBase&lt;T&gt; { 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; } } /// &lt;summary&gt; /// Defines wich parent entity is active /// when this property changes, the parent element field is nuled, forcing the parent element to be updated /// &lt;/summary&gt; 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&lt;T&gt; 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); } } </code></pre> <p><strong>Now, a real world implementation</strong></p> <pre class="lang-cs prettyprint-override"><code>public class RepositorioAgendamento : EntityXmlRepository&lt;Agendamento&gt;, IRepositorioAgendamento { protected override Func&lt;XElement, Agendamento&gt; Selector { get { return x =&gt; 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&lt;Agendamento&gt; ObterAtivos() { return ParentElement.Elements() .Where(e =&gt; e.Attribute("Ativo").GetBoolean()) .Select(Selector); } } </code></pre> <p>And now, the XDocumentProvider. Its function is to abstract the access to the xml file and unify to all repositories what XDocument is the data context. <strong>This can be named UnitOfWork</strong>?</p> <pre class="lang-cs prettyprint-override"><code>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 &amp;&amp; !pendingChanges) { GetDocument(true); } } /// &lt;summary&gt; /// Returns an open XDocument or create a new document /// &lt;/summary&gt; /// &lt;returns&gt;&lt;/returns&gt; 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; } /// &lt;summary&gt; /// Creates a new XDocument with a determined schemma. /// &lt;/summary&gt; 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; } } } </code></pre> <p>Then I can have many repositories for diferent entities adding pendent persistence actions in a single data context.</p> <p>I have made tests for my application that uses this repository using mocks and worked well.</p> <p>On my IoC configuration I have to set the Default for XDocumentProvider. If necessary, we can pass the XDocumentProvider throught constructor instead of this static "Default" property</p> <p>What do you think about my implementation?</p> <p>Thanks</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload