代码改变世界

Xaml/Xml 实现对象与存储分离

2014-02-10 14:10  stoneniqiu  阅读(1708)  评论(2编辑  收藏  举报

     刚开始用xml存储东西的时候都是不断的在xml文件里面添加或者修改xml的节点,这个是很常见的做法,这方面的博客也很多我也就不介绍了。但其实在小批量存储的时候我们可以直接将对象存进xml/xaml,使用的时候将整个对象加载出来,操作完成后再保存下去,这种做法没有什么技术难点,但我只是觉得更加的面相对象,模型和存储可以分开,模型的接口可以暴露出来,让前端的或者后台的调用,而存储可以换成xml/xaml和数据库。这样的好处就不言而喻了。

一、创建仓库

    1.仓库的接口及基类

 /// <summary>
    /// Interface  Repository
    /// </summary>
    public interface IRepository
    {
        /// <summary>
        /// Loads this instance.
        /// </summary>
        void Load(Type type); // 可以加载不同的类型,xml,xaml
        /// <summary>
        /// Saves this instance.
        /// </summary>
        void Save();

        /// <summary>
        /// model
        /// </summary>
        object Model { get; set; }//我们要操作的对象
    }
 /// <summary>
    /// Class RepositoryBase
    /// </summary>
    public abstract class RepositoryBase : IRepository
    {
        /// <summary>
        /// Loads this instance.
        /// </summary>
        public virtual void Load(Type type)
        {
        }

        /// <summary>
        /// Saves this instance.
        /// </summary>
        public virtual void Save()
        {
        }

        /// <summary>
        /// model
        /// </summary>
        /// <value>The model.</value>
        public object Model { get; set; }

        /// <summary>
        /// 存储模型文件
        /// </summary>
        public string FileName { get; set; }
    }
View Code

    2.Xaml仓库实现,在读取和写入的时候你还可以进行加密。特别是作为一些重要的工程配置文件,但又不想公开的时候。FileName 叫 FilePath更合适

 /// <summary>
    /// xaml仓库
    /// </summary>
    public class XamlRepository : RepositoryBase 
    {
        public XamlRepository()
        {
        }

        public XamlRepository(string fileName)
        {
            FileName = fileName;
        }

        public XamlRepository(string fileName, object model)
        {
            FileName = fileName;
            Model = model;
        }

        public override void Load(Type type)
        {
            if (!File.Exists(FileName))
            {
                return;
            }
            var content = File.ReadAllText(FileName);
            //解密
            // var encrypt = new Encrypt();
            //  content = encrypt.DecryptString(content);
            using (var reader = XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes(content))))
            {
                Model = XamlReader.Load(reader);
                reader.Close();
            }
        }

        public override void Save()
        {
            var dir = Path.GetDirectoryName(FileName);
            if (dir != null && !Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            var settings = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = ("\t"),
                OmitXmlDeclaration = true
            };
            string content;
            using (var ms = new MemoryStream())
            {
                using (var xmlWriter = XmlWriter.Create(ms, settings))
                {
                    XamlWriter.Save(Model, xmlWriter);
                    xmlWriter.Close();
                }
                ms.Position = 0;
                using (var reader = new StreamReader(ms))
                {
                    content = reader.ReadToEnd();
                    reader.Close();
                }
                ms.Close();
            }
            //加密
            //  var encrypt = new Encrypt();
            // content = encrypt.EncryptString(content);
            File.WriteAllText(FileName, content);
        }
    }
View Code

 

   这个xaml仓库就可以像一个基础设施来服务于模型了。xaml和xml都作为这种存储文件没有多大的区别,两者排版不同。xaml是一个对象一个节点,对象的属性将成为节点的属性,xml就是一层层的父子节点。

 

