1

我正在尝试制作一个通用的 Duplicate linq 扩展方法。但是我不能正确地得到表达式树。这是我试图模仿的 linq 语句。

var query = cars.AsQueryable().GroupBy(x => new { x.Color, x.Length }).Where(g => g.Count() > 1).SelectMany(p => p);

但我想这样称呼我的分机。请注意,我可以发送任意数量的属性。(颜色、长度)等等……</p>

var test = cars.AsQueryable().GetDuplicates2(new[] { "Color", "Length" });

当我试图计算匿名类型时,我陷入了 where 表达式中。groupby 表达式已经按预期工作。

请注意,我知道还有很多其他方法可以做到这一点,但是我正在尝试使用这些表达式来获得经验。所以请保持针对此的答案。

这是我当前的代码:

    public static IEnumerable<TSource> GetDuplicates2<TSource>(this IQueryable<TSource> source, IEnumerable<string> fieldNames)
    {

        IQueryable groups = null;
        try
        {
            Dictionary<string, PropertyInfo> sourceProperties = fieldNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name));
            Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);

            ParameterExpression sourceItem = Expression.Parameter(typeof(TSource), "x");
            IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();

            Expression e1 = Expression.Lambda(Expression.MemberInit(
                Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

            MethodCallExpression groupByExpression = Expression.Call(typeof(Queryable), "GroupBy", new Type[] { source.ElementType, dynamicType },
                            Expression.Constant(source), e1);

            sourceItem = Expression.Parameter(source.ElementType, "group");
            Expression left = Expression.Call(sourceItem, typeof(Queryable).GetMethods().FirstOrDefault(p => p.Name == "Count"));
            Expression right = Expression.Constant(0);
            Expression e2 = Expression.GreaterThan(left, right);

            MethodCallExpression whereCallExpression = Expression.Call(
            typeof(Queryable),
            "Where",
            new Type[] { typeof(TSource) },
            groupByExpression,
            Expression.Lambda<Func<TSource, bool>>(e2, new ParameterExpression[] { sourceItem }));


            sourceItem = Expression.Parameter(typeof(TSource), "p");

            MethodCallExpression selectManyCallExpression = Expression.Call(
                typeof(IQueryable<TSource>),
                "SelectMany",
                null,
                whereCallExpression,
                Expression.Lambda<Func<TSource, TSource>>(sourceItem, new ParameterExpression[] { sourceItem }));

            groups = source.Provider.CreateQuery(selectManyCallExpression);

        }
        catch (Exception ex) { }

        if (groups != null)
            foreach (var group in groups)
                foreach (var item in @group)
                    yield return item;
    }

    public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<string> fieldNames)
    {
        Dictionary<string, PropertyInfo> sourceProperties = fieldNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name));
        Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);

        ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t");
        IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();

        Expression selector = Expression.Lambda(Expression.MemberInit(
            Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

        return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType },
                     Expression.Constant(source), selector));
    }



    public static class LinqRuntimeTypeBuilder
    {
        private static AssemblyName assemblyName = new AssemblyName() { Name = "DynamicLinqTypes" };
        private static ModuleBuilder moduleBuilder = null;
        private static Dictionary<string, Type> builtTypes = new Dictionary<string, Type>();

        static LinqRuntimeTypeBuilder()
        {
            moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name);
        }

        private static string GetTypeKey(Dictionary<string, Type> fields)
        {
            //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter
            string key = string.Empty;
            foreach (var field in fields)
                key += field.Key + ";" + field.Value.Name + ";";

            return key;
        }

        public static Type GetDynamicType(Dictionary<string, Type> fields)
        {
            if (null == fields)
                throw new ArgumentNullException("fields");
            if (0 == fields.Count)
                throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition");

            try
            {
                Monitor.Enter(builtTypes);
                string className = GetTypeKey(fields);

                if (builtTypes.ContainsKey(className))
                    return builtTypes[className];

                TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

                foreach (var field in fields)
                    typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public);

                builtTypes[className] = typeBuilder.CreateType();

                return builtTypes[className];
            }
            catch (Exception ex)
            {

            }
            finally
            {
                Monitor.Exit(builtTypes);
            }

            return null;
        }


        private static string GetTypeKey(IEnumerable<PropertyInfo> fields)
        {
            return GetTypeKey(fields.ToDictionary(f => f.Name, f => f.PropertyType));
        }

        public static Type GetDynamicType(IEnumerable<PropertyInfo> fields)
        {
            return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType));
        }
    }
}

public class Car
{
    public int Length { set; get; }
    public int Width { set; get; }
    public string Color { set; get; }
    public string Model { set; get; }
    public string Make { set; get; }
}

}

4

1 回答 1

4

你把事情弄得太复杂了。说啊:

 public static IEnumerable<TSource> GetDuplicatesByKey<TSource, TKey>(
     this IEnumerable<TSource> source,
     Func<TSource, TKey> keySelector
 ) {
     return source.GroupBy(keySelector)
                  .Where(g => g.Skip(1).Any())
                  .SelectMany(g => g);
 }

你甚至可以有一个带IEqualityComparer<TKey>等的重载。

于 2012-05-10T13:27:05.490 回答