File Cache in C#
Keywords:
C#
encoding
Attribute
Database
less
Write at the beginning
Today is a holiday, it should be said that this year's summary should be written. But looking back, I haven't written technical texts for a long time.
About File Caching
Write a lot of code, often in writing EXE (timing tasks) or writing small sites (between the use of data and client calls) need to use caching, data in memory and text are retained as a whole.
Of course, it can also be written to the database, but I find it inconvenient to view and manage. (Small amount of data)
First Edition
Generally speaking, it is a caching class, which stores the path of the cached file, the real data set, the read-write set and the read-write file.
Provide a common class first
1 public class TestInfo
2 {
3 public string Name { get; set; }
4
5 public int Age { get; set; }
6
7 public string Value { get; set; }
8 }
Testing public classes
Then it's the first cache to read and write files.
1 public class Cache_Test_1
2 {
3 private static List<TestInfo> dataList = new List<TestInfo>();
4
5 private static readonly string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", "test.txt");
6
7 private static readonly string SEP_STR = "---";
8
9 static Cache_Test_1()
10 {
11 string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
12 if (!Directory.Exists(dir))
13 {
14 Directory.CreateDirectory(dir);
15 }
16
17 if (File.Exists(cachePath))
18 {
19 string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
20 foreach (var line in lines)
21 {
22 string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
23 if (lineArray.Length == 3)
24 {
25 dataList.Add(new TestInfo()
26 {
27 Name = lineArray[0],
28 Age = int.Parse(lineArray[1]),
29 Value = lineArray[2]
30 });
31 }
32 }
33 }
34 }
35
36 public static void AddInfo(TestInfo info)
37 {
38 lock (dataList)
39 {
40 var item = dataList.Find(p => p.Name == info.Name);
41 if (item == null)
42 {
43 dataList.Add(info);
44 }
45 else
46 {
47 item.Age = info.Age;
48 item.Value = info.Value;
49 }
50
51 WriteFile();
52 }
53 }
54
55 public static TestInfo GetInfo(string name)
56 {
57 lock (dataList)
58 {
59 return dataList.Find(p => p.Name == name);
60 }
61 }
62
63 public static List<TestInfo> GetAll()
64 {
65 lock (dataList)
66 {
67 return dataList;
68 }
69 }
70
71 private static void WriteFile()
72 {
73 StringBuilder content = new StringBuilder();
74 foreach (var item in dataList)
75 {
76 content.AppendLine(item.Name + SEP_STR + item.Age + SEP_STR + item.Value);
77 }
78
79 File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
80 }
81 }
Version 1, File Cache, Fixed Data Format and Type
However, if there are more such operations, the problem will arise. Every time you write a cache, you have to write a class of cache operations. It says, "This kind of manual work is a little tired, so I'm going to write a class of cache operations."
Poverty makes you think about change. Just think about writing a more general one. So there's a second version.
Second Edition
The purpose of this version is to solve duplication of effort, see the code.
1 public class Cache_Test_2<T> where T : new()
2 {
3 /// <summary>
4 /// Cache separator
5 /// </summary>
6 private static readonly string SEP_STR = "---";
7
8 /// <summary>
9 /// Caching temporary object sets
10 /// </summary>
11 private static List<T> dataList = new List<T>();
12
13 /// <summary>
14 /// Cached text path
15 /// </summary>
16 private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt");
17
18 static Cache_Test_2()
19 {
20 string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
21 if (!Directory.Exists(dir))
22 {
23 Directory.CreateDirectory(dir);
24 }
25
26 if (File.Exists(cachePath))
27 {
28 Type t = typeof(T);
29 string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
30 foreach (var line in lines)
31 {
32 string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
33 if (line.Contains(SEP_STR))
34 {
35 List<PropertyIndexInfo> list = new List<PropertyIndexInfo>();
36 T model = new T();
37 PropertyInfo[] ps = t.GetProperties();
38 for (int i = 0; i < lineArray.Length; i++)
39 {
40 var p = ps[i];
41 if (p.PropertyType == typeof(int))
42 {
43 p.SetValue(model, Convert.ToInt32(lineArray[i]), null);
44 }
45 else if (p.PropertyType == typeof(string))
46 {
47 p.SetValue(model, lineArray[i], null);
48 }
49 }
50
51 dataList.Add(model);
52 }
53 }
54 }
55 }
56
57 /// <summary>
58 /// Add a new cache
59 /// </summary>
60 /// <param name="t"></param>
61 public static void Add(T t)
62 {
63 lock (dataList)
64 {
65 dataList.Add(t);
66
67 WriteFile();
68 }
69 }
70
71 /// <summary>
72 /// Read the cache collection
73 /// </summary>
74 /// <returns></returns>
75 public static List<T> GetAll()
76 {
77 lock (dataList)
78 {
79 return dataList;
80 }
81 }
82
83 /// <summary>
84 /// Write to Cache File(Full quantity)
85 /// </summary>
86 private static void WriteFile()
87 {
88 StringBuilder content = new StringBuilder();
89 foreach (var item in dataList)
90 {
91 List<string> list = new List<string>();
92 var ps = typeof(T).GetProperties();
93 foreach (var p in ps)
94 {
95 object p_object = p.GetValue(item, null);
96 string value = p_object.ToString();
97 list.Add(value);
98 }
99
100 content.AppendLine(string.Join(SEP_STR, list.ToArray()));
101 }
102
103 File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
104 }
105 }
Version 2, Universal File Caching (for general scenarios)
Although the second version comes out, most of the time we create caches on existing classes, otherwise we may need an object like CacheModel every time we create caches.
What's more, not all fields need to be cached, so how can we operate this situation? So we optimized the code again, and the third version appeared.
Third Edition
Here, we will add several classes, in order to solve the problem that the second version can not solve, of course, the specific use depends on the business scenario, because more generic means more dependent on configuration. (Code class)
There are several basic classes needed to save temporary management.
1 /// <summary>
2 /// Features: Specify the sort of attributes and whether they appear in the cache
3 /// </summary>
4 public class CacheOrderAttribute : Attribute
5 {
6 public int Index { get; set; }
7 }
8
9 /// <summary>
10 /// Sort the values of the results of string segmentation
11 /// </summary>
12 public class CacheIndexInfo
13 {
14 public int Index { get; set; }
15
16 public string Value { get; set; }
17 }
18
19 /// <summary>
20 /// Sort the properties of fields
21 /// </summary>
22 public class PropertyIndexInfo
23 {
24 public int Index { get; set; }
25
26 public PropertyInfo PropertyInfo { get; set; }
27 }
Features and classes for general cache
With features, we can screen and sort the attributes of a single class, and eventually get what we need and don't need. At first, it was improved. The landlord tested it, and the speed was OK, but there was a lot of data.
This way of writing all files at a time is low, so it's changed to Append, which greatly improves the speed. At the same time, a class is added to adjust the value of the specific segmentation. The code is as follows:
1 /// <summary>
2 /// File Cache Common Classes
3 /// </summary>
4 /// <typeparam name="T">class</typeparam>
5 public class File_Common_Cache<T> where T : new()
6 {
7 /// <summary>
8 /// Cache separator
9 /// </summary>
10 private static string SEP_STR = "---";
11
12 /// <summary>
13 /// Caching temporary object sets
14 /// </summary>
15 private static List<T> dataList = new List<T>();
16
17 /// <summary>
18 /// Cached text path
19 /// </summary>
20 private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt");
21
22 static File_Common_Cache()
23 {
24 string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
25 if (!Directory.Exists(dir))
26 {
27 Directory.CreateDirectory(dir);
28 }
29
30 if (File.Exists(cachePath))
31 {
32 Type t = typeof(T);
33 string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
34 foreach (var line in lines)
35 {
36 string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
37 if (line.Contains(SEP_STR))
38 {
39 List<PropertyIndexInfo> list = new List<PropertyIndexInfo>();
40 T model = new T();
41 PropertyInfo[] ps = t.GetProperties();
42 foreach (var p in ps)
43 {
44 var ads = p.GetCustomAttributesData();
45 if (ads.Count > 0)
46 {
47 int index = Convert.ToInt32(ads[0].NamedArguments[0].TypedValue.Value);
48 list.Add(new PropertyIndexInfo() { Index = index, PropertyInfo = p });
49 }
50 }
51
52 list = list.OrderBy(p => p.Index).ToList();
53 for (int i = 0; i < list.Count; i++)
54 {
55 var pt = list[i].PropertyInfo.PropertyType;
56 if (pt == typeof(int))
57 {
58 list[i].PropertyInfo.SetValue(model, Convert.ToInt32(lineArray[i]), null);
59 }
60 else if (pt == typeof(string))
61 {
62 list[i].PropertyInfo.SetValue(model, lineArray[i], null);
63 }
64 else if (pt == typeof(DateTime))
65 {
66 list[i].PropertyInfo.SetValue(model, Convert.ToDateTime(lineArray[i]), null);
67 }
68 else
69 {
70 try
71 {
72 list[i].PropertyInfo.SetValue(model, (object)lineArray[i], null);
73 }
74 catch
75 {
76 throw new Exception("Attribute types are not supported(Only support, int,string,DateTime,object)");
77 }
78 }
79 }
80
81 dataList.Add(model);
82 }
83 }
84 }
85 }
86
87 /// <summary>
88 /// Initialize configuration (modify default partition and save file usage)
89 /// </summary>
90 /// <param name="sep_str">Separator</param>
91 /// <param name="fileName">Cache File Name</param>
92 public static void InitSet(string sep_str, string fileName)
93 {
94 SEP_STR = sep_str;
95 cachePath = fileName;
96 }
97
98 /// <summary>
99 /// Add a new cache
100 /// </summary>
101 /// <param name="t"></param>
102 public static void Add(T t)
103 {
104 lock (dataList)
105 {
106 dataList.Add(t);
107
108 AppendFile(t);
109 }
110 }
111
112 /// <summary>
113 /// Remove a cache
114 /// </summary>
115 /// <param name="t"></param>
116 public static void Remove(T t)
117 {
118
119 }
120
121 /// <summary>
122 /// Read the cache collection
123 /// </summary>
124 /// <returns></returns>
125 public static List<T> GetAll()
126 {
127 lock (dataList)
128 {
129 return dataList;
130 }
131 }
132
133 /// <summary>
134 /// Write to Cache File(Full quantity)
135 /// </summary>
136 private static void WriteFile()
137 {
138 StringBuilder content = new StringBuilder();
139 foreach (var item in dataList)
140 {
141 List<CacheIndexInfo> list = new List<CacheIndexInfo>();
142 var ps = typeof(T).GetProperties();
143 foreach (var p in ps)
144 {
145 var ads = p.GetCustomAttributesData();
146 if (ads.Count > 0)
147 {
148 int index = Convert.ToInt32(ads[0].NamedArguments[0].TypedValue.Value);
149 object p_object = p.GetValue(item, null);
150 string value = string.Empty;
151 if (p.PropertyType == typeof(DateTime))
152 {
153 value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") :
154 Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss");
155 }
156 else
157 {
158 value = p_object == null ? "" : p_object.ToString();
159 }
160
161 list.Add(new CacheIndexInfo() { Index = index, Value = value });
162 }
163 }
164
165 list = list.OrderBy(a => a.Index).ToList();
166 content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
167 }
168
169 File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
170 }
171
172 /// <summary>
173 /// Write to the cache file (attached))
174 /// </summary>
175 /// <param name="t"></param>
176 private static void AppendFile(T t)
177 {
178 StringBuilder content = new StringBuilder();
179 List<CacheIndexInfo> list = new List<CacheIndexInfo>();
180 var ps = typeof(T).GetProperties();
181 foreach (var p in ps)
182 {
183 var ads = p.GetCustomAttributesData();
184 if (ads.Count > 0)
185 {
186 int index = Convert.ToInt32(ads[0].NamedArguments[0].TypedValue.Value);
187 object p_object = p.GetValue(t, null);
188 string value = string.Empty;
189 if (p.PropertyType == typeof(DateTime))
190 {
191 value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") :
192 Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss");
193 }
194 else
195 {
196 value = p_object == null ? "" : p_object.ToString();
197 }
198
199 list.Add(new CacheIndexInfo() { Index = index, Value = value });
200 }
201 }
202
203 list = list.OrderBy(a => a.Index).ToList();
204 content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
205 File.AppendAllText(cachePath, content.ToString(), Encoding.UTF8);
206 }
207 }
Final Edition (File Cache Class)
Test code
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 TInfo info = new TInfo();
6 info.Name = "test";
7 info.Age = 10;
8 info.Test = "I'm a test string";
9 var list = File_Common_Cache<TInfo>.GetAll();
10 DateTime startTime = DateTime.Now;
11 for (int i = 0; i < 1000; i++)
12 {
13 File_Common_Cache<TInfo>.Add(info);
14 }
15
16 TimeSpan span = DateTime.Now - startTime;
17 Console.WriteLine(span.TotalMilliseconds);
18 Console.ReadLine();
19 }
20 }
21
22 public class TInfo
23 {
24 [CacheOrder(Index = 0)]
25 public string Name { get; set; }
26
27 [CacheOrder(Index = 2)]
28 public int Age { get; set; }
29
30 [CacheOrder(Index = 1)]
31 public string Test { get; set; }
32 }
Test code
Test results: less than 1 second, or can.
summary
It's not a good summary, but it's not standard to write like that. In the process of writing code, we always encounter that writing becomes manual work. At this time, we should recognize the problem. If we change physical work, it will no longer be manual work.
Make the code simpler and make life more colorful.
Deficiencies, include, pat bricks, throw eggs.
Posted by roice on Sat, 18 May 2019 14:34:00 -0700