U4-8626 - kill dynamic support

This commit is contained in:
Stephan
2016-06-30 16:39:05 +02:00
parent 216329c096
commit cdb1f29ec0
70 changed files with 252 additions and 9926 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,584 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Dynamics;
using System.Collections;
using System.Reflection;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web.Dynamics;
namespace Umbraco.Web.Models
{
/// <summary>
/// Represents a collection of DynamicPublishedContent items.
/// </summary>
public class DynamicPublishedContentList : DynamicObject, IEnumerable<DynamicPublishedContent>
{
private readonly List<IPublishedContent> _content;
internal readonly List<DynamicPublishedContent> Items;
#region Constructor
public DynamicPublishedContentList()
{
_content = new List<IPublishedContent>();
Items = new List<DynamicPublishedContent>();
}
public DynamicPublishedContentList(IEnumerable<IPublishedContent> items)
{
_content = items.ToList();
Items = _content.Select(x => new DynamicPublishedContent(x, this)).ToList();
}
public DynamicPublishedContentList(IEnumerable<DynamicPublishedContent> items)
{
_content = items.Select(x => x.PublishedContent).ToList();
Items = _content.Select(x => new DynamicPublishedContent(x, this)).ToList();
}
#endregion
#region ContentSet
// so we are ~compatible with strongly typed syntax
public DynamicPublishedContentList ToContentSet()
{
return this;
}
#endregion
#region IList (well, part of it)
/// <summary>
/// Adds an item to the collection.
/// </summary>
/// <param name="dynamicContent">The item to add.</param>
public void Add(DynamicPublishedContent dynamicContent)
{
var content = dynamicContent.PublishedContent;
_content.Add(content);
Items.Add(new DynamicPublishedContent(content, this));
}
/// <summary>
/// Removes an item from the collection.
/// </summary>
/// <param name="dynamicContent">The item to remove.</param>
public void Remove(DynamicPublishedContent dynamicContent)
{
if (Items.Contains(dynamicContent) == false) return;
Items.Remove(dynamicContent);
_content.Remove(dynamicContent.PublishedContent);
}
#endregion
#region DynamicObject
// because we want to return DynamicNull and not null, we need to implement the index property
// via the dynamic getter and not natively - otherwise it's not possible to return DynamicNull
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
result = DynamicNull.Null;
if (indexes.Length != 1)
return false;
var index = indexes[0] as int?;
if (index.HasValue == false)
return false;
if (index >= 0 && index < Items.Count)
result = Items[index.Value];
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
//TODO: Nowhere here are we checking if args is the correct length!
//NOTE: For many of these we could actually leave them out since we are executing custom extension methods and because
// we implement IEnumerable<T> they will execute just fine, however, to do that will be quite a bit slower than checking here.
var firstArg = args.FirstOrDefault();
//this is to check for 'DocumentTypeAlias' vs 'NodeTypeAlias' for compatibility
if (firstArg != null && firstArg.ToString().InvariantStartsWith("NodeTypeAlias"))
{
firstArg = "DocumentTypeAlias" + firstArg.ToString().Substring("NodeTypeAlias".Length);
}
var name = binder.Name;
if (name == "Single")
{
string predicate = firstArg == null ? "" : firstArg.ToString();
var values = predicate.IsNullOrWhiteSpace() ? new object[] {} : args.Skip(1).ToArray();
var single = Single<DynamicPublishedContent>(predicate, values);
result = new DynamicPublishedContent(single);
return true;
}
if (name == "SingleOrDefault")
{
string predicate = firstArg == null ? "" : firstArg.ToString();
var values = predicate.IsNullOrWhiteSpace() ? new object[] { } : args.Skip(1).ToArray();
var single = SingleOrDefault<DynamicPublishedContent>(predicate, values);
if (single == null)
result = DynamicNull.Null;
else
result = new DynamicPublishedContent(single);
return true;
}
if (name == "First")
{
string predicate = firstArg == null ? "" : firstArg.ToString();
var values = predicate.IsNullOrWhiteSpace() ? new object[] { } : args.Skip(1).ToArray();
var first = First<DynamicPublishedContent>(predicate, values);
result = new DynamicPublishedContent(first);
return true;
}
if (name == "FirstOrDefault")
{
string predicate = firstArg == null ? "" : firstArg.ToString();
var values = predicate.IsNullOrWhiteSpace() ? new object[] { } : args.Skip(1).ToArray();
var first = FirstOrDefault<DynamicPublishedContent>(predicate, values);
if (first == null)
result = DynamicNull.Null;
else
result = new DynamicPublishedContent(first);
return true;
}
if (name == "Last")
{
string predicate = firstArg == null ? "" : firstArg.ToString();
var values = predicate.IsNullOrWhiteSpace() ? new object[] { } : args.Skip(1).ToArray();
var last = Last<DynamicPublishedContent>(predicate, values);
result = new DynamicPublishedContent(last);
return true;
}
if (name == "LastOrDefault")
{
string predicate = firstArg == null ? "" : firstArg.ToString();
var values = predicate.IsNullOrWhiteSpace() ? new object[] { } : args.Skip(1).ToArray();
var last = LastOrDefault<DynamicPublishedContent>(predicate, values);
if (last == null)
result = DynamicNull.Null;
else
result = new DynamicPublishedContent(last);
return true;
}
if (name == "Where")
{
string predicate = firstArg.ToString();
var values = args.Skip(1).ToArray();
//TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses
// are nested! We should somehow support an QueryableDocumentList!
result = new DynamicPublishedContentList(Where<DynamicPublishedContent>(predicate, values).ToList());
return true;
}
if (name == "OrderBy")
{
//TODO: We are pre-resolving the where into a ToList() here which will have performance impacts if there where clauses
// are nested! We should somehow support an QueryableDocumentList!
result = new DynamicPublishedContentList(OrderBy<DynamicPublishedContent>(firstArg.ToString()).ToList());
return true;
}
if (name == "Take")
{
result = new DynamicPublishedContentList(this.Take<DynamicPublishedContent>((int)firstArg));
return true;
}
if (name == "Skip")
{
result = new DynamicPublishedContentList(this.Skip<DynamicPublishedContent>((int)firstArg));
return true;
}
if (name == "InGroupsOf")
{
int groupSize;
if (int.TryParse(firstArg.ToString(), out groupSize))
{
result = InGroupsOf(groupSize);
return true;
}
result = DynamicNull.Null;
return true;
}
if (name == "GroupedInto")
{
int groupCount;
if (int.TryParse(firstArg.ToString(), out groupCount))
{
result = GroupedInto(groupCount);
return true;
}
result = DynamicNull.Null;
return true;
}
if (name == "GroupBy")
{
result = GroupBy(firstArg.ToString());
return true;
}
if (name == "Average" || name == "Min" || name == "Max" || name == "Sum")
{
result = Aggregate(args, name);
return true;
}
if (name == "Union")
{
// check DynamicPublishedContentList before IEnumerable<...> because DynamicPublishedContentList
// is IEnumerable<...> so ... the check on DynamicPublishedContentList would never be reached.
var firstArgAsDynamicPublishedContentList = firstArg as DynamicPublishedContentList;
if (firstArgAsDynamicPublishedContentList != null)
{
result = new DynamicPublishedContentList(Items.Union((firstArgAsDynamicPublishedContentList).Items));
return true;
}
var firstArgAsIEnumerable = firstArg as IEnumerable<DynamicPublishedContent>;
if (firstArgAsIEnumerable != null)
{
result = new DynamicPublishedContentList(Items.Union(firstArgAsIEnumerable));
return true;
}
}
if (name == "Except")
{
if ((firstArg as IEnumerable<DynamicPublishedContent>) != null)
{
result = new DynamicPublishedContentList(Items.Except(firstArg as IEnumerable<DynamicPublishedContent>, new DynamicPublishedContentIdEqualityComparer()));
return true;
}
}
if (name == "Intersect")
{
if ((firstArg as IEnumerable<DynamicPublishedContent>) != null)
{
result = new DynamicPublishedContentList(Items.Intersect(firstArg as IEnumerable<DynamicPublishedContent>, new DynamicPublishedContentIdEqualityComparer()));
return true;
}
}
if (name == "Distinct")
{
result = new DynamicPublishedContentList(Items.Distinct(new DynamicPublishedContentIdEqualityComparer()));
return true;
}
if (name == "Pluck" || name == "Select")
{
result = Pluck(args);
return true;
}
var runtimeCache = ApplicationContext.Current != null ? ApplicationContext.Current.ApplicationCache.RuntimeCache : new NullCacheProvider();
//ok, now lets try to match by member, property, extensino method
var attempt = DynamicInstanceHelper.TryInvokeMember(runtimeCache, this, binder, args, new[]
{
typeof (IEnumerable<DynamicPublishedContent>),
typeof (DynamicPublishedContentList)
});
if (attempt.Success)
{
result = attempt.Result.ObjectResult;
//need to check the return type and possibly cast if result is from an extension method found
if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod)
{
//we don't need to cast if the result is already DynamicPublishedContentList
if (attempt.Result.ObjectResult != null && (!(attempt.Result.ObjectResult is DynamicPublishedContentList)))
{
if (attempt.Result.ObjectResult is IPublishedContent)
{
result = new DynamicPublishedContent((IPublishedContent)attempt.Result.ObjectResult);
}
else if (attempt.Result.ObjectResult is IEnumerable<DynamicPublishedContent>)
{
result = new DynamicPublishedContentList((IEnumerable<DynamicPublishedContent>)attempt.Result.ObjectResult);
}
else if (attempt.Result.ObjectResult is IEnumerable<IPublishedContent>)
{
result = new DynamicPublishedContentList((IEnumerable<IPublishedContent>)attempt.Result.ObjectResult);
}
}
}
return true;
}
//this is the result of an extension method execution gone wrong so we return dynamic null
if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod
&& attempt.Exception != null && attempt.Exception is TargetInvocationException)
{
result = DynamicNull.Null;
return true;
}
result = null;
return false;
}
#endregion
#region Linq and stuff
private T Aggregate<T>(IEnumerable<T> data, string name) where T : struct
{
switch (name)
{
case "Min":
return data.Min<T>();
case "Max":
return data.Max<T>();
case "Average":
if (typeof(T) == typeof(int))
{
return (T)Convert.ChangeType((data as List<int>).Average(), typeof(T));
}
if (typeof(T) == typeof(decimal))
{
return (T)Convert.ChangeType((data as List<decimal>).Average(), typeof(T));
}
break;
case "Sum":
if (typeof(T) == typeof(int))
{
return (T)Convert.ChangeType((data as List<int>).Sum(), typeof(T));
}
if (typeof(T) == typeof(decimal))
{
return (T)Convert.ChangeType((data as List<decimal>).Sum(), typeof(T));
}
break;
}
return default(T);
}
private object Aggregate(object[] args, string name)
{
object result;
string predicate = args.First().ToString();
var values = args.Skip(1).ToArray();
var query = (IQueryable<object>)this.Select(predicate, values);
object firstItem = query.FirstOrDefault();
if (firstItem == null)
{
result = DynamicNull.Null;
}
else
{
var types = from i in query
group i by i.GetType() into g
where g.Key != typeof(DynamicNull)
orderby g.Count() descending
select new { g, Instances = g.Count() };
var dominantType = types.First().g.Key;
//remove items that are not the dominant type
//e.g. string,string,string,string,false[DynamicNull],string
var itemsOfDominantTypeOnly = query.ToList();
itemsOfDominantTypeOnly.RemoveAll(item => !item.GetType().IsAssignableFrom(dominantType));
if (dominantType == typeof(string))
{
throw new ArgumentException("Can only use aggregate methods on properties which are numeric");
}
else if (dominantType == typeof(int))
{
List<int> data = (List<int>)itemsOfDominantTypeOnly.Cast<int>().ToList();
return Aggregate<int>(data, name);
}
else if (dominantType == typeof(decimal))
{
List<decimal> data = (List<decimal>)itemsOfDominantTypeOnly.Cast<decimal>().ToList();
return Aggregate<decimal>(data, name);
}
else if (dominantType == typeof(bool))
{
throw new ArgumentException("Can only use aggregate methods on properties which are numeric or datetime");
}
else if (dominantType == typeof(DateTime))
{
if (name != "Min" || name != "Max")
{
throw new ArgumentException("Can only use aggregate min or max methods on properties which are datetime");
}
List<DateTime> data = (List<DateTime>)itemsOfDominantTypeOnly.Cast<DateTime>().ToList();
return Aggregate<DateTime>(data, name);
}
else
{
result = query.ToList();
}
}
return result;
}
private object Pluck(object[] args)
{
object result;
string predicate = args.First().ToString();
var values = args.Skip(1).ToArray();
var query = (IQueryable<object>)this.Select(predicate, values);
object firstItem = query.FirstOrDefault();
if (firstItem == null)
{
result = new List<object>();
}
else
{
var types = from i in query
group i by i.GetType() into g
where g.Key != typeof(DynamicNull)
orderby g.Count() descending
select new { g, Instances = g.Count() };
var dominantType = types.First().g.Key;
//remove items that are not the dominant type
//e.g. string,string,string,string,false[DynamicNull],string
var itemsOfDominantTypeOnly = query.ToList();
itemsOfDominantTypeOnly.RemoveAll(item => !item.GetType().IsAssignableFrom(dominantType));
if (dominantType == typeof(string))
{
result = (List<string>)itemsOfDominantTypeOnly.Cast<string>().ToList();
}
else if (dominantType == typeof(int))
{
result = (List<int>)itemsOfDominantTypeOnly.Cast<int>().ToList();
}
else if (dominantType == typeof(decimal))
{
result = (List<decimal>)itemsOfDominantTypeOnly.Cast<decimal>().ToList();
}
else if (dominantType == typeof(bool))
{
result = (List<bool>)itemsOfDominantTypeOnly.Cast<bool>().ToList();
}
else if (dominantType == typeof(DateTime))
{
result = (List<DateTime>)itemsOfDominantTypeOnly.Cast<DateTime>().ToList();
}
else
{
result = query.ToList();
}
}
return result;
}
public T Single<T>(string predicate, params object[] values)
{
return predicate.IsNullOrWhiteSpace()
? ((IQueryable<T>) Items.AsQueryable()).Single()
: Where<T>(predicate, values).Single();
}
public T SingleOrDefault<T>(string predicate, params object[] values)
{
return predicate.IsNullOrWhiteSpace()
? ((IQueryable<T>)Items.AsQueryable()).SingleOrDefault()
: Where<T>(predicate, values).SingleOrDefault();
}
public T First<T>(string predicate, params object[] values)
{
return predicate.IsNullOrWhiteSpace()
? ((IQueryable<T>)Items.AsQueryable()).First()
: Where<T>(predicate, values).First();
}
public T FirstOrDefault<T>(string predicate, params object[] values)
{
return predicate.IsNullOrWhiteSpace()
? ((IQueryable<T>)Items.AsQueryable()).FirstOrDefault()
: Where<T>(predicate, values).FirstOrDefault();
}
public T Last<T>(string predicate, params object[] values)
{
return predicate.IsNullOrWhiteSpace()
? ((IQueryable<T>)Items.AsQueryable()).Last()
: Where<T>(predicate, values).Last();
}
public T LastOrDefault<T>(string predicate, params object[] values)
{
return predicate.IsNullOrWhiteSpace()
? ((IQueryable<T>)Items.AsQueryable()).LastOrDefault()
: Where<T>(predicate, values).LastOrDefault();
}
public IQueryable<T> Where<T>(string predicate, params object[] values)
{
return ((IQueryable<T>)Items.AsQueryable()).Where(predicate, values);
}
public IQueryable<T> OrderBy<T>(string key)
{
return ((IQueryable<T>)Items.AsQueryable()).OrderBy(key, () => typeof(DynamicPublishedContentListOrdering));
}
public DynamicGrouping GroupBy(string key)
{
var group = new DynamicGrouping(this, key);
return group;
}
public DynamicGrouping GroupedInto(int groupCount)
{
var groupSize = (int)Math.Ceiling(((decimal)Items.Count() / groupCount));
return new DynamicGrouping(
Items
.Select((node, index) => new KeyValuePair<int, DynamicPublishedContent>(index, node))
.GroupBy(kv => (object)(kv.Key / groupSize))
.Select(item => new Grouping<object, DynamicPublishedContent>()
{
Key = item.Key,
Elements = item.Select(inner => inner.Value)
}));
}
public DynamicGrouping InGroupsOf(int groupSize)
{
return new DynamicGrouping(
Items
.Select((node, index) => new KeyValuePair<int, DynamicPublishedContent>(index, node))
.GroupBy(kv => (object)(kv.Key / groupSize))
.Select(item => new Grouping<object, DynamicPublishedContent>()
{
Key = item.Key,
Elements = item.Select(inner => inner.Value)
}));
}
public IQueryable Select(string predicate, params object[] values)
{
return DynamicQueryable.Select(Items.AsQueryable(), predicate, values);
}
#endregion
#region Dynamic
public bool IsNull()
{
return false;
}
public bool HasValue()
{
return true;
}
#endregion
#region Enumerate inner IPublishedContent items
public IEnumerator<DynamicPublishedContent> GetEnumerator()
{
return Items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

@@ -1,11 +1,10 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Dynamics;
namespace Umbraco.Web.Models
{
[DataContract(Name = "imageCropCoordinates")]
public class ImageCropCoordinates : CaseInsensitiveDynamicObject<ImageCropCoordinates>, IEquatable<ImageCropCoordinates>
public class ImageCropCoordinates : IEquatable<ImageCropCoordinates>
{
[DataMember(Name = "x1")]
public decimal X1 { get; set; }
@@ -44,7 +43,7 @@ namespace Umbraco.Web.Models
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj.GetType() != GetType()) return false;
return Equals((ImageCropCoordinates) obj);
}
@@ -73,7 +72,7 @@ namespace Umbraco.Web.Models
public static bool operator !=(ImageCropCoordinates left, ImageCropCoordinates right)
{
return !Equals(left, right);
return Equals(left, right) == false;
}
}
}

