草庐IT

c# - XML 存储库实现

coder 2024-06-24 原文

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

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

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

最佳答案

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


嗯,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文件的访问,统一到所有的repositories,什么XDocument是data context。 这可以命名为 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 而不是这个静态“默认”属性

您如何看待我的实现?

谢谢

关于c# - XML 存储库实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4819680/

有关c# - XML 存储库实现的更多相关文章

  1. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  4. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  10. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

随机推荐