I have the following class, for which usage is not important. What is important is method SetCacheItemSelector which takes one parameter, a select expression that projects Account entity to AccountCacheDTO:
public class AccountRepositoryCache : RepositoryCache<Account, AccountCacheDTO>
{
public AccountRepositoryCache()
{
SetCacheItemSelector(x => new AccountCacheDTO
{
Id = x.Id,
Login = x.Login
});
}
}
So signature for this method is:
public void SetCacheItemSelector(Expression<Func<TEntity, TCacheItem>> selector)
In this case, TEntity is Account class, and TCacheItem is AccountCacheDTO class.
Is there a way to use reflection to build select expression dynamically for all the properties that are matching for both Account class and AccountCacheDTO class?
Goal is to have method that would look like this:
public Expression<Func<TEntity, TCacheItem>> BuildSelector<TEntity, TCacheItem>()
{
... // implementation with reflection goes here
}
EDIT:
Here is final implementation (pretty much the same as the accepted answer):
public static Expression<Func<TSource, TTarget>> BuildSelector<TSource, TTarget>()
{
Type targetType = typeof(TTarget);
Type sourceType = typeof(TSource);
ParameterExpression parameterExpression = Expression.Parameter(sourceType, "source");
List<MemberBinding> bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in sourceType.GetProperties().Where(x => x.CanRead))
{
PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name);
if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
MemberExpression propertyExpression = Expression.Property(parameterExpression, sourceProperty);
bindings.Add(Expression.Bind(targetProperty, propertyExpression));
}
}
NewExpression newExpression = Expression.New(targetType);
Expression initializer = Expression.MemberInit(newExpression, bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, parameterExpression);
}