View File

@@ -1,12 +1,10 @@
using System;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using Umbraco.Core.Dynamics;
namespace Umbraco.Web.Models
{
[DataContract(Name = "imageCropData")]
public class ImageCropData : CaseInsensitiveDynamicObject<ImageCropData>, IEquatable<ImageCropData>
public class ImageCropData : IEquatable<ImageCropData>
{
[DataMember(Name = "alias")]
public string Alias { get; set; }
@@ -45,7 +43,7 @@ namespace Umbraco.Web.Models
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj.GetType() != GetType()) return false;
return Equals((ImageCropData) obj);
}
@@ -59,10 +57,10 @@ namespace Umbraco.Web.Models
{
unchecked
{
var hashCode = (Alias != null ? Alias.GetHashCode() : 0);
var hashCode = Alias?.GetHashCode() ?? 0;
hashCode = (hashCode*397) ^ Width;
hashCode = (hashCode*397) ^ Height;
hashCode = (hashCode*397) ^ (Coordinates != null ? Coordinates.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (Coordinates?.GetHashCode() ?? 0);
return hashCode;
}
}
@@ -74,7 +72,7 @@ namespace Umbraco.Web.Models
public static bool operator !=(ImageCropData left, ImageCropData right)
{
return !Equals(left, right);
return Equals(left, right) == false;
}
}

