2012-05-10 3 views
1

일반 중복 linq 확장 메서드를 만들려고합니다. 하지만 표현식 트리를 제대로 가져올 수 없습니다. 다음은 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" }); 

합니다. 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'>) (오히려 카운트'보다') ((1) .ANY 건너 뛰기'사용할 수 있습니다 그룹 전체에 하나 이상의 요소가 있는지 확인하기 만하면됩니다. – Servy

+0

@Servy : 잘 부탁드립니다. – jason

+0

완벽한 감사합니다! – retslig