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; }
}
}
당신은 열거 피하기 위해 1'>) (오히려 카운트'보다') ((1) .ANY 건너 뛰기'사용할 수 있습니다 그룹 전체에 하나 이상의 요소가 있는지 확인하기 만하면됩니다. – Servy
@Servy : 잘 부탁드립니다. – jason
완벽한 감사합니다! – retslig