C#之文件缓存
写在开头
今天就放假了,照理说应该写今年的总结了,但是回头一看,很久没有写过技术类的文字了,还是先不吐槽了。
关于文件缓存
写了很多的代码,常常在写EXE(定时任务)或者写小站点(数据的使用和客户端调用之间)都需要用到缓存,数据在内存和文本都保留一个整体。
当然也可以写到数据库,不过个人觉得不方便查看和管理。(数据量不大)
第一个版本
一般来说,就是一个缓存类,然后存放缓存文件路径,真实数据集合,读写集合,读写文件。
先提供一个公用类
public class TestInfo
{
public string Name { get; set; } public int Age { get; set; } public string Value { get; set; }
}
测试公有类
然后是第一个对文件读写操作的缓存了
public class Cache_Test_1
{
private static List<TestInfo> dataList = new List<TestInfo>(); private static readonly string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", "test.txt"); private static readonly string SEP_STR = "---"; static Cache_Test_1()
{
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(cachePath))
{
string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
foreach (var line in lines)
{
string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
if (lineArray.Length == )
{
dataList.Add(new TestInfo()
{
Name = lineArray[],
Age = int.Parse(lineArray[]),
Value = lineArray[]
});
}
}
}
} public static void AddInfo(TestInfo info)
{
lock (dataList)
{
var item = dataList.Find(p => p.Name == info.Name);
if (item == null)
{
dataList.Add(info);
}
else
{
item.Age = info.Age;
item.Value = info.Value;
} WriteFile();
}
} public static TestInfo GetInfo(string name)
{
lock (dataList)
{
return dataList.Find(p => p.Name == name);
}
} public static List<TestInfo> GetAll()
{
lock (dataList)
{
return dataList;
}
} private static void WriteFile()
{
StringBuilder content = new StringBuilder();
foreach (var item in dataList)
{
content.AppendLine(item.Name + SEP_STR + item.Age + SEP_STR + item.Value);
} File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
}
}
版本1,文件缓存,固定数据格式和类型
但是,这样的操作如果多了起来,问题就出来了。每次写一种缓存就要写一个缓存操作的类了,写着写着,这样的体力活就有点累了,于是
穷则思变,就想,写一个通用一点的吧。于是又了第二个版本
第二个版本
这个版本的目的就是解决重复劳动,见代码
public class Cache_Test_2<T> where T : new()
{
/// <summary>
/// 缓存分隔符
/// </summary>
private static readonly string SEP_STR = "---"; /// <summary>
/// 缓存临时对象集合
/// </summary>
private static List<T> dataList = new List<T>(); /// <summary>
/// 缓存文本路径
/// </summary>
private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt"); static Cache_Test_2()
{
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(cachePath))
{
Type t = typeof(T);
string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
foreach (var line in lines)
{
string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
if (line.Contains(SEP_STR))
{
List<PropertyIndexInfo> list = new List<PropertyIndexInfo>();
T model = new T();
PropertyInfo[] ps = t.GetProperties();
for (int i = ; i < lineArray.Length; i++)
{
var p = ps[i];
if (p.PropertyType == typeof(int))
{
p.SetValue(model, Convert.ToInt32(lineArray[i]), null);
}
else if (p.PropertyType == typeof(string))
{
p.SetValue(model, lineArray[i], null);
}
} dataList.Add(model);
}
}
}
} /// <summary>
/// 新增一个缓存
/// </summary>
/// <param name="t"></param>
public static void Add(T t)
{
lock (dataList)
{
dataList.Add(t); WriteFile();
}
} /// <summary>
/// 读取缓存集合
/// </summary>
/// <returns></returns>
public static List<T> GetAll()
{
lock (dataList)
{
return dataList;
}
} /// <summary>
/// 写入缓存文件(全量)
/// </summary>
private static void WriteFile()
{
StringBuilder content = new StringBuilder();
foreach (var item in dataList)
{
List<string> list = new List<string>();
var ps = typeof(T).GetProperties();
foreach (var p in ps)
{
object p_object = p.GetValue(item, null);
string value = p_object.ToString();
list.Add(value);
} content.AppendLine(string.Join(SEP_STR, list.ToArray()));
} File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
}
}
版本2,通用文件缓存(适合一般场景)
虽然,第二个版本出来了,但是大多数时候,我们创建缓存都是在已有的类上面进行操作,不然每次创建缓存可能就需要一个CacheModel这样一个对象了,
这样还有,并不是所有的字段我们都是需要进入缓存文件,这样的情况该如何操作呢,于是我们再次优化了一下代码,出现了目前来说的第三个版本了。
第三个版本
这里,就会新增几个类了,为了解决第二个版本不能解决的问题,当然具体使用还是要看业务场景,因为,更通用就代表更依赖配置了。(代码类)
这里需要几个基本类,用来保存临时管理的。
/// <summary>
/// 特性:指定属性的排序和是否出现在缓存中使用
/// </summary>
public class CacheOrderAttribute : Attribute
{
public int Index { get; set; }
} /// <summary>
/// 对字符串分割的结果的值进行排序
/// </summary>
public class CacheIndexInfo
{
public int Index { get; set; } public string Value { get; set; }
} /// <summary>
/// 对字段的属性进行排序
/// </summary>
public class PropertyIndexInfo
{
public int Index { get; set; } public PropertyInfo PropertyInfo { get; set; }
}
特性和通用缓存用到的类
有了特性,我们就能对单个类的属性进行特性筛查,排序,最终得到我们需要的和不需要的,最开始改好了,楼主测试了一下,速度上还可以,但是数据了一多,
这种每次全部写入文件的方式就low了,于是改成了Append的方式,大大提升了速度。同时添加了一个类,对具体分割的值进行调整的。代码如下:
/// <summary>
/// 文件缓存共有类
/// </summary>
/// <typeparam name="T">类</typeparam>
public class File_Common_Cache<T> where T : new()
{
/// <summary>
/// 缓存分隔符
/// </summary>
private static string SEP_STR = "---"; /// <summary>
/// 缓存临时对象集合
/// </summary>
private static List<T> dataList = new List<T>(); /// <summary>
/// 缓存文本路径
/// </summary>
private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt"); static File_Common_Cache()
{
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(cachePath))
{
Type t = typeof(T);
string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
foreach (var line in lines)
{
string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
if (line.Contains(SEP_STR))
{
List<PropertyIndexInfo> list = new List<PropertyIndexInfo>();
T model = new T();
PropertyInfo[] ps = t.GetProperties();
foreach (var p in ps)
{
var ads = p.GetCustomAttributesData();
if (ads.Count > )
{
int index = Convert.ToInt32(ads[].NamedArguments[].TypedValue.Value);
list.Add(new PropertyIndexInfo() { Index = index, PropertyInfo = p });
}
} list = list.OrderBy(p => p.Index).ToList();
for (int i = ; i < list.Count; i++)
{
var pt = list[i].PropertyInfo.PropertyType;
if (pt == typeof(int))
{
list[i].PropertyInfo.SetValue(model, Convert.ToInt32(lineArray[i]), null);
}
else if (pt == typeof(string))
{
list[i].PropertyInfo.SetValue(model, lineArray[i], null);
}
else if (pt == typeof(DateTime))
{
list[i].PropertyInfo.SetValue(model, Convert.ToDateTime(lineArray[i]), null);
}
else
{
try
{
list[i].PropertyInfo.SetValue(model, (object)lineArray[i], null);
}
catch
{
throw new Exception("不支持属性类型(仅支持,int,string,DateTime,object)");
}
}
} dataList.Add(model);
}
}
}
} /// <summary>
/// 初始化配置(修改默认分割和保存文件使用)
/// </summary>
/// <param name="sep_str">分隔符</param>
/// <param name="fileName">缓存文件名</param>
public static void InitSet(string sep_str, string fileName)
{
SEP_STR = sep_str;
cachePath = fileName;
} /// <summary>
/// 新增一个缓存
/// </summary>
/// <param name="t"></param>
public static void Add(T t)
{
lock (dataList)
{
dataList.Add(t); AppendFile(t);
}
} /// <summary>
/// 移除一个缓存
/// </summary>
/// <param name="t"></param>
public static void Remove(T t)
{ } /// <summary>
/// 读取缓存集合
/// </summary>
/// <returns></returns>
public static List<T> GetAll()
{
lock (dataList)
{
return dataList;
}
} /// <summary>
/// 写入缓存文件(全量)
/// </summary>
private static void WriteFile()
{
StringBuilder content = new StringBuilder();
foreach (var item in dataList)
{
List<CacheIndexInfo> list = new List<CacheIndexInfo>();
var ps = typeof(T).GetProperties();
foreach (var p in ps)
{
var ads = p.GetCustomAttributesData();
if (ads.Count > )
{
int index = Convert.ToInt32(ads[].NamedArguments[].TypedValue.Value);
object p_object = p.GetValue(item, null);
string value = string.Empty;
if (p.PropertyType == typeof(DateTime))
{
value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") :
Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
value = p_object == null ? "" : p_object.ToString();
} list.Add(new CacheIndexInfo() { Index = index, Value = value });
}
} list = list.OrderBy(a => a.Index).ToList();
content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
} File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
} /// <summary>
/// 写入缓存文件(附加)
/// </summary>
/// <param name="t"></param>
private static void AppendFile(T t)
{
StringBuilder content = new StringBuilder();
List<CacheIndexInfo> list = new List<CacheIndexInfo>();
var ps = typeof(T).GetProperties();
foreach (var p in ps)
{
var ads = p.GetCustomAttributesData();
if (ads.Count > )
{
int index = Convert.ToInt32(ads[].NamedArguments[].TypedValue.Value);
object p_object = p.GetValue(t, null);
string value = string.Empty;
if (p.PropertyType == typeof(DateTime))
{
value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") :
Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
value = p_object == null ? "" : p_object.ToString();
} list.Add(new CacheIndexInfo() { Index = index, Value = value });
}
} list = list.OrderBy(a => a.Index).ToList();
content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
File.AppendAllText(cachePath, content.ToString(), Encoding.UTF8);
}
}
终版(文件缓存类)
测试代码
class Program
{
static void Main(string[] args)
{
TInfo info = new TInfo();
info.Name = "test";
info.Age = ;
info.Test = "我是测试字符串";
var list = File_Common_Cache<TInfo>.GetAll();
DateTime startTime = DateTime.Now;
for (int i = ; i < ; i++)
{
File_Common_Cache<TInfo>.Add(info);
} TimeSpan span = DateTime.Now - startTime;
Console.WriteLine(span.TotalMilliseconds);
Console.ReadLine();
}
} public class TInfo
{
[CacheOrder(Index = )]
public string Name { get; set; } [CacheOrder(Index = )]
public int Age { get; set; } [CacheOrder(Index = )]
public string Test { get; set; }
}
测试代码
测试结果:1秒不到,还是可以。
总结
没啥好总结的,但是不这样写,感觉不规范。写代码的过程中,总是会遇到,写着写着就变成体力活的代码,这个时候,我们就应该认识到问题了,把体力活改变一下,就不再是体力活。
让代码更简单,让生活个多彩。
不足之处,望包含,拍砖,丢鸡蛋。
C#之文件缓存的更多相关文章
- PHP文件缓存实现
有些时候,我们不希望使用redis等第三方缓存,使得系统依赖于其他服务.这时候,文件缓存会是一个不错的选择. 我们需要文件缓存实现哪些共更能: 功能实现:get.set.has.increment.d ...
- 高性能文件缓存key-value存储—Redis
1.高性能文件缓存key-value存储-Memcached 2.ASP.NET HttpRuntime.Cache缓存类使用总结 备注:三篇博文结合阅读,简单理解并且使用,如果想深入学习,请多参考文 ...
- [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html 这个可以实现ImageView异步加载 ...
- php使用文件缓存
使用php读取mysql中的数据很简单,数据量不大的时候,mysql的性能还是不错的.但是有些查询可能比较耗时,这时可以把查询出的结果,缓存起来,减轻mysql的查询压力. 缓存的方法有几种:使用me ...
- 高性能文件缓存key-value存储—Memcached
1.高性能文件缓存key-value存储—Redis 2.ASP.NET HttpRuntime.Cache缓存类使用总结 备注:三篇博文结合阅读,简单理解并且使用,如果想深入学习,请多参考文章中给出 ...
- htaccess 增加静态文件缓存和压缩
增加图片视频等静态文件缓存: <FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf)$"> Header set Cache-Cont ...
- PHP文件缓存与memcached缓存 相比 优缺点是什么呢【总结】
如果不考虑其他任何问题,只比较速度的话,那肯定是mem快,但他们各有优缺点.文件缓存优点:1.由于现在的硬盘都非常大,所有如果是大数据的时候,放硬盘里就比较合适,比如做一个cms网站,网站里有10万篇 ...
- app缓存设计-文件缓存
采用缓存,可以进一步大大缓解数据交互的压力,又能提供一定的离线浏览.下边我简略列举一下缓存管理的适用环境: 1. 提供网络服务的应用 2. 数据更新不需要实时更新,哪怕是3-5分钟的延迟也是可以采用缓 ...
- phalcon: 缓存片段,文件缓存,memcache缓存
几种缓存,需要用到前端配置,加后端实例配合着用 片段缓存: public function indexAction() { //渲染页面 $this->view->setTemplateA ...
- js和HTML结合(补充知识:如何防止文件缓存的js代码)
来自<javascript高级程序设计 第三版:作者Nicholas C. Zakas>的学习笔记(二) 使用html标签<script>可以把js嵌入到html页面中,让脚本 ...
随机推荐
- JS中使用正则表达式替换对象里的大小写
function parse(obj){ var str = JSON.stringify(obj); var dataObj = str.replace(/Name/g, "name& ...
- python网络编程基础
一.客户端/服务器架构 网络中到处都应有了C/S架构,我们学习socket就是为了完成C/S架构的开发. 二.scoket与网络协议 如果想要实现网络通信我们需要对tcpip,http等很多网络知识有 ...
- 十二个 ASP.NET Core 例子——IOC
目录 简单介绍 core自带IOC的实现解释 1.简单介绍 (个人理解) 是什么:IOC是一种设计原则,而非设计模式,是对流程控制,当你注入你需要的定制化类时,流程就确定了 怎么用:和IOC容器说你这 ...
- windows 防火墙拦截nginx的问题
今天在azure vm上安装了nginx并配置了代理设置,但域名访问始终无法中转,一开始怀疑是nginx的服务没起来,但在本地访问localhost看下如下界面,证明服务是没问题的. 本地访问没问题, ...
- PHP和Python如何选择?或许可以考虑这三个问题
撤稿纠错 文/黄小天.李亚洲 (选自Hackernoon 机器之心编译) 2017 年可谓是网页应用与 API 之年,开发者不用每次重新发明轮子,而是利用脚手架和第三方库就能确保项目在几天内实时部署. ...
- Xshell学习--菜鸟篇
http://www.cnblogs.com/perseverancevictory/p/4910145.html 1)关于Xshell 网上更多的资料里提到的SSH客户端是putty,因为简单.开源 ...
- ffmpeg批量实现视频转码命令行
ffmpeg实现视频转码命令行,result需要提前建好作为保存转码后的视频路径: ffmpeg -i .mp4 -vcodec h264 "result\1.mp4" 当有大量视 ...
- ASIHTTPRequest
ASIHTTPRequest,是一个直接在CFNetwork上做的开源项目,提供了一个比官方更方便更强大的HTTP网络传输的封装.
- 【js 实践】js 实现木桶布局
还有两个月左右就要准备实习了,所以特意练一练,今天终于搞定了js 的木桶布局了 这一个是按照一个插件的规格去写的以防以后工作需要,详细的解释在前端网这里 http://www.qdfuns.com/n ...
- linux_ssh
什么是ssh? 配置文件位置:/etc/ssh/sshd_config 远程登录和为其他网络提供安全的加密数据传输协议,默认端口22,默认协议是SSH2 # 远程终端通过ssh连接服务器管理服务器 # ...