How can I rewrite this LINQ query with reflection

So, I wrote this LINQ query using reflection and later found out that it is not 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


source to share


2 answers


Use reflection to create a request, not in a request. 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 produces an expression first (so if there name

was "Test" it would generate an expression that matched p => p.TestUUID == uuid

and then use that in the call Where

.

Since this step is performed first, and not within the expression itself, there is no need for the query engine to try to translate typeof

either GetProperty()

to SQL (which it certainly could not do).

So:

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

      

Returns IQueryable<Profile>

with matching attached Where

. So:

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

      

In general, first use reflection to build your query, then apply the query, then build a list from it asynchronously, and then wait for its results.

It is worth noting that since it Filter()

will accept any IQueryable<Profile>

, they can be either constrained or combined. So:



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

      

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

      

Equivalent to:

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

      

A more generalized 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 when you have to do + "UUID"

in the calling code, you can use it to make similar requests with any IQueryable<>

type of element.

+1


source


How easy is it to compare all property names? By definition, the UUID will have no conflicts. Since Profile

it is just a data class, # the properties for the UUID are 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 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();

      

+1


source







All Articles