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...
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);
}
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");
}