我使用MVC进行REST,这样我就可以利用Razor输出不同的类型。CSV就是其中一种输出。而不是为每个输入类型编写这个模板:
ID,Created,Content
@foreach (var item in Model.TimeData)
{
<text>@item.ID,@item.Created,"@Html.Raw(item.Content.Replace(""", """"))"</text>
}
我想利用params
和System.Linq.Expressions.Expression
来写这样的东西:
@{
Html.WriteCsv<TimeObject>(Model.TimeData, p => p.ID, p => p.Created, p => p.Content);
}
我开始写一个通用的HtmlHelper
,并很快意识到我有值类型的问题(memberExpression将为null)。下面的代码试图只写出CSV标题(ID,Created,Content),但它只输出"Content"(因为ID和Created是值类型(int
和DateTime
)。
public static void WriteCsv<TModel>(this HtmlHelper htmlHelper, List<TModel> list, params Expression<Func<TModel, object>>[] expressions)
{
foreach (var expression in expressions)
{
MemberExpression memberExpression = expression.Body as MemberExpression;
if (memberExpression != null)
{
var propertyInfo = (PropertyInfo)memberExpression.Member;
htmlHelper.ViewContext.Writer.Write(propertyInfo.Name + Environment.NewLine);
}
}
}
我试着用dynamic
代替object
,认为这会起作用,但是当我快速观看expression.Body
时,它似乎仍然认为它正在处理一个对象(DebugView
属性是(System.Object)$p.ID
)。
这在c# 4.0中是不可能的吗?
这是我使用它的类型:
[DataContract(IsReference = true, Namespace = "urn:test:TimeObject")]
public class TimeObject
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public DateTime Created { get; set; }
}
在表达式引用值类型的情况下,编译器必须将引用框起来;它含蓄地做到了这一点。这种复杂性意味着Value Type成员表达式的表达式树不是简单的MemberExpression,因此强制转换返回null。
下面是从值类型或引用类型成员表达式中获取属性名的一般解决方案,取自这个问题:
private string GetPropertyName(Expression<Func<object, object>> f) {
var body = f.Body;
if (body.NodeType==ExpressionType.Convert)
body = ((UnaryExpression) body).Operand;
if ((body as MemberExpression) != null) {
return (body as MemberExpression).Member.Name;
}
return "";
}