using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace NzbDrone.Core.Datastore { public class TableMapper { private readonly HashSet _allowedOrderBy = new HashSet(StringComparer.OrdinalIgnoreCase); public TableMapper() { IgnoreList = new Dictionary>(); LazyLoadList = new Dictionary>(); TableMap = new Dictionary(); } public Dictionary> IgnoreList { get; set; } public Dictionary> LazyLoadList { get; set; } public Dictionary TableMap { get; set; } public ColumnMapper Entity(string tableName) where TEntity : ModelBase { var type = typeof(TEntity); TableMap.Add(type, tableName); if (IgnoreList.TryGetValue(type, out var list)) { return new ColumnMapper(list, LazyLoadList[type], _allowedOrderBy); } IgnoreList[type] = new List(); LazyLoadList[type] = new List(); return new ColumnMapper(IgnoreList[type], LazyLoadList[type], _allowedOrderBy); } public List ExcludeProperties(Type x) { return IgnoreList.ContainsKey(x) ? IgnoreList[x] : new List(); } public string TableNameMapping(Type x) { return TableMap.ContainsKey(x) ? TableMap[x] : null; } public string SelectTemplate(Type x) { return $"SELECT /**select**/ FROM \"{TableMap[x]}\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/ /**orderby**/"; } public string DeleteTemplate(Type x) { return $"DELETE FROM \"{TableMap[x]}\" /**where**/"; } public string PageCountTemplate(Type x) { return $"SELECT /**select**/ FROM \"{TableMap[x]}\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/"; } public bool IsValidSortKey(string sortKey) { string table = null; if (sortKey.Contains('.')) { var split = sortKey.Split('.'); if (split.Length != 2) { return false; } table = split[0]; sortKey = split[1]; } if (table != null && !TableMap.Values.Contains(table, StringComparer.OrdinalIgnoreCase)) { return false; } if (!_allowedOrderBy.Contains(sortKey)) { return false; } return true; } public string GetSortKey(string sortKey) { string table = null; if (sortKey.Contains('.')) { var split = sortKey.Split('.'); if (split.Length != 2) { return sortKey; } table = split[0]; sortKey = split[1]; } if (table != null && !TableMap.Values.Contains(table, StringComparer.OrdinalIgnoreCase)) { return sortKey; } if (!_allowedOrderBy.Contains(sortKey)) { return sortKey; } return _allowedOrderBy.First(x => x.Equals(sortKey, StringComparison.OrdinalIgnoreCase)); } } public class LazyLoadedProperty { public PropertyInfo Property { get; set; } public ILazyLoaded LazyLoad { get; set; } } public class ColumnMapper where T : ModelBase { private readonly List _ignoreList; private readonly List _lazyLoadList; private readonly HashSet _allowedOrderBy; public ColumnMapper(List ignoreList, List lazyLoadList, HashSet allowedOrderBy) { _ignoreList = ignoreList; _lazyLoadList = lazyLoadList; _allowedOrderBy = allowedOrderBy; } public ColumnMapper AutoMapPropertiesWhere(Func predicate) { var properties = typeof(T).GetProperties(); _ignoreList.AddRange(properties.Where(x => !predicate(x))); _allowedOrderBy.UnionWith(properties.Where(x => predicate(x)).Select(x => x.Name)); return this; } public ColumnMapper RegisterModel() { return AutoMapPropertiesWhere(x => x.IsMappableProperty()); } public ColumnMapper Ignore(Expression> property) { _ignoreList.Add(property.GetMemberName()); return this; } public ColumnMapper LazyLoad(Expression>> property, Func query, Func condition) { var lazyLoad = new LazyLoaded(query, condition); var item = new LazyLoadedProperty { Property = property.GetMemberName(), LazyLoad = lazyLoad }; _lazyLoadList.Add(item); return this; } public ColumnMapper HasOne(Expression>> portalExpression, Func childIdSelector) where TChild : ModelBase { return LazyLoad(portalExpression, (db, parent) => { var id = childIdSelector(parent); return db.Query(new SqlBuilder(db.DatabaseType).Where(x => x.Id == id)).SingleOrDefault(); }, parent => childIdSelector(parent) > 0); } } }