首页 > 技术文章 > 简单的xml数据库设计

yuxin5156 2019-06-11 09:44 原文

我的需求

有时候系统需要存储一些简单的关系型属性,不考虑并发,不考虑性能(一次操作在几ms),数据库没有其他依赖引用,拿过来就可以用

为什么选xml作为数据库?

  • 可读性好
  • 实体的对应关系不严格

二进制序列化实体改动后不能读取以前序列化的数据,而且实体上面需要贴可序列化标签

数据库功能

  1. 无侵入存储实体,可存储poco对象,不需要在实体上面贴标签或继承什么
  2. 单例操作内存数据,只有初始化或者写入的时候才会读写文件,其他时候操作内存中的数据

使用指南

    * 定义实体(public)
        public class Friend{}
    * 定义实体操作类,继承XmlDb<T>
        public class FriendDbSet : XmlDb<Friend>
        {
            static FriendDbSet _instance = new FriendDbSet();
            public static FriendDbSet GetInstance()
            {
                return _instance;
            }
        }
    * 操作实体
        Friend friend = new Friend() { Id = "1", name = "何仙姑" };
        Friend friend2 = new Friend() { Id = "1", name = "何仙姑2" };
        //添加
        FriendDbSet.GetInstance().Add(friend2);
        //删除
        FriendDbSet.GetInstance().Remove(r => r.Id == "1");
        //修改
        friend.name = "何惠惠";
        FriendDbSet.GetInstance().UpdateByIdOrKey(friend);
        //查询
        var result = FriendDbSet.GetInstance().ToList().Where(r => r.name.StartsWith("何"));
  >也可以不定义实体操作类直接进行如下操作
   >XmlDb<Friend>.GetInstance().Add(u);//增加
   >XmlDb<Friend>.GetInstance().Remove(r=>r.Age>1000);//根据条件删除
   >XmlDb<Friend>.GetInstance().Remove("key");//根据key或id删除
   >XmlDb<Friend>.GetInstance().UpdateByIdOrKey(new User() { });//修改
   >XmlDb<Friend>.GetInstance().ToList();//查询全部
   >XmlDb<Friend>.GetInstance().Find("key");//查询单个

代码(使用单例模式,模版方法模式)

public class XmlDb where T : new()
{
private static XmlDb instance = new XmlDb();

    public static XmlDb<T> GetInstance()
    {
        return instance;
    }
    private List<T> entityList = new List<T>();
    public XmlDb()
    {
        this.SetDbFile();
        this.ReadDb();
    }
    private string dbFile;
    private string Dbfile
    {
        get { return dbFile; }
        set
        {
            if (!string.IsNullOrEmpty(value) && !value.Equals(dbFile))
            {
                this.entityList.Clear();
            }
            dbFile = value;
            this.ReadDb();
        }
    }
    protected virtual void ReadDb()
    {
        if (File.Exists(this.Dbfile))
        {
            XmlSerializer ks = new XmlSerializer(typeof(List<T>));
            Stream reader = new FileStream(this.Dbfile, FileMode.Open, FileAccess.ReadWrite);
            this.entityList = ks.Deserialize(reader) as List<T>;
            reader.Close();
        }
        else
        {
            this.entityList = new List<T>();
        }
    }
    protected virtual void SetDbFile()
    {
        string folder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "storage", "litedb");

