我正在尝试使用表达式树和匿名类型来实现以下目的。
假设我有这门课程:
class Person
{
public string FirstName {get;set;}
public string MiddleName {get;set;}
public string LastName {get;set;}
public DateTime DateOfBirth {get;set;}
}
现在我希望能够拨打以下电话:
string[] names = Foo<Person>(x=> new { x.LastName, x.DateOfBirth });
我希望名称包含2个项目,“LastName”和“DateOfBirth”。
我试图以编译时安全的方式扩展PetaPoco,而不是编写字符串sql,这样我就可以指定我想要包含在SQL中的属性/列列表,而不是选择所有内容。我有一些非常大的实体,有些情况下我不想出于性能原因选择所有列。
答案 0 :(得分:3)
尝试这个尺寸:
public static string[] Foo<T, TResult>(Expression<Func<T, TResult>> func)
{
return typeof(TResult).GetProperties().Select(pi => pi.Name).ToArray();
}
当您从lamda返回匿名类型时,您可以遍历此匿名类型的所有属性并使用属性的推断名称。但是,使用它时,语法更像是:
Foo((Person x) => new { x.LastName, x.DateOfBirth });
这是因为第二个通用参数是一个非常类型的。
答案 1 :(得分:2)
我很懒,所以这段代码只处理公共属性。但它应该是一个很好的基础,让你开始。
public static string[] Foo<T>(Expression<Func<T, object>> func)
{
var properties = func.Body.Type.GetProperties();
return typeof(T).GetProperties()
.Where(p => properties.Any(x => p.Name == x.Name))
.Select(p =>
{
var attr = (ColumnAttribute) p.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
return (attr != null ? attr.Name : p.Name);
}).ToArray();
}
答案 2 :(得分:2)
此处给出的答案在选择单个属性时或在选择多个属性时起作用。它们都不适用于两者。 answer到Lukazoid仅适用于多个属性,其余用于单个属性,就像写我的答案一样。
下面的代码同时考虑了这种情况,也就是说,您可以使用它来选择单个属性和多个属性。请注意,我尚未在此处添加任何完整性检查,因此请随意添加自己的。
string[] Foo<T>(Expression<Func<Person, T>> func)
{
if (func.Body is NewExpression)
{
// expression selects multiple properties,
// OR, single property but as an anonymous object
// extract property names right from the expression itself
return (func.Body as NewExpression).Members.Select(m => m.Name).ToArray();
// Or, simply using reflection, as shown by Lukazoid
// return typeof(T).GetProperties().Select(p => p.Name).ToArray();
}
else
{
// expression selects only a single property of Person,
// and not as an anonymous object.
return new string[] { (func.Body as MemberExpression).Member.Name };
}
}
或者更简洁地说,使用三元运算符就变成了这一点:
string[] Foo<T>(Expression<Func<Person, T>> func)
{
return (func.Body as NewExpression) != null
? typeof(T).GetProperties().Select(p => p.Name).ToArray()
: new string[] { (func.Body as MemberExpression).Member.Name };
}
下载LinkPad文件:LinkPad
在线查看:Repl.it
请随时指出我可能错过的任何内容。
答案 3 :(得分:0)
一页代码是千言万语,所以微软在Prism中的表现如下:
///<summary>
/// Provides support for extracting property information based on a property expression.
///</summary>
public static class PropertySupport
{
/// <summary>
/// Extracts the property name from a property expression.
/// </summary>
/// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
/// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param>
/// <returns>The name of the property.</returns>
/// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when the expression is:<br/>
/// Not a <see cref="MemberExpression"/><br/>
/// The <see cref="MemberExpression"/> does not represent a property.<br/>
/// Or, the property is static.
/// </exception>
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException(Resources.PropertySupport_NotMemberAccessExpression_Exception, "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException(Resources.PropertySupport_ExpressionNotProperty_Exception, "propertyExpression");
}
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
{
throw new ArgumentException(Resources.PropertySupport_StaticExpression_Exception, "propertyExpression");
}
return memberExpression.Member.Name;
}
}
如果你想考虑属性,它会稍微复杂一点,但是接受Expression<Func<T>>
和捕捉被定位属性名称的一般想法是一样的。< / p>
更新:按原样,该方法只接受一个参数;我只提供了它作为指导。这个想法当然可以概括为:
public static string[] ExtractPropertyNames<T>(
Expression<Func<T, object>> propertyExpression)
此方法将接受一个带有T的表达式,并返回一个您可以反思的匿名类型。您可以替换object
的第二个类型参数,但这里并没有真正做任何事情,因为您唯一要做的就是反映类型。
答案 4 :(得分:-1)
我想您必须从Html.LabelFor(LabelExtensions.LabelFor<TModel,TValue>
程序集中反汇编System.Web.Mvc
的代码。
例如,请查看ExpressionHelper.GetExpressionText
至于用属性成员值替换成员名称 - 你必须使用旧式的反射。