0

I'm looking for a way to use reflection in my linq queries to increase code reusability. for example, i want to change this method :

private Orders GetObjectGraph(MyDbContext dbContext, int orderID)
{
    return dbContext.Orders.Include(o => o.OrderDetails).AsNoTracking().FirstOrDefault(o => o.OrderID == orderID);
}

To something like this :

private object GetObjectGraph(MyDbContext dbContext, string masterEntityName, string detailsEntityName, string masterFieldName, object masterFieldValue)
{
    return dbContext[masterEntityName].Include(o => o[detailsEntityName]).AsNoTracking().FirstOrDefault(o => o.[masterFieldName] == masterFieldValue);
}

Can anybody help me how to accomplish this task ?

Thanks in advance

hdv212
  • 61
  • 10
  • 1
    I’d stay away from reflection, but sounds like a use case for generic methods, maybe with a little specification pattern built in. Check out eShopOnWeb for an implementation – Ben Sampica May 31 '20 at 20:45
  • 1
    Do you only have the string names for the entities? Because if you know the entity type at the moment of the call you could replace the `string` parameters by with `Expressions` and add a generic type to identify the root entity. – devcrp May 31 '20 at 20:51

1 Answers1

1

You can try something like this (if you are not getting strings from somewhere outside in runtime, if you do than this solution can be adapted to build corresponding Expression Trees from strings):

private T GetObjectGraph<T, TDetails, TField>(
    MyDbContext dbContext,
    Expression<Func<T, TDetails>> details,
    Expression<Func<T, TField>> field,
    TField value) where T: class
{
    var p = field.Parameters[0];
    var eq = Expression.Equal(field.Body, Expression.Constant(value));
    var expr = Expression.Lambda<Func<T, bool>>(eq, p); 
    return dbContext
        .Set<T>()
        .Include(details)
        .AsNoTracking()
        .FirstOrDefault(expr);
}

And usage:

GetObjectGraph(dbContext, (Order o) => o.OrderDetails, o => o.OrderID, orderID)

If you don't need to parse strings and so on this could be simplified to:

public static class Ext
{
    public static T GetObjectGraph<T, TDetails>(
        this DbSet<T> set, 
        Expression<Func<T, TDetails>> details,
        Expression<Func<T, bool>> filter) where T : class
    {

        return set
            .Include(details)
            .AsNoTracking()
            .FirstOrDefault(filter);
    }
}

And usage:

dbContext.Orders(o => o.OrderDetails, o => o.OrderID == orderID);

And for more insight how to handle strings you can look at this question.

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Thanks for reply. But i think you missing 'masterEntityName' in your sample. Can you provide more description that what's happened? Thanks – hdv212 May 31 '20 at 21:02