Как я могу разбить лямбда-выражение на объекты, которые составляют выражение?
Учитывая выражение, такое как:
m => m.Employee.Address.Line1
Мне нужно создать функцию, которая может ходить по выражению, вытаскивая вовлеченные объекты или, по крайней мере, конечный объект в цепочке (то есть объект Address выше). Так, для приведенного выше примера функция найдет:
- "здесь" - это экземпляр параметра m (из которого я могу получить тип)
- "здесь" - это экземпляр члена Employee
- (т.е. повторить по цепочке)
Цель состоит в том, чтобы иметь возможность генерировать конфигурационную информацию для "пути".
Если я могу хотя бы получить окончательный "содержащий" объект, то я могу произвести информацию о типе, а также вызвать объект, если он поддерживает определенные атрибуты или методы, для запроса дополнительной информации о конфигурации от объекта (и типа объекта для получения информация о конфигурации для типа контейнера).
Даже немного более сложные утверждения делают это еще сложнее, например:
m.Employee.Address [я].Line1
--- Просто, чтобы предоставить пример того, что у меня есть, работая над базовым сценарием в MVC. Приведенный ниже GetConistingModel() вызывается внутри CreateMetadata() в загруженном DataAnnotationsMetadataProvider. Модель, которая передается в CreateMetadata() - это модель в корне оператора Lambda, а не то, что нам нужно. Таким образом, логика пытается найти конечный содержащий объект. Я бы предпочел НЕ использовать рефлексию, как я, или хуже, добираясь до частного участника (плохо), но... Я готов к предложениям.
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
private object GetContainingModel()
{
if (_metadata == null)
{
throw new SystemException("Unexpected NULL Metadata");
}
var model = _metadata.Model;
try
{
if (_modelAccessor != null)
{
var data = _modelAccessor.Target;
if (data != null)
{
if (GetModelFromContainer(data, ref model))
{
GetModelFromExpression(data, ref model);
}
}
}
}
catch (Exception ex)
{
var msg = ex.Message;
}
return model;
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
private bool GetModelFromContainer(object data, ref object model)
{
var isSet = false;
var container = data.GetType().GetField("container");
if (container != null)
{
model = container.GetValue(data);
isSet = true;
}
else
{
var vdi = data.GetType().GetField("vdi");
if (vdi != null)
{
var viewDataInfo = vdi.GetValue(data) as ViewDataInfo;
if (viewDataInfo != null)
{
model = viewDataInfo.Container;
isSet = true;
}
}
}
return isSet && model != null;
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
private bool GetModelFromExpression(object data, ref object model)
{
if (model == null)
return false;
var expressionField = data.GetType().GetField("expression");
if (expressionField == null)
return false;
return GetModelFromExpression(expressionField.GetValue(data) as LambdaExpression, ref model);
}
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
private bool GetModelFromExpression(LambdaExpression sourceExpression, ref object model)
{
if (sourceExpression == null)
return false;
var expressionBody = sourceExpression.Body as MemberExpression;
if (expressionBody == null || expressionBody.NodeType != ExpressionType.MemberAccess || expressionBody.Member == null)
return false;
var expression = expressionBody.Expression as MemberExpression;
if (expression == null)
{
return false;
}
switch (expression.Member.MemberType)
{
case MemberTypes.Field:
if (expression.NodeType == ExpressionType.MemberAccess)
{
var fieldInfo = (FieldInfo)expression.Member;
if (fieldInfo != null)
{
model = fieldInfo.GetValue(model);
}
}
break;
case MemberTypes.Property:
var propertyInfo = (PropertyInfo)expression.Member;
if (propertyInfo != null)
{
model = propertyInfo.GetValue(model, null);
}
break;
}
return model != null;
}