View File

@@ -1,16 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Web;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Serialization;
using Umbraco.Web.PropertyEditors.ValueConverters;
@@ -19,7 +14,7 @@ namespace Umbraco.Web.Models
[JsonConverter(typeof(NoTypeConverterJsonConverter<ImageCropDataSet>))]
[TypeConverter(typeof(ImageCropDataSetConverter))]
[DataContract(Name="imageCropDataSet")]
public class ImageCropDataSet : CaseInsensitiveDynamicObject<ImageCropDataSet>, IHtmlString, IEquatable<ImageCropDataSet>
public class ImageCropDataSet : IHtmlString, IEquatable<ImageCropDataSet>
{
[DataMember(Name="src")]
@@ -81,7 +76,7 @@ namespace Umbraco.Web.Models
public string ToHtmlString()
{
return this.Src;
return Src;
}
/// <summary>
@@ -122,7 +117,7 @@ namespace Umbraco.Web.Models
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj.GetType() != GetType()) return false;
return Equals((ImageCropDataSet) obj);
}
@@ -136,9 +131,9 @@ namespace Umbraco.Web.Models
{
unchecked
{
var hashCode = (Src != null ? Src.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (FocalPoint != null ? FocalPoint.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (Crops != null ? Crops.GetHashCode() : 0);
var hashCode = Src?.GetHashCode() ?? 0;
hashCode = (hashCode*397) ^ (FocalPoint?.GetHashCode() ?? 0);
hashCode = (hashCode*397) ^ (Crops?.GetHashCode() ?? 0);
return hashCode;
}
}
@@ -150,7 +145,7 @@ namespace Umbraco.Web.Models
public static bool operator !=(ImageCropDataSet left, ImageCropDataSet right)
{
return !Equals(left, right);
return Equals(left, right) == false;
}
}
}

View File

@@ -1,11 +1,10 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Dynamics;
namespace Umbraco.Web.Models
{
[DataContract(Name = "imageCropFocalPoint")]
public class ImageCropFocalPoint : CaseInsensitiveDynamicObject<ImageCropFocalPoint>, IEquatable<ImageCropFocalPoint>
public class ImageCropFocalPoint : IEquatable<ImageCropFocalPoint>
{
[DataMember(Name = "left")]
public decimal Left { get; set; }
@@ -38,7 +37,7 @@ namespace Umbraco.Web.Models
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (obj.GetType() != GetType()) return false;
return Equals((ImageCropFocalPoint) obj);
}
@@ -63,7 +62,7 @@ namespace Umbraco.Web.Models
public static bool operator !=(ImageCropFocalPoint left, ImageCropFocalPoint right)
{
return !Equals(left, right);
return Equals(left, right) == false;
}
}
}