How can you create a query without a generic type?

.net c# dynamic-linq linq-to-sql

Question

I'll start with piece of code:

var objectType = typeof(Department); // Department is entity class from linqdatacontext

using (var dataContext = new DataModel.ModelDataContext())
{
    var entity = Expression.Parameter(objectType, "model");
    var keyValue = Expression.Property(entity, "Id");
    var pkValue = Expression.Constant(reader.Value);
    var cond = Expression.Equal(keyValue, pkValue);
    var table = dataContext.GetTable(objectType);
    ... // and here i don't how to proceed
}

I am not even sure if i am building that expression correctly. However simply put, i need to call dynamically SingleOrDefault() on that table to find entity by primary key. Every example i had found is using generic variant of GetTable<>(), but i cannot use that obviously. I am probably overlooking something...

1
1
10/26/2011 3:15:28 PM

Accepted Answer

Whenever I build expression trees, I like to start off with an example of what I'm building:

() => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == 1);

From that, we can easily dissect the target expression. You are partway there; you just need to include a call to the GetTable method in the expression tree and then build an outer lambda expression to call the whole thing:

using(var dataContext = new DataModel.ModelDataContext())
{
    var getTableCall = Expression.Call(
        Expression.Constant(dataContext),
        "GetTable",
        new[] { entityType });

    var entity = Expression.Parameter(entityType, "entity");

    var idCheck = Expression.Equal(
        Expression.Property(entity, "Id"),
        Expression.Constant(reader.Value));

    var idCheckLambda = Expression.Lambda(idCheck, entity);

    var singleOrDefaultCall = Expression.Call(
        typeof(Queryable),
        "SingleOrDefault",
        new[] { entityType },
        getTableCall,
        Expression.Quote(idCheckLambda));

    var singleOrDefaultLambda = Expression.Lambda<Func<object>>(
        Expression.Convert(singleOrDefaultCall, typeof(object)));

    var singleOrDefaultFunction = singleOrDefaultLambda.Compile();

    return singleOrDefaultFunction();    
}

We have to convert the SingleOrDefault call to have a return type of object so it can serve as the body of the Func<object> function.

(Untested)

Edit: Parameterizing the data context and value

Now we are building this function:

(dataContext, value) => dataContext.GetTable<TEntity>().SingleOrDefault(entity => entity.Id == value);

You would change the constants to parameters and add those parameters to the function you compile:

var dataContextParameter = Expression.Parameter(typeof(ModelDataContext), "dataContext");

var valueParameter = Expression.Parameter(typeof(object), "value");

var getTableCall = Expression.Call(
    dataContextParameter,
    "GetTable",
    new[] { entityType });

var entity = Expression.Parameter(entityType, "entity");

var idCheck = Expression.Equal(
    Expression.Property(entity, "Id"),
    valueParameter);

var idCheckLambda = Expression.Lambda(idCheck, entity);

var singleOrDefaultCall = Expression.Call(
    typeof(Queryable),
    "SingleOrDefault",
    new[] { entityType },
    getTableCall,
    Expression.Quote(idCheckLambda));

var singleOrDefaultLambda =
    Expression.Lambda<Func<ModelDataContext, object, object>>(
        Expression.Convert(singleOrDefaultCall, typeof(object)),
        dataContextParameter,
        valueParameter);

var singleOrDefaultFunction = singleOrDefaultLambda.Compile();

// Usage

using(var dataContext = new DataModel.ModelDataContext())
{
    return singleOrDefaultFunction(dataContext, reader.Value);    
}
1
10/26/2011 9:18:38 PM

Popular Answer

If you are using .NET 4, you could try casting your returned objects as dynamic, so you could then query like this.

using (var dataContext = new DataModel.ModelDataContext())
{
   var entity = Expression.Parameter(objectType, "model");
   var keyValue = Expression.Property(entity, "Id");
   var pkValue = Expression.Constant(reader.Value);
   var cond = Expression.Equal(keyValue, pkValue);
   var table = dataContext.GetTable(objectType);

   var result = table.Where(ent => ((dynamic)ent).SomeField == "SomeValue");
}


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow