Browse Source

import redis wrap

HOME 2 years ago
parent
commit
a3d7c3c192

+ 10 - 0
VCommon.Caching/CacheExpire.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace VCommon.Caching
+{
+    public class CacheExpire
+    {
+        public TimeSpan? Specified { get; set; }
+        public DateTime? To { get; set; }
+    }
+}

+ 211 - 0
VCommon.Caching/CacheManager.cs

@@ -0,0 +1,211 @@
+using StackExchange.Redis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using VCommon.Caching.Internals;
+using VCommon.Json;
+
+namespace VCommon.Caching
+{
+    public class CacheManager
+    {
+        private readonly RedisCacheAccess _cacheAccess;
+
+        public CacheManager(string server) => _cacheAccess = CacheAccess.CreateRedisAccessInstance(server);
+
+        public void KeyExpire(int db, string key, CacheExpire expire)
+        {
+            if (null != expire)
+            {
+                if (expire.To.HasValue) _cacheAccess.CacheOp(db, p => p.KeyExpire(key, expire.To.Value));
+                if (expire.Specified.HasValue) _cacheAccess.CacheOp(db, p => p.KeyExpire(key, expire.Specified.Value));
+            }
+        }
+
+        public bool KeyExist(int db, string key) => _cacheAccess.CacheOp(db, p => p.KeyExists(key));
+
+        public bool KeyDelete(int db, string key) => _cacheAccess.CacheOp(db, p => p.KeyDelete(key));
+
+        public void KeySetJson(int db, string key, object value, CacheExpire expire = null)
+        {
+            var json = VJsonSerializer.Serialize(value);
+            _cacheAccess.CacheOp(db, p => p.StringSet(key, json));
+            KeyExpire(db, key, expire);
+        }
+
+        public T KeyGetJson<T>(int db, string key) => VJsonSerializer.Deserialize<T>(_cacheAccess.CacheOp(db, p => p.StringGet(key)));
+
+        public void KeySet(int db, string key, CacheValue value, CacheExpire expire = null)
+        {
+            _cacheAccess.CacheOp(db, p => p.StringSet(key, value));
+            KeyExpire(db, key, expire);
+        }
+
+        public CacheValue KeyGet(int db, string key) => _cacheAccess.CacheOp(db, p => p.StringGet(key));
+
+        public bool HashFieldExist(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashExists(hashKey, hashField));
+
+        public bool HashFieldDelete(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashDelete(hashKey, hashField));
+
+        public long HashFieldDelete(int db, string hashKey, params string[] hashFields)
+        {
+            if (0 == hashFields.Length) return 0;
+            var fields = hashFields.Select(p => (RedisValue)p).ToArray();
+            return _cacheAccess.CacheOp(db, p => p.HashDelete(hashKey, fields));
+        }
+
+        public CacheValue HashFieldFetch(int db, string hashKey, string hashField, Func<CacheValue> missingCacheValueProvider, CacheExpire expire = null)
+        {
+            CacheValue cv = _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField));
+            if (false == cv.IsNull) return cv;
+
+            var through = missingCacheValueProvider();
+            if (through.IsNull) return through;
+
+            _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, through));
+            KeyExpire(db, hashKey, expire);
+
+            return through;
+        }
+
+        public T HashFieldFetchJson<T>(int db, string hashKey, string hashField, Func<T> missingCacheValueProvider, CacheExpire expire = null)
+        {
+            string json = _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField));
+            if (null != json) return VJsonSerializer.Deserialize<T>(json);
+
+            var through = missingCacheValueProvider();
+
+            var json2 = VJsonSerializer.Serialize(through);
+            _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, json2));
+            KeyExpire(db, hashKey, expire);
+
+            return through;
+        }
+
+        public T HashFieldGetJson<T>(int db, string hashKey, string hashField)
+        {
+            var json = (string)_cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField));
+            return VJsonSerializer.Deserialize<T>(json);
+        }
+
+        public void HashFieldSetJson<T>(int db, string hashKey, string hashField, T value, CacheExpire expire = null)
+        {
+            var json = VJsonSerializer.Serialize(value);
+            _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, json));
+            KeyExpire(db, hashKey, expire);
+        }
+
+        public void HashFieldSetJson<T>(int db, string hashKey, IReadOnlyDictionary<string, T> entries, CacheExpire expire = null)
+        {
+            var hashEntrys = entries.Select(p => new HashEntry(p.Key, VJsonSerializer.Serialize(p.Value))).ToArray();
+            _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashEntrys));
+            KeyExpire(db, hashKey, expire);
+        }
+
+        public void HashFieldPurgeAndSet(int db, string hashKey, IReadOnlyDictionary<string, CacheValue> hashFields)
+        {
+            var entries = hashFields.Select(p => new HashEntry(p.Key, p.Value)).ToArray();
+            if (0 == entries.Length) return;
+            _cacheAccess.CacheOp(db, p =>
+            {
+                var batch = p.CreateBatch();
+                batch.KeyDeleteAsync(hashKey);
+                batch.HashSetAsync(hashKey, entries);
+                batch.Execute();
+            });
+        }
+
+        public void HashFieldDeleteByPattern(int db, string hashKey, string pattern)
+        {
+            var entries = _cacheAccess.CacheOp(db, p => p.HashScan(hashKey, pattern, int.MaxValue)).Select(p => p.Name).ToArray();
+            _cacheAccess.CacheOp(db, p => p.HashDelete(hashKey, entries));
+        }
+
+        public IReadOnlyDictionary<string, CacheValue> HashFieldGetByPattern(int db, string hashKey, string pattern)
+        {
+            return _cacheAccess.CacheOp(db, p => p.HashScan(hashKey, pattern, int.MaxValue))
+                .ToDictionary(p => (string)p.Name, p => (CacheValue)p.Value);
+        }
+
+        public IReadOnlyDictionary<string, T> HashFieldGetByPatternJson<T>(int db, string hashKey, string pattern)
+        {
+            return _cacheAccess.CacheOp(db, p => p.HashScan(hashKey, pattern, int.MaxValue))
+                .ToDictionary(p => (string)p.Name, p => VJsonSerializer.Deserialize<T>(p.Value));
+        }
+
+        public void HashFieldSet(int db, string hashKey, string hashField, CacheValue value, CacheExpire expire = null)
+        {
+            _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, value));
+            KeyExpire(db, hashKey, expire);
+        }
+
+        public CacheValue HashFieldGet(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField));
+
+        public void HashFieldSet(int db, string hashKey, IReadOnlyDictionary<string, CacheValue> hashField, CacheExpire expire = null)
+        {
+            var entries = hashField.Select(p => new HashEntry(p.Key, p.Value)).ToArray();
+            if (0 == entries.Length) return;
+            _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, entries));
+            KeyExpire(db, hashKey, expire);
+        }
+
+        public long HashFieldIncrement(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashIncrement(hashKey, hashField));
+
+        public long HashFieldDecrement(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashDecrement(hashKey, hashField));
+
+        public IReadOnlyDictionary<string, CacheValue> HashFieldGet(int db, string hashKey, params string[] hashFields)
+        {
+            if (0 == hashFields.Length) return new Dictionary<string, CacheValue>();
+
+            var keys = hashFields.Distinct().ToArray();
+            var fields = keys.Select(p => (RedisValue)p).ToArray();
+            return _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, fields))
+                .Select((p, i) => new { k = keys[i], v = p })
+                .ToDictionary(p => p.k, p => (CacheValue)p.v);
+        }
+
+        public IReadOnlyDictionary<TK, TV> HashFieldsFetchAsDic<TK, TV>(int db, string key, IEnumerable<TK> hashFields,
+            Func<TK, string> convertHashField,
+            Func<IEnumerable<TK>, IReadOnlyDictionary<TK, TV>> missingCacheValueProvider,
+            Func<TV, CacheValue> convertValueToCache,
+            Func<CacheValue, TV> convertCacheToValue,
+            CacheExpire expire = null)
+        {
+            var result = new Dictionary<TK, TV>();
+
+            var missingFields = new List<TK>(); //get from cache,and collect missing
+            {
+                var fields = hashFields.Distinct().ToArray();
+                if (0 == fields.Length) return result;
+
+                var hf = fields.Select(p => (RedisValue)convertHashField(p)).ToArray();
+
+                var fromCache = _cacheAccess.CacheOp(db, p => p.HashGet(key, hf));
+
+                for (var i = 0; i < fromCache.Length; i++)
+                {
+                    if (false == fromCache[i].HasValue) missingFields.Add(fields[i]);
+                    else result[fields[i]] = convertCacheToValue(fromCache[i]);
+                }
+            }
+
+            if (0 != missingFields.Count) //get from db,and put cache
+            {
+                var fromDb = missingCacheValueProvider(missingFields);
+                var entries = fromDb.Select(p => new HashEntry(convertHashField(p.Key), convertValueToCache(p.Value)))
+                    .Where(p => false == p.Value.IsNull)
+                    .ToArray();
+                _cacheAccess.CacheOp(db, p => p.HashSet(key, entries));
+
+                //merge to result
+                foreach (var kvp in fromDb) result[kvp.Key] = kvp.Value;
+            }
+
+            KeyExpire(db, key, expire);
+
+            return result;
+        }
+
+        public string[] SearchKeys(int db, string pattern) => _cacheAccess.SearchKeys(db, pattern).Select(p => (string)p).ToArray();
+    }
+}

