Xml存储库实施

发布于 2024-10-15 01:25:36 字数 202 浏览 3 评论 0原文

我正在寻找一个简单的 Xml 存储库(GetAll、添加、更新、删除)示例。

每个人都说“使用存储库模式是个好主意,因为您可以交换数据存储位置...”现在我需要将数据存储在 xml 文件上,但不知道如何实现 XML 存储库。我用谷歌搜索遍了也没找到。

如果可能,请发送包含关系数据句柄的示例。就像您在 EF 上保存产品实体一样,所有产品相关实体也会被持久化。

I'm looking for a simple Xml Repository(GetAll, Add, Update, Delete) example.

Everyone says "It's a good idea to use the repository pattern because you can swap your data store location..." now I need to store my data on a xml file and dont know how to implement a XML repository. I've searched all over the google and cannot find it.

If possible, send an example containing relational data handle. Like when you save a product entity on EF and all the product dependent entities are also persisted.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

假情假意假温柔 2024-10-22 01:25:36

更新 2020: 已经有很好的 nuget 包可以很好地处理这个问题,例如 SharpRepository.XmlRepository,它是许多存储库实现套件的一部分。


嗯,彼得的解决方案很好。

只是为了分享我的实现,我将再次回答我的问题,我希望这对某人有用。请评分和评论。

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

然后,我可以为不同的实体创建多个存储库,在单个数据上下文中添加挂起的持久性操作。

我已经使用模拟对使用此存储库的应用程序进行了测试,并且运行良好。

在我的 IoC 配置中,我必须设置 XDocumentProvider 的默认值。如果有必要,我们可以通过构造函数传递 XDocumentProvider 而不是这个静态“默认”属性

您对我的实现有何看法?

谢谢

Update 2020: There is already nice nuget packages that handle this nicelly, such as SharpRepository.XmlRepository, which is part of a suite of many repository implementations.


Well, Petter solution is nice.

Just to share my implementation I will answer my question again, I hope that can be usefull to someone. Please, rate and comment.

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

And the base class for XML Repositories

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
}

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

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
}

Now the implementation for child types

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

Now, a real world implementation

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

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. This can be named 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;
        }
    }
}

Then I can have many repositories for diferent entities adding pendent persistence actions in a single data context.

I have made tests for my application that uses this repository using mocks and worked well.

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

What do you think about my implementation?

Thanks

没︽人懂的悲伤 2024-10-22 01:25:36

我和一位同事正是实现了这样一个 XML 存储库,它被称为 XmlRepository :-)。

它是使用 linq to XML 在内部构建的,外部访问类似于使用 linq for nhibernate 的方式。它是通过 linq to object 完成的,由于简单的 XML 注释接口,客户端代码中的使用非常简单、容易且快速理解。

当前版本(程序集)没有内置对子类或 1:n 关系的支持,但是当前的开发源代码(您也可以在上面的站点上找到)内置了这两者。

它还没有完全准备好发布——它可能有一些小错误,但是如果你愿意的话,可以尝试一下,获取源代码并改进它。它是开源的。

任何评论、愿望、建设性批评以及开源(只读:仅源)项目的补丁都会让我和我的同事(Golo Roden)感到高兴,并使项目达到更好的状态。

示例应用程序可在此处< /a>(文本为德语)。

A colleague and I implemented exactly such an XML repository, and it's called XmlRepository :-).

It's built internally with linq to XML and the external access is similar to how you use linq for nhibernate. It's done with linq to objects, the usage in client code is very simple, easy and quickly understandable because of the simple XML-commented interface.

The current release (assembly) has no support built in for subclasses or 1:n relations, but the current development source code, which you can find also on the site above, has both of them built in.

It is not completely ready to release-- it may have some bit minor bugs, but try it out, take the source code and improve it, if you like. It's open source.

Any comments, wishes, constructive criticism, and patches for the open source (read: only source) project will make my colleague (Golo Roden) and I happy and bring the project to a better state.

An example application is available here (text is in German).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文