using Connected.Expressions.Collections; using Connected.Interop; using System.Linq.Expressions; using System.Reflection; namespace Connected.Expressions.Translation; public static class Aggregator { public static LambdaExpression? GetAggregator(Type expectedType, Type actualType) { var actualElementType = Enumerables.GetEnumerableElementType(actualType); if (!expectedType.GetTypeInfo().IsAssignableFrom(actualType.GetTypeInfo())) { var expectedElementType = Enumerables.GetEnumerableElementType(expectedType); var p = Expression.Parameter(actualType, "p"); Expression? body = null; if (expectedType.GetTypeInfo().IsAssignableFrom(actualElementType.GetTypeInfo())) body = Expression.Call(typeof(Enumerable), "SingleOrDefault", new Type[] { actualElementType }, p); else if (expectedType.GetTypeInfo().IsGenericType && (expectedType == typeof(IQueryable) || expectedType == typeof(IOrderedQueryable) || expectedType.GetGenericTypeDefinition() == typeof(IQueryable<>) || expectedType.GetGenericTypeDefinition() == typeof(IOrderedQueryable<>))) { body = Expression.Call(typeof(Queryable), "AsQueryable", new Type[] { expectedElementType }, CoerceElement(expectedElementType, p)); if (body.Type != expectedType) body = Expression.Convert(body, expectedType); } else if (expectedType.IsArray && expectedType.GetArrayRank() == 1) body = Expression.Call(typeof(Enumerable), "ToArray", new Type[] { expectedElementType }, CoerceElement(expectedElementType, p)); else if (expectedType.GetTypeInfo().IsGenericType && expectedType.GetGenericTypeDefinition().GetTypeInfo().IsAssignableFrom(typeof(IList<>).GetTypeInfo())) { var gt = typeof(DeferredList<>).MakeGenericType(expectedType.GetTypeInfo().GenericTypeArguments); var cn = Types.FindConstructor(gt, new Type[] { typeof(IEnumerable<>).MakeGenericType(expectedType.GetTypeInfo().GenericTypeArguments) }); body = Expression.New(cn, CoerceElement(expectedElementType, p)); } else if (expectedType.GetTypeInfo().IsAssignableFrom(typeof(List<>).MakeGenericType(actualElementType).GetTypeInfo())) body = Expression.Call(typeof(Enumerable), "ToList", new Type[] { expectedElementType }, CoerceElement(expectedElementType, p)); else { var ci = Types.FindConstructor(expectedType, new Type[] { actualType }); if (ci is not null) body = Expression.New(ci, p); } if (body is not null) return Expression.Lambda(body, p); } return null; } private static Expression CoerceElement(Type expectedElementType, Expression expression) { var elementType = Enumerables.GetEnumerableElementType(expression.Type); if (expectedElementType != elementType && (expectedElementType.GetTypeInfo().IsAssignableFrom(elementType.GetTypeInfo()) || elementType.GetTypeInfo().IsAssignableFrom(expectedElementType.GetTypeInfo()))) return Expression.Call(typeof(Enumerable), "Cast", new Type[] { expectedElementType }, expression); return expression; } }