二、创建模型

  模型设计就看你自身切实的考虑了,我这里例举一个文件下载的模型。这个模型有个集合类Filegroup。

    [Serializable]
    public class MyFileInfo
    {
        public int Id { get; set; }
        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName { get; set; }
        /// <summary>
        /// 上传者ID
        /// </summary>
        public int UserId { get; set; }
        /// <summary>
        /// 上传时间
        /// </summary>
        public DateTime UploadTime { get; set; }
        /// <summary>
        /// 下载次数
        /// </summary>
        public int DownloadTimes { get; set; }
        /// <summary>
        /// 是否可见
        /// </summary>
        public bool IsVisible { get; set; }

        /// <summary>
        /// 文件类型
        /// </summary>
        public FileType FileType { get; set; }


        /// <summary>
        /// 对应的集合类
        /// </summary>
        [XmlIgnore, DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public FileGroup FileGroup { get; set; }
    }

  1.要加入序列化标签 [Serializable],

  2.[XmlIgnore, DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 可以忽略掉不想序列化的类。DesignerSerializationVisibility 这个枚举类型还有     Visible,Content,Visible和Hidden对应,Content用于集合元素,序列化它的内部类。

  3.当一个类型中包含有继承类的子类事,需要用XmlInclude标签。

   

   [XmlInclude(typeof(UserCustomAlarmFormat))]
    public class AlarmContentFormat{

    public List<AlarmPropertyFormat> AlarmPropertyFormats   

    }

public class UserCustomAlarmFormat : AlarmPropertyFormat
{

public string Content { get; set; }
public UserCustomAlarmFormat()
{
Name = "用户自定义";
}
}

 

 生成的文档会xsi的备注

 另外content的例子比如:

 public class FileGroup
    {
        private FileCollection _files;
     [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public FileCollection Files {
            get { return _files ?? (_files = new FileCollection()); }
            set { _files = value; }
        }
//...... }

 这里的FileCollection是一个集合类,是list和字典的结合。这里可以直接换成List<T>

三、任务加载

   现在仓库和模型都创建了,我们就可以用applet来封装,这个功能要用的时候就加载这个applet,不用的时候就拿掉。

  1.任务接口

 public interface IServerApplet
    {
        /// <summary>
        /// Called when [init].
        /// </summary>
        void OnInit();
        /// <summary>
        /// Called when [start].
        /// </summary>
        void OnStart();
        /// <summary>
        /// Called when [stop].
        /// </summary>
        void OnStop();
        /// <summary>
        /// Called when [exit].
        /// </summary>
        void OnExit();

        void Onload();

        /// <summary>
        /// Gets or sets the repository.
        /// </summary>
        /// <value>The repository.</value>
        RepositoryBase Repository { get; set; }

       
    }
View Code

  2.任务实现

 public class DownloadApplet : IServerApplet
    {
 
        public  string FilePath = @"D:\VS2012\Support\Main\Protal\Protal.Web.Framework\Data\File.xaml";
        public string RealFilePath = @"../../Content";

        #region 构造函数

        public DownloadApplet()
        {

        }

        public DownloadApplet(ProjectContext projectContext)
        {
            ProjectContext = projectContext;
        }

        #endregion

        #region 属性

        public FileGroup FileGroup { get; private set; }

     
        #endregion


        public void OnInit()
        {
            
            Onload();
        }

        public void OnStart()
        {

        }

        public void OnStop()
        {

        }

        public void OnExit()
        {
            OnSave();
        }

        public void Onload()
        {
            Repository = Repository ?? new XamlRepository(FilePath);
            Repository.Load(typeof(FileGroup));
            FileGroup = (Repository.Model as FileGroup) ?? new FileGroup();
            Logger.Debug("DownloadApplet 开始加载");
        }

        public void OnSave()
        {
            Repository.Model = FileGroup = FileGroup ?? new FileGroup();
            Repository.Save();
        }
      
        public RepositoryBase Repository { get; set; }
    }
View Code

现在这个任务就拿去用了。 

 3.任务管理

 当任务很多的时候,可以再创建一个AppletManager类 来管理这些任务,决定哪些加载哪些不加载。这里我默认加载了DownloadApplet

 public  class AppletManager
    {
        private static AppletManager _instance;
        private List<IServerApplet> _applets;

        public AppletManager()
        {
            Applets.Add(new DownloadApplet());
        }

        public List<IServerApplet> Applets
        {
            get { return _applets??(_applets=new List<IServerApplet>()); }
            set { _applets = value; }
        }

        /// <summary>
        /// 启动工程
        /// </summary>
        public void Start()
        {
            foreach (IServerApplet applet in Applets)
                applet.OnStart();
        }

        /// <summary>
        /// 停止工程
        /// </summary>
        public void Stop()
        {
            foreach (IServerApplet applet in _applets)
                applet.OnStop();
            foreach (IServerApplet applet in _applets)
                applet.OnExit();
            _applets.Clear();
        }

        /// <summary>
        /// 退出工程,开发时使用
        /// </summary>
        public void Exit()
        {
            foreach (IServerApplet applet in _applets)
                applet.OnExit();
            _applets.Clear();
        }

        public static AppletManager GetInstance(bool always = true)
        {
            if (_instance == null && always)
                _instance = new AppletManager();
            return _instance;
        }
    }
View Code

 当然需要在Global.asax中启动。

     protected void Application_Start()
        {
......... //
var proj = AppletManager.GetInstance(); proj.Start(); Logger.Debug("工程开始启动"); }

 

四、应用

  这里我是在MVC的controller里面调用,模型对象加载之后就可以直接用了。需要的时候save一下。

    public class FileController : Controller
    {
       
        // GET: /Fileprivate readonly DownloadApplet _applet = AppletManager.GetInstance().Applets[0] as DownloadApplet;
        private readonly FileGroup _fileGroup;
        public FileController()
        {
            if (_applet == null) return;
            _applet.Onload();
            _fileGroup = _applet.FileGroup ?? new FileGroup();
        }/// <summary>
        /// TransmitFile的方式下载   
        /// </summary>
        /// <param name="pathstr"></param>
        public void TransmitFileLoad(string pathstr)
        {
            var strs = pathstr.Split('/');
            var sname = strs[4];
            var extensionname = sname.Split('.')[1];
            Response.Clear();
            Response.ContentType = GetContentType(extensionname);
            Response.AddHeader("Content-Disposition", "attachment;fileName=" + sname);
            var b = pathstr.IndexOf('/') + 1;
            var serverpath = pathstr.Substring(b, pathstr.Length - b);
            string fileName = Server.MapPath(serverpath);
            Response.TransmitFile(fileName);
            Response.End();

            //统计次数
            var file = _fileGroup.Files.Find(m => m.FileName == sname);
            if (file != null)
            {
                file.DownloadTimes += 1;
                _applet.OnSave();
            }
        }

这样每次使用模型对象的时候不必再检索每个字段,其实就是序列化的一种应用场景,主要是将模型和存储分开了,模型你可以继续扩展,存储你可以实现xml的仓库,sqlserver的仓库,任务模块分开,便于控制。

     这只是个小例子,马年第一次分享,喜欢就支持下,tks~