        if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
        Type type = typeof(T);
        if (string.IsNullOrEmpty(this.Dbfile))
        {
            //获取全名称签名,防止类名重复
            string md5Sign = BitConverter.ToString(MD5.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(type.FullName))).Replace("-", "");
            this.Dbfile = Path.Combine(folder, type.Name + "_" + md5Sign + ".xml");
        }


    }


    protected virtual void WriteDb()
    {
        //异常处理
        string tempFileName = this.dbFile.Replace(".xml", "_Temp.xml");
        try
        {

            XmlSerializer ks = new XmlSerializer(typeof(List<T>));
            FileInfo fi = new FileInfo(this.Dbfile);
            var dir = fi.Directory;
            if (!dir.Exists)
            {
                dir.Create();
            }
            //缓存数据防止读写失败
            if (fi.Exists)
            {
                fi.CopyTo(tempFileName, true);
            }
            Stream writer = new FileStream(this.Dbfile, FileMode.Create, FileAccess.ReadWrite);
            ks.Serialize(writer, this.entityList);
            writer.Close();
            //删除缓存数据
            if (File.Exists(tempFileName))
            {
                File.Delete(tempFileName);
            }
            UpdateSchema();

        }
        catch (Exception ex)
        {
            //恢复数据
            throw ex;

        }

    }
    /// <summary>
    /// 更新数据的元数据信息
    /// </summary>
    private void UpdateSchema()
    {
        string root = Path.GetDirectoryName(this.Dbfile);
        var files = Directory.GetFiles(root, "*.xml").Where(r => !r.EndsWith("schema.xml")).Select(r => Path.GetFileName(r));
        //构建xml并存储
        string schemaFile = Path.Combine(root, "schema.xml");
        XDocument xdoc = new XDocument();
        XElement xmlroot = new XElement("root");
        xdoc.Add(xmlroot);
        foreach (var item in files)
        {
            xdoc.Root.Add(new XElement("fileName", item));
        }
        xdoc.Save(schemaFile);
    }


    /// <summary>
    /// 根据主键或Id获取实体对象
    /// </summary>
    /// <param name="KeyOrId"></param>
    /// <returns></returns>
    public T Find(string KeyOrId)
    {
        Type t = typeof(T);
        foreach (var inst in this.entityList)
        {
            foreach (PropertyInfo pro in t.GetProperties())
            {
                var keyattr = pro.GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), false);
                if (keyattr != null || pro.Name.ToLower() == "id")
                {
                    if (pro.GetValue(inst, null)?.ToString() == KeyOrId)
                    {
                        return inst;
                    }
                }
            }
        }
        return default(T);
    }



    public void Add(T entity)
    {
        this.entityList.Add(entity);
        this.WriteDb();
    }

    public void AddRange(List<T> list)
    {
        this.entityList.AddRange(list);
        this.WriteDb();
    }

    public List<T> ToList()
    {
        this.ReadDb();
        return entityList;
    }

    /// <summary>
    /// 根据条件移除元素
    /// </summary>
    /// <param name="filters"></param>
    public void Remove(Predicate<T> filters)
    {
        this.entityList.RemoveAll(filters);
        this.WriteDb();
    }

    /// <summary>
    /// 根据key或id移除元素
    /// </summary>
    /// <param name="filters"></param>
    public void Remove(string KeyOrId)
    {
        Type t = typeof(T);
        T entity = default(T);
        foreach (var inst in this.entityList)
        {

            foreach (PropertyInfo pro in t.GetProperties())
            {
                var keyattr = pro.GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), false);
                if (keyattr != null || pro.Name.ToLower() == "id")
                {
                    if (pro.GetValue(inst, null)?.ToString() == KeyOrId)
                    {
                        entity = inst;
                        goto FinishLoop;

                    }
                }
            }
        }
        FinishLoop:
        entityList.Remove(entity);
        this.WriteDb();
    }


    public void UpdateByIdOrKey(T entity)
    {
        Type t = typeof(T);
        string id = string.Empty;
        foreach (PropertyInfo pro in t.GetProperties())
        {
            var keyattr = pro.GetCustomAttribute(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), false);
            if (keyattr != null || pro.Name.ToLower() == "id")
            {
                id = pro.GetValue(entity, null)?.ToString();
                break;
            }
        }

        this.Remove(id);
        this.Add(entity);
    }



    /// <summary>
    /// 清空列表
    /// </summary>
    /// <param name="filters"></param>
    public void Clear()
    {
        entityList.Clear();
        this.WriteDb();
    }


}

推荐阅读