+ 60 - 0
VCommon.Caching/CacheValue.cs

@@ -0,0 +1,60 @@
+using System;
+using StackExchange.Redis;
+
+namespace VCommon.Caching
+{
+    public struct CacheValue
+    {
+        private RedisValue _redisValue;
+
+        public static implicit operator CacheValue(RedisValue value) => new CacheValue { _redisValue = value };
+
+        public static implicit operator RedisValue(CacheValue self) => self._redisValue;
+
+        public static explicit operator CacheValue(long value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(long? value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(double value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(double? value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(bool value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(bool? value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(string value) => new CacheValue { _redisValue = value };
+
+        public static explicit operator CacheValue(Guid value) => new CacheValue { _redisValue = value.ToString() };
+
+        public static explicit operator CacheValue(Guid? value) => new CacheValue { _redisValue = value?.ToString() };
+
+        public static explicit operator CacheValue(DateTime value) => new CacheValue { _redisValue = value.Ticks };
+
+        public static explicit operator CacheValue(DateTime? value) => new CacheValue { _redisValue = value?.Ticks };
+
+        public static explicit operator double(CacheValue self) => (double)self._redisValue;
+
+        public static explicit operator double? (CacheValue self) => (double?)self._redisValue;
+
+        public static explicit operator long(CacheValue self) => (long)self._redisValue;
+
+        public static explicit operator long? (CacheValue self) => (long?)self._redisValue;
+
+        public static explicit operator bool(CacheValue self) => (bool)self._redisValue;
+
+        public static explicit operator bool? (CacheValue self) => (bool?)self._redisValue;
+
+        public static explicit operator string(CacheValue self) => self._redisValue;
+
+        public static explicit operator Guid(CacheValue self) => new Guid((string)self._redisValue);
+
+        public static explicit operator Guid? (CacheValue self) => self._redisValue.IsNull ? (Guid?)null : new Guid((string)self._redisValue);
+
+        public static explicit operator DateTime? (CacheValue self) => self._redisValue.IsNull ? (DateTime?)null : new DateTime((long)self._redisValue);
+
+        public static explicit operator DateTime(CacheValue self) => new DateTime((long)self._redisValue);
+
+        public bool IsNull => _redisValue.IsNull;
+    }
+}

+ 76 - 0
VCommon.Caching/FixedCacheManager.cs

@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+
+namespace VCommon.Caching
+{
+    public class FixedCacheManager
+    {
+        private readonly int _fixedDb;
+        private readonly CacheManager _underlying;
+
+        public FixedCacheManager(string server, int fixedDb)
+        {
+            _fixedDb = fixedDb;
+            _underlying = new CacheManager(server);
+        }
+
+        public void KeyExpire(string key, CacheExpire expire) => _underlying.KeyExpire(_fixedDb, key, expire);
+
+        public bool KeyExist(string key) => _underlying.KeyExist(_fixedDb, key);
+
+        public bool KeyDelete(string key) => _underlying.KeyDelete(_fixedDb, key);
+
+        public void KeySetJson(string key, object value, CacheExpire expire = null) => _underlying.KeySetJson(_fixedDb, key, value, expire);
+
+        public T KeyGetJson<T>(string key) => _underlying.KeyGetJson<T>(_fixedDb, key);
+
+        public void KeySet(string key, CacheValue value, CacheExpire expire = null) => _underlying.KeySet(_fixedDb, key, value, expire);
+
+        public CacheValue KeyGet(string key) => _underlying.KeyGet(_fixedDb, key);
+
+        public bool HashFieldExist(string hashKey, string hashField) => _underlying.HashFieldExist(_fixedDb, hashKey, hashField);
+
+        public bool HashFieldDelete(string hashKey, string hashField) => _underlying.HashFieldDelete(_fixedDb, hashKey, hashField);
+
+        public long HashFieldDelete(string hashKey, params string[] hashFields) => _underlying.HashFieldDelete(_fixedDb, hashKey, hashFields);
+
+        public CacheValue HashFieldFetch(string hashKey, string hashField, Func<CacheValue> missingCacheValueProvider, CacheExpire expire = null) => _underlying.HashFieldFetch(_fixedDb, hashKey, hashField, missingCacheValueProvider, expire);
+
+        public T HashFieldFetchJson<T>(string hashKey, string hashField, Func<T> missingCacheValueProvider, CacheExpire expire = null) => _underlying.HashFieldFetchJson(_fixedDb, hashKey, hashField, missingCacheValueProvider, expire);
+
+        public void HashFieldSetJson<T>(string hashKey, string hashField, T value, CacheExpire expire = null) => _underlying.HashFieldSetJson(_fixedDb, hashKey, hashField, value, expire);
+
+        public void HashFieldSetJson<T>(string hashKey, IReadOnlyDictionary<string, T> entries, CacheExpire expire = null) => _underlying.HashFieldSetJson(_fixedDb, hashKey, entries, expire);
+
+        public T HashFieldGetJson<T>(string hashKey, string hashField) => _underlying.HashFieldGetJson<T>(_fixedDb, hashKey, hashField);
+
+        public void HashFieldPurgeAndSet(string hashKey, IReadOnlyDictionary<string, CacheValue> hashFields) => _underlying.HashFieldPurgeAndSet(_fixedDb, hashKey, hashFields);
+
+        public void HashFieldDeleteByPattern(string hashKey, string pattern) => _underlying.HashFieldDeleteByPattern(_fixedDb, hashKey, pattern);
+
+        public IReadOnlyDictionary<string, CacheValue> HashFieldGetByPattern(string hashKey, string pattern) => _underlying.HashFieldGetByPattern(_fixedDb, hashKey, pattern);
+
+        public IReadOnlyDictionary<string, T> HashFieldGetByPatternJson<T>(string hashKey, string pattern) => _underlying.HashFieldGetByPatternJson<T>(_fixedDb, hashKey, pattern);
+
+        public void HashFieldSet(string hashKey, string hashField, CacheValue value, CacheExpire expire = null) => _underlying.HashFieldSet(_fixedDb, hashKey, hashField, value, expire);
+
+        public CacheValue HashFieldGet(string hashKey, string hashField) => _underlying.HashFieldGet(_fixedDb, hashKey, hashField);
+
+        public void HashFieldSet(string hashKey, IReadOnlyDictionary<string, CacheValue> hashField, CacheExpire expire = null) => _underlying.HashFieldSet(_fixedDb, hashKey, hashField, expire);
+
+        public long HashFieldIncrement(string hashKey, string hashField) => _underlying.HashFieldIncrement(_fixedDb, hashKey, hashField);
+
+        public long HashFieldDecrement(string hashKey, string hashField) => _underlying.HashFieldDecrement(_fixedDb, hashKey, hashField);
+
+        public IReadOnlyDictionary<string, CacheValue> HashFieldGet(string hashKey, params string[] hashFields) => _underlying.HashFieldGet(_fixedDb, hashKey, hashFields);
+
+        public IReadOnlyDictionary<TK, TV> HashFieldsFetchAsDic<TK, TV>(string key, IEnumerable<TK> hashFields,
+            Func<TK, string> convertHashField,
+            Func<IEnumerable<TK>, IReadOnlyDictionary<TK, TV>> missingCacheValueProvider,
+            Func<TV, CacheValue> convertValueToCache,
+            Func<CacheValue, TV> convertCacheToValue,
+            CacheExpire expire = null) => _underlying.HashFieldsFetchAsDic(_fixedDb, key, hashFields, convertHashField, missingCacheValueProvider, convertValueToCache, convertCacheToValue, expire);
+
+        public string[] SearchKeys(string pattern) => _underlying.SearchKeys(_fixedDb, pattern);
+    }
+}

+ 64 - 0
VCommon.Caching/HashCacheManager.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+
+namespace VCommon.Caching
+{
+    public class HashCacheManager
+    {
+        private readonly string _key;
+        private readonly FixedCacheManager _underlying;
+
+        public HashCacheManager(string server, int db, string key)
+        {
+            _key = key;
+            _underlying = new FixedCacheManager(server, db);
+        }
+
+        public void Clean() => _underlying.KeyDelete(_key);
+
+        public bool Exist(string field) => _underlying.HashFieldExist(_key, field);
+
+        public bool Delete(string field) => _underlying.HashFieldDelete(_key, field);
+
+        public long Delete(params string[] fields) => _underlying.HashFieldDelete(_key, fields);
+
+        public CacheValue Fetch(string field, Func<CacheValue> missingCacheValueProvider, CacheExpire expire = null) => _underlying.HashFieldFetch(_key, field, missingCacheValueProvider, expire);
+
+        public T FetchJson<T>(string field, Func<T> missingCacheValueProvider, CacheExpire expire = null) => _underlying.HashFieldFetchJson(_key, field, missingCacheValueProvider, expire);
+
+        public void SetJson<T>(string field, T value, CacheExpire expire = null) => _underlying.HashFieldSetJson(_key, field, value, expire);
+
+        public T GetJson<T>(string field) => _underlying.HashFieldGetJson<T>(_key, field);
+
+        public void PurgeAndSet(IReadOnlyDictionary<string, CacheValue> fields) => _underlying.HashFieldPurgeAndSet(_key, fields);
+
+        public void DeleteByPattern(string pattern) => _underlying.HashFieldDeleteByPattern(_key, pattern);
+
+        public IReadOnlyDictionary<string, CacheValue> GetByPattern(string pattern) => _underlying.HashFieldGetByPattern(_key, pattern);
+
+        public IReadOnlyDictionary<string, T> GetByPatternJson<T>(string pattern) => _underlying.HashFieldGetByPatternJson<T>(_key, pattern);
+
+        public void Set(string field, CacheValue value, CacheExpire expire = null) => _underlying.HashFieldSet(_key, field, value, expire);
+
+        public CacheValue Get(string field) => _underlying.HashFieldGet(_key, field);
+
+        public void Set(IReadOnlyDictionary<string, CacheValue> field, CacheExpire expire = null) => _underlying.HashFieldSet(_key, field, expire);
+
+        public long Increment(string field) => _underlying.HashFieldIncrement(_key, field);
+
+        public long Decrement(string field) => _underlying.HashFieldDecrement(_key, field);
+
+        public IReadOnlyDictionary<string, CacheValue> Get(params string[] fields) => _underlying.HashFieldGet(_key, fields);
+
+        public IReadOnlyDictionary<TK, TV> FetchAsDic<TK, TV>(
+            IEnumerable<TK> fields,
+            Func<TK, string> convertField, Func<IEnumerable<TK>,
+            IReadOnlyDictionary<TK, TV>> missingCacheValueProvider,
+            Func<TV, CacheValue> convertValueToCache,
+            Func<CacheValue, TV> convertCacheToValue,
+            CacheExpire expire = null)
+        {
+            return _underlying.HashFieldsFetchAsDic(_key, fields, convertField, missingCacheValueProvider, convertValueToCache, convertCacheToValue, expire);
+        }
+    }
+}

+ 10 - 0
VCommon.Caching/Internals/CacheAccess.cs

@@ -0,0 +1,10 @@
+namespace VCommon.Caching.Internals
+{
+    internal abstract class CacheAccess
+    {
+        public static RedisCacheAccess CreateRedisAccessInstance(string server)
+        {
+            return new RedisCacheAccess(server);
+        }
+    }
+}

+ 35 - 0
VCommon.Caching/Internals/RedisCacheAccess.cs

@@ -0,0 +1,35 @@
+using StackExchange.Redis;
+using System;
+using System.Linq;
+using VCommon.Diagnostics;
+
+namespace VCommon.Caching.Internals
+{
+    internal class RedisCacheAccess : CacheAccess
+    {
+        private readonly ConnectionMultiplexer _conn;
+
+        internal RedisCacheAccess(string server) => _conn = ConnectionMultiplexer.Connect(server);
+
+        // --- database ---
+
+        private IDatabase GetDatabase(int db) => _conn.GetDatabase(db);
+
+        public void CacheOp(int db, Action<IDatabase> action)
+        {
+            using (new TimeoutWarnLog(nameof(CacheAccess))) action(GetDatabase(db));
+        }
+
+        public T CacheOp<T>(int db, Func<IDatabase, T> action)
+        {
+            using (new TimeoutWarnLog(nameof(CacheAccess))) return action(GetDatabase(db));
+        }
+
+        public RedisKey[] SearchKeys(int db, string pattern)
+        {
+            return _conn.GetEndPoints()
+                .SelectMany(p => _conn.GetServer(p).Keys(db, pattern, int.MaxValue))
+                .ToArray();
+        }
+    }
+}

+ 16 - 0
VCommon.Caching/VCommon.Caching.csproj

@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net5.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="StackExchange.Redis" Version="2.2.62" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\VCommon.Json\VCommon.Json.csproj" />
+    <ProjectReference Include="..\VCommon\VCommon.csproj" />
+  </ItemGroup>
+
+</Project>

+ 6 - 0
VCommonCore.sln

@@ -44,6 +44,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "@", "@", "{FEA6F92D-28D4-4C
 		README.md = README.md
 	EndProjectSection
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VCommon.Caching", "VCommon.Caching\VCommon.Caching.csproj", "{3FA626FD-0A30-42EE-A45F-1923544B5042}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -118,6 +120,10 @@ Global
 		{4310976A-CBDB-4568-984F-34B3A214F329}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4310976A-CBDB-4568-984F-34B3A214F329}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4310976A-CBDB-4568-984F-34B3A214F329}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3FA626FD-0A30-42EE-A45F-1923544B5042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3FA626FD-0A30-42EE-A45F-1923544B5042}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3FA626FD-0A30-42EE-A45F-1923544B5042}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3FA626FD-0A30-42EE-A45F-1923544B5042}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE