2012-05-10 11 views
1

一般的な重複linq拡張メソッドを作成しようとしています。 しかし、私は式ツリーを正しく取得できません。 ここに私が模倣しようとしているlinq文があります。Linq to Objects汎用重複メソッド

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" }); 

私は、どこの式で立ち往生しています。 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

あなたはそれがあまりにも複雑になっている:

は、ここに私の現在のコードです。ジャストは言う:

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>を取るオーバーロードを持つことができる、など

+3

あなたが)ではなく(カウント 'よりも、(1).ANYを()'スキップ '使用することができます> 1 '列挙避けるために、グループ全体に複数の要素があるかどうかを確認するだけです。 – Servy

+0

@Servy:よろしくお願いします。 – jason

+0

パーフェクト感謝! – retslig

関連する問題