我如何用反射来重写这个LINQ查询 [英] How can I rewrite this LINQ query with reflection

查看:108
本文介绍了我如何用反射来重写这个LINQ查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我用反射编写了这个LINQ查询,后来发现它不支持。从代码中获取相同功能的最佳方法是什么?

 列表&&#Profile> profilesFromUUID = await MobileService.GetTable< Profile>()。其中​​(p => typeof(Profile)
.GetProperty(handler.Name +UUID)。GetValue(p)== obj.uuid).ToListAsync ();


解决方案

使用反射来创建查询,查询。考虑:

  public static IQueryable&&Profile过滤器(
this IQueryable< Profile> source,string name,Guid uuid)
{
//。< name> UUID
var property = typeof(Profile).GetProperty名称+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 相对应的表达式,然后在调用其中



由于此步骤首先完成,而不是在表达式本身内,因此无需查询引擎尝试将 typeof GetProperty()转换为SQL(当然这不可能) / p>

所以:

  var filtered = MobileService.GetTable< Profile> ).Filter(handler.Name,obj.uuid); 

返回一个 IQueryable< Profile> 与适当的附加。所以:

  var profilesFromUUID = await MobileService.GetTable< Profile>()。Filter(handler.Name,obj.uuid).ToListAsync(); 

作为一个整体首先使用反射b请求查询,然后应用查询,然后从其中异步生成一个列表,然后等待其结果。



值得注意的是,由于 Filter ()将接受任何 IQueryable< Profile> 它们可以被链接或联合。所以:

  MobileService.GetTable< Profile>()。Filter(A,uuid0).Filter(B UUID1); 

相当于:

  from p in MobileService.GetTable< Profile>()其中p.AUUID = uuid0&&& p.BUUID == uuid1 

And:

  MobileService.GetTable< Profile>()。Filter(A,uuid0).Union(
MobileService.GetTable< Profile>() uuid1))

相当于:

  from p in MobileService.GetTable< Profile>()其中p.AUUID = uuid0 || p.BUUID == uuid1 

更广义的版本是:

  public static IQueryable< TSource> FilterByNamedProperty< TSource,TValue>(此IQueryable< TSource>源,字符串propertyName,TValue值)
{
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 return source.Where(lambda);
}

然后当你必须做 +UUID 在调用代码中,您可以使用它来执行任何元素类型的任何 IQueryable 的类似查询。


So I had written this LINQ query using reflection, and later found out it isn't supported. What would be the best way to get the same functionality from this code?

List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>().Where(p => typeof(Profile)
.GetProperty(handler.Name + "UUID").GetValue(p) == obj.uuid).ToListAsync();

解决方案

Use the reflection to create the query, not in the query. Consider:

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

This builds up the expression first (so if name was "Test" it would create the expression corresponding with p => p.TestUUID == uuid and then uses that in the call to Where.

Because this step is done first, rather than within the expression itself, there's no need for the query engine to try to translate typeof or GetProperty() into SQL (which it of course, couldn't do).

So:

var filtered = MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid);

Returns an IQueryable<Profile> with the appropriate Where attached. And so:

var profilesFromUUID = await MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid).ToListAsync();

Will as a whole first use reflection to build the query, then apply the query, then produce a list from it asynchrously and then wait for its results.

It's worth noting that since Filter() will accept any IQueryable<Profile> they can be either chained or unioned. So:

MobileService.GetTable<Profile>().Filter("A", uuid0).Filter("B", uuid1);

Is equivalent to:

from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 && p.BUUID == uuid1

And:

MobileService.GetTable<Profile>().Filter("A", uuid0).Union(
  MobileService.GetTable<Profile>().Filter("B", uuid1))

Is equivalent to:

from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 || p.BUUID == uuid1

A more generalised version would be:

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

Then while you have to do the + "UUID" in the calling code, you can use this to do analogous queries with any IQueryable<> of any element type.

这篇关于我如何用反射来重写这个LINQ查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