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'避免枚舉整個團隊只是爲了看看它是否有多個元素。 – Servy
@Servy:好的電話。 – jason
完美謝謝! – retslig