How can I rewrite this LINQ query with reflection

[亡魂溺海] 提交于 2019-12-24 12:33:37

问题


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

回答1:


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.




回答2:


How about just compare all property name? By definition UUID would not have collision anyway. Since Profile is just a data class, the # of the property for UUID is fixed.

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

Or add a method (extension method) for Profile like:

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


来源:https://stackoverflow.com/questions/30794544/how-can-i-rewrite-this-linq-query-with-reflection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!