Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if property exists in Type in an extension method?

I frequently have code like this:

var stRecs = db.<someTable>
             .Where(a => a.DepID == depID)

to select a single record, however if depID == 0 I would like to get back all records.

I was thinking about creating an extension method "WhereDepID_OrAll", like

public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source)
        where T: // is what?
{
    if(depID > 0) { return source.Where(a => a.depID == depID); }
    else return source.Where(a => a);
}

Now my basic question is: I have several tables with depID - how do I set the Where T: ?
How would the method determine, whether the table has depID?

A better approach to the underlying problem?

like image 866
reinhard Avatar asked May 24 '13 08:05

reinhard


3 Answers

At first glance, the reaction would be : create an interface

public interface ObjectWithDepartmentInterface {
    int depID;
}

make all the entities using this depId implementing this interface, and use

where T : ObjectWithDepartmentInterface

but linq to entities doesn't accept properties from interfaces in query... See for example : Expression generated based on interface

So the only way would to make your entities with a depId inheriting from a common entity (probably abstract) with a depId property.

And use this abstract entity as the

where T:

An easier (but uglier way) could be to not add a constraint on T, build the predicate in the method, and throw an exception in bad cases.

if (typeof(T).GetProperty("depId") == null)
   throw InvalidOperationException (string.Format("{0}" doesn't have a depId property, typeof(T).Name))

EDIT

But maybe it's not a problem of depId as a common property Then

public static IQueryable<T> WhereExistsOrAll<T>(this IQueryable<T> source, string propertyName, int value)
        where T: // is what?
{
    if (value == 0)
    return source;
    var parameter = Expression.Parameter(typeof(T), "m");
    Expression member = parameter;
    member = Expression.Property(member, propertyName);
    member = Expression.Equals(member, Expression.Constant(value));
    var lambda = Expression.Lambda<Func<T, bool>>(member, new[]{parameter});
    return source.Where(lambda);
}

usage

var stRecs = db.<someTable>.WhereExistsOrAll("depId", depId);

EDIT 2

Another way would be to parse the Predicate to get the "constant" value

something like that

public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
{
       var expression = predicate.Body as BinaryExpression;

       var rightPart = expression.Right as MemberExpression;
       var value = GetValue(rightPart);
       var test = value.ToString();
       int val;
       if (Int32.TryParse(value.ToString(), out val))
       {
           if (val != 0)
               return queryable.Where(predicate);
       }

       return queryable;
}

private static object GetValue(MemberExpression member)
{
     var objectMember = Expression.Convert(member, typeof(object));
     var getterLambda = Expression.Lambda<Func<object>>(objectMember);
     var getter = getterLambda.Compile();
     return getter();
}

usage

var stRecs = db.<someTable>.GetAllOrRestrict(m => m.depID == depId);
like image 64
Raphaël Althaus Avatar answered Sep 16 '22 18:09

Raphaël Althaus


I know it's not particularly fashionable, but isn't this exactly what the Query Builder methods in Entity Framework are for?

var stRecs = db.<someTable>
             .Where("it.DepID == @depID OR @depID = 0", 
                    new ObjectParameter("depID", depID));

This works on any someTable such that it has a column named DepID. It can of course be made an extension method:

public static ObjectQuery<T> WhereIdEqualOrAll<T>(this ObjectQuery<T> q, int depID)
    where T : class
{
    return q.Where("it.DepID = @id OR @id = 0", new ObjectParameter("id", id));
}

to be invoked thus:

var stRecs = db.<someTable>.WhereIdEqualOrAll(depID);
like image 40
AakashM Avatar answered Sep 16 '22 18:09

AakashM


Use an interface:

public interface IInterface
{
   int depId;
}

Which will force T to inherit from IInterface and implement depId.

Then you can add it to the extension:

public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source) where T: IInterface
{

}
like image 22
Darren Avatar answered Sep 17 '22 18:09

Darren