2011-01-27 18 views
8

Estoy buscando un ejemplo simple del Repositorio Xml (GetAll, Agregar, Actualizar, Eliminar).Implementación del repositorio Xml

Todo el mundo dice "Es una buena idea usar el patrón de repositorio porque puede cambiar la ubicación de su almacén de datos ..." ahora necesito almacenar mis datos en un archivo xml y no sé cómo implementar un repositorio XML. He buscado en todo el google y no puedo encontrarlo.

Si es posible, envíe un ejemplo que contenga el identificador de datos relacionales. Al igual que cuando guarda una entidad de producto en EF y todas las entidades dependientes del producto también se conservan.

Respuesta

8

Bueno, la solución de Petter es agradable.

Solo para compartir mi implementación, responderé mi pregunta nuevamente, espero que pueda ser útil para alguien. Por favor, califica y comenta.

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(); 
} 

Y la clase base para los repositorios 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 
} 

}

y 2 más clases abstractas, uno a entidades independientes y otras entidades a los niños. Para evitar leer el archivo XML cada vez, he hecho una especie de control de caché

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 
} 

Ahora la aplicación para este tipo de niño

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); 
    } 
} 

Ahora, una aplicación en el mundo real

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); 
    } 
} 

Y ahora, el XDocumentProvider. Su función es abstraer el acceso al archivo xml y unificar a todos los repositorios lo que XDocument es el contexto de datos. ¿Esto se puede llamar 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; 
     } 
    } 
} 

}

Entonces puede tener muchos depósitos de entidades diferentes añadiendo pendiente persistencia acciones en un contexto de datos única.

He hecho pruebas para mi aplicación que usa este repositorio usando simulaciones y funcionó bien.

En mi configuración de IoC, tengo que establecer el valor predeterminado para XDocumentProvider. Si es necesario, podemos pasar el constructor de XDocumentProvider en lugar de esta propiedad estática "Predeterminada"

¿Qué opina de mi implementación?

Gracias

+2

sólo quiero dejar claro que esta aplicación se hizo hace un tiempo y hoy en día hay mejores maneras de hacerlo, como el uso de LINQ to XML. Al revisar esta respuesta hoy, descubrí que puedo hacerlo mejor ahora. Si algún día lo necesito, lo implementaré y publicaré aquí. Gracias por el apoyo –

2

Un colega y yo implementamos exactamente dicho repositorio XML, y se llama XmlRepository :-).

Está construido internamente con linq a XML y el acceso externo es similar a cómo usa linq for nhibernate. Se hace con linq a los objetos, el uso en el código del cliente es muy simple, fácil y rápidamente comprensible debido a la simple interfaz comentada en XML.

La versión actual (montaje) no tiene soporte incorporado para subclases o 1: N relaciones, pero el código fuente de desarrollo actual, que se puede encontrar también en el sitio anterior, tiene ambos construidos en

.

No está completamente listo para ser liberado, puede tener algunos errores menores, pero pruébalo, toma el código fuente y mejora, si quieres. Es de código abierto.

Cualquier comentario, deseo, crítica constructiva y parches para el proyecto de fuente abierta (léase: solo fuente) hará feliz a mi colega (Golo Roden) y a mí y traerá el proyecto a un mejor estado.

Existe una aplicación de ejemplo disponible here (el texto está en alemán).

Cuestiones relacionadas