我怎样才能用反射重写这个 LINQ 查询
How can I rewrite this LINQ query with reflection
所以我用反射写了这个LINQ查询,后来发现它不被支持。从此代码获得相同功能的最佳方法是什么?
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>().Where(p => typeof(Profile)
.GetProperty(handler.Name + "UUID").GetValue(p) == obj.uuid).ToListAsync();
只比较所有 属性 名称怎么样?根据定义,UUID 无论如何都不会 发生冲突。因为Profile
只是一个数据class,所以UUID的属性的#是固定的。
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
.Where(p =>
p.A_UUID == obj.uuid ||
p.B_UUID == obj.uuid ||
p.C_UUID == obj.uuid)
.ToListAsync();
或者为配置文件添加方法(扩展方法),如:
public static Guid GetUUIDByTableName(this Profile value, string tableName)
{
switch (tableName)
{
case "A_": return value.A_UUID;
case "B_": return value.B_UUID;
default: return Guid.Empty;
}
}
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
.Where(p => p.GetUUIDByTableName(handler.Name) == obj.uuid)
.ToListAsync();
使用反射创建查询,而不是在查询中。考虑:
public static IQueryable<Profile> Filter(
this IQueryable<Profile> source, string name, Guid uuid)
{
// .<name>UUID
var property = typeof(Profile).GetProperty(name + "UUID");
// p
var parExp = Expression.Parameter(typeof(Profile));
// p.<name>UUID
var methodExp = Expression.Property(parExp, property);
// uuid
var constExp = Expression.Constant(uuid, typeof(Guid));
// p.<name>UUID == uuid
var binExp = Expression.Equal(methodExp, constExp);
// p => p.<name>UUID == uuid
var lambda = Expression.Lambda<Func<Profile, bool>>(binExp, parExp);
// source.Where(p => p.<name>UUID == uuid)
return source.Where(lambda);
}
这首先构建了表达式(所以如果 name
是 "Test" 它将创建与 p => p.TestUUID == uuid
对应的表达式,然后在对 Where
的调用中使用它.
因为这一步是先完成的,而不是在表达式本身内,所以查询引擎不需要尝试将 typeof
或 GetProperty()
翻译成 SQL(它当然,做不到)。
所以:
var filtered = MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid);
Returns 附有适当的 Where
的 IQueryable<Profile>
。所以:
var profilesFromUUID = await MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid).ToListAsync();
将作为一个整体首先使用反射来构建查询,然后应用查询,然后从中异步生成一个列表,然后等待其结果。
值得注意的是,由于 Filter()
将接受任何 IQueryable<Profile>
它们可以被链接或合并。所以:
MobileService.GetTable<Profile>().Filter("A", uuid0).Filter("B", uuid1);
相当于:
from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 && p.BUUID == uuid1
并且:
MobileService.GetTable<Profile>().Filter("A", uuid0).Union(
MobileService.GetTable<Profile>().Filter("B", uuid1))
相当于:
from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 || p.BUUID == uuid1
更通用的版本是:
public static IQueryable<TSource> FilterByNamedProperty<TSource, TValue>(this IQueryable<TSource> source, string propertyName, TValue value)
{
var property = typeof(TSource).GetProperty(propertyName);
var parExp = Expression.Parameter(typeof(TSource));
var methodExp = Expression.Property(parExp, property);
var constExp = Expression.Constant(value, typeof(TValue));
var binExp = Expression.Equal(methodExp, constExp);
var lambda = Expression.Lambda<Func<TSource, bool>>(binExp, parExp);
return source.Where(lambda);
}
然后,当您必须在调用代码中执行 + "UUID"
时,您可以使用它对任何元素类型的任何 IQueryable<>
执行类似查询。
所以我用反射写了这个LINQ查询,后来发现它不被支持。从此代码获得相同功能的最佳方法是什么?
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>().Where(p => typeof(Profile)
.GetProperty(handler.Name + "UUID").GetValue(p) == obj.uuid).ToListAsync();
只比较所有 属性 名称怎么样?根据定义,UUID 无论如何都不会 发生冲突。因为Profile
只是一个数据class,所以UUID的属性的#是固定的。
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
.Where(p =>
p.A_UUID == obj.uuid ||
p.B_UUID == obj.uuid ||
p.C_UUID == obj.uuid)
.ToListAsync();
或者为配置文件添加方法(扩展方法),如:
public static Guid GetUUIDByTableName(this Profile value, string tableName)
{
switch (tableName)
{
case "A_": return value.A_UUID;
case "B_": return value.B_UUID;
default: return Guid.Empty;
}
}
List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
.Where(p => p.GetUUIDByTableName(handler.Name) == obj.uuid)
.ToListAsync();
使用反射创建查询,而不是在查询中。考虑:
public static IQueryable<Profile> Filter(
this IQueryable<Profile> source, string name, Guid uuid)
{
// .<name>UUID
var property = typeof(Profile).GetProperty(name + "UUID");
// p
var parExp = Expression.Parameter(typeof(Profile));
// p.<name>UUID
var methodExp = Expression.Property(parExp, property);
// uuid
var constExp = Expression.Constant(uuid, typeof(Guid));
// p.<name>UUID == uuid
var binExp = Expression.Equal(methodExp, constExp);
// p => p.<name>UUID == uuid
var lambda = Expression.Lambda<Func<Profile, bool>>(binExp, parExp);
// source.Where(p => p.<name>UUID == uuid)
return source.Where(lambda);
}
这首先构建了表达式(所以如果 name
是 "Test" 它将创建与 p => p.TestUUID == uuid
对应的表达式,然后在对 Where
的调用中使用它.
因为这一步是先完成的,而不是在表达式本身内,所以查询引擎不需要尝试将 typeof
或 GetProperty()
翻译成 SQL(它当然,做不到)。
所以:
var filtered = MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid);
Returns 附有适当的 Where
的 IQueryable<Profile>
。所以:
var profilesFromUUID = await MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid).ToListAsync();
将作为一个整体首先使用反射来构建查询,然后应用查询,然后从中异步生成一个列表,然后等待其结果。
值得注意的是,由于 Filter()
将接受任何 IQueryable<Profile>
它们可以被链接或合并。所以:
MobileService.GetTable<Profile>().Filter("A", uuid0).Filter("B", uuid1);
相当于:
from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 && p.BUUID == uuid1
并且:
MobileService.GetTable<Profile>().Filter("A", uuid0).Union(
MobileService.GetTable<Profile>().Filter("B", uuid1))
相当于:
from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 || p.BUUID == uuid1
更通用的版本是:
public static IQueryable<TSource> FilterByNamedProperty<TSource, TValue>(this IQueryable<TSource> source, string propertyName, TValue value)
{
var property = typeof(TSource).GetProperty(propertyName);
var parExp = Expression.Parameter(typeof(TSource));
var methodExp = Expression.Property(parExp, property);
var constExp = Expression.Constant(value, typeof(TValue));
var binExp = Expression.Equal(methodExp, constExp);
var lambda = Expression.Lambda<Func<TSource, bool>>(binExp, parExp);
return source.Where(lambda);
}
然后,当您必须在调用代码中执行 + "UUID"
时,您可以使用它对任何元素类型的任何 IQueryable<>
执行类似查询。