Linq to Objects Generic Duplicate Method

时间:2012-05-10 13:21:08

标签: c# linq

我正在尝试制作一个通用的Duplicate linq扩展方法。 但我不能正确表达树。 这是我试图模仿的linq声明。

var query = cars.AsQueryable().GroupBy(x => new { x.Color, x.Length }).Where(g => g.Count() > 1).SelectMany(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; }
}

}

1 个答案:

答案 0 :(得分: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>等的重载