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