using ExpressionTreeToString; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Tango.BL; using Tango.BL.Entities; using Tango.FSE.Web.Messages; namespace Tango.FSE.BL { public abstract class EntityRepositoryBase : FSEServiceBase where TEntity : ObservableEntity where TCachedEntity : ObservableEntityDTO { private MemoryCacheDictionary _memoryCache; public EntityRepositoryBase() { _memoryCache = MemoryCache.GetOrCreateCache(typeof(TEntity).Name); } protected Task> FindAll(Expression> expression, params DataResolverNode[] nodes) { //Validate expression if (expression.ToString().Contains("Convert(")) { throw new NotSupportedException($"Type conversion is not supported by LiteDB. '{expression.ToString()}'"); } return DataResolver>.Builder.New() .ConfigureCascade(nodes) .WithLogsCallingName(typeof(TEntity).Name.PluralizeMVC()) .InMemoryCache((context) => { return _memoryCache .ToList(true) .Where(GetCacheExpression(expression).Compile()) .ToList() .Select(x => ConvertToEntity(x)) .ToList(); }) .Web((context) => { List entities = new List(); if (typeof(TEntity) == typeof(TechMonitor)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Monitors = true }).GetAwaiter().GetResult() .Monitors.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(TechIo)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { IOs = true }).GetAwaiter().GetResult() .IOs.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(TechDispenser)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Dispensers = true }).GetAwaiter().GetResult() .Dispensers.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(TechController)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Controllers = true }).GetAwaiter().GetResult() .Controllers.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(TechHeater)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Heaters = true }).GetAwaiter().GetResult() .Heaters.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(TechValve)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Valves = true }).GetAwaiter().GetResult() .Valves.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(HardwareMotorType)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Motors = true }).GetAwaiter().GetResult() .Motors.Select(x => x.ToObservable()) .Cast().ToList(); } else if (typeof(TEntity) == typeof(HardwareBlowerType)) { entities = WebClient.GetTechComponents(new GetTechComponentsRequest() { Blowers = true }).GetAwaiter().GetResult() .Blowers.Select(x => x.ToObservable()) .Cast().ToList(); } List cachedEntities = new List(); try { foreach (var entity in entities) { var cachedEntity = ConvertToCached(entity); cachedEntities.Add(cachedEntity); _memoryCache.Put(entity.Guid, cachedEntity); } } catch (Exception ex) { LogManager.Log(ex, $"Error occurred while trying to cache entities of type '{typeof(TEntity).Name}' to memory."); } try { using (var cache = DiskCache.CreateContext()) { var collection = cache.Database.GetCollection(); foreach (var cachedEntity in cachedEntities) { collection.Upsert(cachedEntity); } } } catch (Exception ex) { LogManager.Log(ex, $"Error occurred while trying to cache entities of type '{typeof(TEntity).Name}' to disk."); } return entities; }) .Online((context) => { using (ObservablesContext db = ObservablesContext.CreateDefault()) { var entities = db.Set().Where(expression).ToList(); List cachedEntities = new List(); try { foreach (var entity in entities) { var cachedEntity = ConvertToCached(entity); cachedEntities.Add(cachedEntity); _memoryCache.Put(entity.Guid, cachedEntity); } } catch (Exception ex) { LogManager.Log(ex, $"Error occurred while trying to cache entities of type '{typeof(TEntity).Name}' to memory."); } try { using (var cache = DiskCache.CreateContext()) { var collection = cache.Database.GetCollection(); foreach (var cachedEntity in cachedEntities) { collection.Upsert(cachedEntity); } } } catch (Exception ex) { LogManager.Log(ex, $"Error occurred while trying to cache entities of type '{typeof(TEntity).Name}' to disk."); } return entities; } }) .DiskCache((context) => { using (var cache = DiskCache.CreateContext()) { List entities = new List(); var cachedEntities = cache.Database.GetCollection().Find(GetCacheExpression(expression)).ToList(); foreach (var cachedEntity in cachedEntities) { _memoryCache.Put(cachedEntity.Guid, cachedEntity); entities.Add(ConvertToEntity(cachedEntity)); } return entities; } }) .BuildExecuteAsync(); } protected Task FindOne(Expression> expression, params DataResolverNode[] nodes) { //Validate expression if (expression.ToString().Contains("Convert(")) { throw new NotSupportedException($"Type conversion is not supported by LiteDB. '{expression.ToString()}'"); } return DataResolver.Builder.New() .ConfigureCascade(nodes) .WithLogsCallingName(typeof(TEntity).Name.PluralizeMVC()) .InMemoryCache((context) => { return ConvertToEntity(_memoryCache .ToList(true) .First(GetCacheExpression(expression).Compile())); }) .Online((context) => { using (ObservablesContext db = ObservablesContext.CreateDefault()) { var entity = db.Set().First(expression); try { TCachedEntity cachedEntity = null; cachedEntity = ConvertToCached(entity); _memoryCache.Put(entity.Guid, cachedEntity); try { using (var cache = DiskCache.CreateContext()) { var collection = cache.Database.GetCollection(); collection.Upsert(cachedEntity); } } catch (Exception ex) { LogManager.Log(ex, $"Error caching entity '{entity.Guid}' of type '{typeof(TEntity).Name}' to disk."); } } catch (Exception ex) { LogManager.Log(ex, $"Error caching entity '{entity.Guid}' of type '{typeof(TEntity).Name}'."); } return entity; } }) .DiskCache((context) => { using (var cache = DiskCache.CreateContext()) { var cachedEntity = cache.Database.GetCollection().FindOne(GetCacheExpression(expression)); if (cachedEntity == null) { throw new KeyNotFoundException("The entity was not found on the disk cache."); } _memoryCache.Put(cachedEntity.Guid, cachedEntity); return ConvertToEntity(cachedEntity); } }) .BuildExecuteAsync(); } protected virtual Expression> GetCacheExpression(Expression> expression) { var param = Expression.Parameter(typeof(TCachedEntity), expression.Parameters[0].Name); var result = new CustomExpVisitor(param).Visit(expression.Body); Expression> lambda = Expression.Lambda>(result, new[] { param }); return lambda; } protected abstract TCachedEntity ConvertToCached(TEntity entity); protected abstract TEntity ConvertToEntity(TCachedEntity cachedEntity); private class CustomExpVisitor : ExpressionVisitor { ParameterExpression _param; public CustomExpVisitor(ParameterExpression param) { _param = param; } protected override Expression VisitParameter(ParameterExpression node) { return _param; } protected override Expression VisitMember(MemberExpression node) { if (node.Member.MemberType == MemberTypes.Property) { MemberExpression memberExpression = null; var memberName = node.Member.Name; var otherMember = typeof(T).GetProperty(memberName); if (otherMember == null) { return base.VisitMember(node); } memberExpression = Expression.Property(Visit(node.Expression), otherMember); return memberExpression; } else { return base.VisitMember(node); } } } } }