Fixed: Rename more than 999 movies in one go

Also pulls across Dapper improvements from Readarr.

Fixes Sentry RADARR-1V
Fixes #4359
This commit is contained in:
ta264
2020-05-10 22:01:07 +01:00
parent 93d27c70c4
commit 2c6b13dd11
16 changed files with 840 additions and 263 deletions

View File

@@ -3,48 +3,36 @@ using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Dapper;
using NzbDrone.Common.Reflection;
namespace NzbDrone.Core.Datastore
{
public static class MappingExtensions
{
public static PropertyInfo GetMemberName<T>(this Expression<Func<T, object>> member)
{
var memberExpression = member.Body as MemberExpression;
if (memberExpression == null)
{
memberExpression = (member.Body as UnaryExpression).Operand as MemberExpression;
}
return (PropertyInfo)memberExpression.Member;
}
}
public class TableMapper
{
public TableMapper()
{
IgnoreList = new Dictionary<Type, List<PropertyInfo>>();
LazyLoadList = new Dictionary<Type, List<LazyLoadedProperty>>();
TableMap = new Dictionary<Type, string>();
}
public Dictionary<Type, List<PropertyInfo>> IgnoreList { get; set; }
public Dictionary<Type, List<LazyLoadedProperty>> LazyLoadList { get; set; }
public Dictionary<Type, string> TableMap { get; set; }
public ColumnMapper<TEntity> Entity<TEntity>(string tableName)
where TEntity : ModelBase
{
TableMap.Add(typeof(TEntity), tableName);
var type = typeof(TEntity);
TableMap.Add(type, tableName);
if (IgnoreList.TryGetValue(typeof(TEntity), out var list))
if (IgnoreList.TryGetValue(type, out var list))
{
return new ColumnMapper<TEntity>(list);
return new ColumnMapper<TEntity>(list, LazyLoadList[type]);
}
list = new List<PropertyInfo>();
IgnoreList[typeof(TEntity)] = list;
return new ColumnMapper<TEntity>(list);
IgnoreList[type] = new List<PropertyInfo>();
LazyLoadList[type] = new List<LazyLoadedProperty>();
return new ColumnMapper<TEntity>(IgnoreList[type], LazyLoadList[type]);
}
public List<PropertyInfo> ExcludeProperties(Type x)
@@ -56,21 +44,44 @@ namespace NzbDrone.Core.Datastore
{
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 class LazyLoadedProperty
{
public PropertyInfo Property { get; set; }
public ILazyLoaded LazyLoad { get; set; }
}
public class ColumnMapper<T>
where T : ModelBase
{
private readonly List<PropertyInfo> _ignoreList;
private readonly List<LazyLoadedProperty> _lazyLoadList;
public ColumnMapper(List<PropertyInfo> ignoreList)
public ColumnMapper(List<PropertyInfo> ignoreList, List<LazyLoadedProperty> lazyLoadList)
{
_ignoreList = ignoreList;
_lazyLoadList = lazyLoadList;
}
public ColumnMapper<T> AutoMapPropertiesWhere(Func<PropertyInfo, bool> predicate)
{
Type entityType = typeof(T);
var properties = entityType.GetProperties();
var properties = typeof(T).GetProperties();
_ignoreList.AddRange(properties.Where(x => !predicate(x)));
return this;
@@ -78,7 +89,7 @@ namespace NzbDrone.Core.Datastore
public ColumnMapper<T> RegisterModel()
{
return AutoMapPropertiesWhere(IsMappableProperty);
return AutoMapPropertiesWhere(x => x.IsMappableProperty());
}
public ColumnMapper<T> Ignore(Expression<Func<T, object>> property)
@@ -87,30 +98,31 @@ namespace NzbDrone.Core.Datastore
return this;
}
public static bool IsMappableProperty(MemberInfo memberInfo)
public ColumnMapper<T> LazyLoad<TChild>(Expression<Func<T, LazyLoaded<TChild>>> property, Func<IDatabase, T, TChild> query, Func<T, bool> condition)
{
var propertyInfo = memberInfo as PropertyInfo;
var lazyLoad = new LazyLoaded<T, TChild>(query, condition);
if (propertyInfo == null)
var item = new LazyLoadedProperty
{
return false;
}
Property = property.GetMemberName(),
LazyLoad = lazyLoad
};
if (!propertyInfo.IsReadable() || !propertyInfo.IsWritable())
{
return false;
}
_lazyLoadList.Add(item);
// This is a bit of a hack but is the only way to see if a type has a handler set in Dapper
#pragma warning disable 618
SqlMapper.LookupDbType(propertyInfo.PropertyType, "", false, out var handler);
#pragma warning restore 618
if (propertyInfo.PropertyType.IsSimpleType() || handler != null)
{
return true;
}
return this;
}
return false;
public ColumnMapper<T> HasOne<TChild>(Expression<Func<T, LazyLoaded<TChild>>> portalExpression, Func<T, int> childIdSelector)
where TChild : ModelBase
{
return LazyLoad(portalExpression,
(db, parent) =>
{
var id = childIdSelector(parent);
return db.Query<TChild>(new SqlBuilder().Where<TChild>(x => x.Id == id)).SingleOrDefault();
},
parent => childIdSelector(parent) > 0);
}
}
}