LINQ: Expression.Call(typeof(Queryable), "Select"
I'm attempting to create a small "automapper-esq" utility that will take a LinqToSql entity and map it to a "projection class".
So far I have something like this:
class Entity
{
public int ID { get; set; }
public string WantedProperty { get; set; }
public string UnWantedPropertyData { get; set; }
...More Unwanted Properties...
public IEnumerable<ChildEntity> ChildEntities { get; set; }
}
class EntityProjection
{
public int ID { get; set; }
public string WantedProperty { get; set; }
public IEnumerable<ChildEntityProjection> ChildEntities { get; set; }
}
class ChildEntityProjection
{
public int ID { get; set; }
public string WantedProperty { get; set; }
public string UnWantedPropertyData { get; set; }
...More Unwant开发者_StackOverflow中文版ed Properties...
}
var results = context.Table.Select(ProjectionHelper.BuildProjection<Entity,EntityProjection>());
where BuildProjection returns:
Expression<Func<TSource, TResult>>
which essentially creates a lambda like this:
A => new EntityProjection() { ID = A.ID, WantedProperty = A.WantedProperty }
Now the tricky part...I'd like to be able to project association properties of the "parent" entity as well. Essentially what I need is to get something like this:
A => new EntityProjection() {
ID = A.ID,
WantedProperty = A.WantedProperty,
ChildEntities = A.ChildEntities.Select(B => new ChildEntityProjection {
ID = B.ID,
WantedProperty = B.WantedProperty
}
}
I have gotten as far as getting this part:
A => new EntityProjection() {
ID = A.ID,
WantedProperty = A.WantedProperty,
ChildEntities = System.Collections.Generic.List1[ChildEntity].Select(B => new ChildEntityProjection {
ID = B.ID,
WantedProperty = B.WantedProperty
}
}
By doing this:
IQueryable<ChildEntity> list = new List<ChildEtity>().AsQueryable();
Expression _selectExpression = Expression.Call(
typeof(Queryable),
"Select",
new Type[] { typeof(ChildEntity), typeof(ChildEntityProjection) },
Expression.Constant(list),
_nestedLambda);
Here is where I am stuck at the moment...I am getting a little confused when attempting to replace Expression.Constant(list) with some other expression that represents the actual datatype for the property so that "System.Collections.Generic.List1[ChildEntity].Select(B=>..." will be replaced with "A.ChildEntities.Select(B=>..."
Any ideas?
I was looking more for how to do this using Expressions (correct terminology?) and I did eventually figure it out.
I had to change this:
IQueryable<ChildEntity> list = new List<ChildEtity>().AsQueryable();
Expression _selectExpression = Expression.Call(
typeof(Queryable),
"Select",
new Type[] { typeof(ChildEntity), typeof(ChildEntityProjection) },
Expression.Constant(list),
_nestedLambda);
to this:
MethodInfo selectMethod = null;
foreach (MethodInfo m in typeof(Enumerable).GetMethods().Where(m => m.Name == "Select"))
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("selector")))
if (p.ParameterType.GetGenericArguments().Count() == 2)
selectMethod = (MethodInfo)p.Member;
var _selectExpression = Expression.Call(
null,
selectMethod.MakeGenericMethod(new Type[] { typeof(ChildEntity), typeof(ChildEntityProjection) }),
new Expression[] { _myPropertyExpression, _myFuncExpression });
Hope this helps someone else out...
Here's a suggested query. And you might want to change your IEnumerable<> in EntityProjection to a List<> to avoid a lazy load (if it matters...). Anyway, I hope this is helpful.
EntityProjection projection = context.Table
.Where(entity => entity.ID == 123)
.Select(entity => new EntityProjection()
{
ID = entity.ID,
WantedProperty = entity.WantedProperty,
ChildEntities = entity.ChildEntities
.Select(child => new ChildEntityProjection()
{
ID = child.EmployeeID,
WantedProperty = child.WantedProperty
})
.ToList()
})
.SingleOrDefault();
精彩评论