Xaml/Xml 实现对象与存储分离
2014-02-10 14:10 stoneniqiu 阅读(1715) 评论(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; } }
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); } }
这个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; } }
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; } }
现在这个任务就拿去用了。
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; } }
当然需要在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~
你的关注和支持是我写作的最大动力~
书山有路群:452450927