C# 迭代lambda表达式的属性
我试图泛化一个复杂的控件,这个控件在我的网站上经常使用,但有不同的字段。控件中的功能总是相同的,只是底层字段发生了变化 为了实现显示不同字段的方法,我正在尝试创建一个HTMLHelper扩展,它接受一个C# 迭代lambda表达式的属性,c#,.net,lambda,expression-trees,C#,.net,Lambda,Expression Trees,我试图泛化一个复杂的控件,这个控件在我的网站上经常使用,但有不同的字段。控件中的功能总是相同的,只是底层字段发生了变化 为了实现显示不同字段的方法,我正在尝试创建一个HTMLHelper扩展,它接受一个表达式作为参数,它将包含控件中显示所需的类的属性。例如: 观点: @model Project.Core.Page @Html.MyHelper(p => new { p.Author.Name, p.Author.Location, p.Author.Age }); 这是我遇到问题的扩展
表达式
作为参数,它将包含控件中显示所需的类的属性。例如:
观点:
@model Project.Core.Page
@Html.MyHelper(p => new { p.Author.Name, p.Author.Location, p.Author.Age });
这是我遇到问题的扩展-我如何迭代lambda中提供的参数,为每个参数提供TextBoxFor()
,或者甚至手动创建input
元素,并用lambda参数的值和名称填充它
psuedo中的扩展:
public static MvcHtmlString MyHelper<TModel,TProperty>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel,TProperty>> expression) {
foreach (var parameter in expression.???) {
// helper.TextBoxFor(???)
// TagBuilder("input").Attributes("name", expression.???)
}
}
publicstaticmvchtmlstring-MyHelper(
这个HtmlHelper助手,
表达式(表达式){
foreach(表达式中的var参数){
//用于(???)的helper.textbox
//标记生成器(“输入”)。属性(“名称”,表达式。?)
}
}
我觉得我已经盯着这个太久了,我也觉得有一个更简单的方法我忽略了实现这一点
非常感谢您的帮助。如果您需要进一步的详细信息,或者我遗漏了一些重要的内容,请告诉我。您将创建的表达式将相对复杂-它需要获取所有属性,然后调用匿名类型构造函数。“拆卸”可能会让人痛苦。。。虽然如果您仍然想尝试,我建议只留下一个空的方法实现,然后查看调试器,看看表达式是什么样子
如果您愿意接受一种稍微丑陋的调用代码形式,那么实现它会简单得多:
@Html.MyHelper(p => p.Author.Name, p => p.Author.Location, p => p.Author.Age);
您可以使其采用params表达式
,也可以使用不同数量的参数声明多个重载,例如
// Overload for one property
MyHelper<TModel, TProperty1>(this ..., Expression<Func<TModel, TProperty1>> func1)
// Overload for two properties
MyHelper<TModel, TProperty1, TProperty2>(this ...,
Expression<Func<TModel, TProperty1>> func1,
Expression<Func<TModel, TProperty2>> func2)
//一个属性的重载
MyHelper(此…,表达式func1)
//两个属性的重载
我的助手(这个。。。,
表达式func1,
表达式(2)
等等。如果您假设:
输入表达式的结果是一个投影(返回一个新对象,
(匿名或其他)
投影的元素都是成员表达式
,不包含对模型或其子对象的方法的调用
然后,您可以使用以下方法实现您想要的:
编辑:
在意识到我的第一个示例无法处理具有复杂属性的对象之后,我更新了代码以使用helper方法访问属性值。此方法使用递归遍历属性链以返回适当的值
public static MvcHtmlString MyHelper<TModel,object>(
this HtmlHelper<TModel> helper,
Expression<Func<TModel,object>> expression) {
var newExpression = expression.Body as NewExpression;
TModel model = helper.ViewData.Model;
foreach (MemberExpression a in newExpression.Arguments) {
var propertyName = a.Member.Name;
var propertyValue = GetPropertyValue<TModel>(model, a);
// Do whatever you need to with the property name and value;
}
}
private static object GetPropertyValue<T>(T instance, MemberExpression me) {
object target;
if (me.Expression.NodeType == ExpressionType.Parameter) {
// If the current MemberExpression is at the root object, set that as the target.
target = instance;
}
else {
target = GetPropertyValue<T>(instance, me.Expression as MemberExpression);
}
// Return the value from current MemberExpression against the current target
return target.GetType().GetProperty(me.Member.Name).GetValue(target, null);
}
publicstaticmvchtmlstring-MyHelper(
这个HtmlHelper助手,
表达式(表达式){
var newExpression=expression.Body作为newExpression;
TModel model=helper.ViewData.model;
foreach(newExpression.Arguments中的MemberExpression a){
var propertyName=a.Member.Name;
var propertyValue=GetPropertyValue(模型,a);
//使用属性名称和值执行任何需要的操作;
}
}
私有静态对象GetPropertyValue(T实例,MemberExpression me){
目标对象;
if(me.Expression.NodeType==ExpressionType.Parameter){
//如果当前MemberExpression位于根对象,则将其设置为目标。
目标=实例;
}
否则{
target=GetPropertyValue(实例,me.Expression作为MemberExpression);
}
//根据当前目标从当前MemberExpression返回值
返回target.GetType().GetProperty(me.Member.Name).GetValue(target,null);
}
注意:我没有在我的IDE中直接将其作为MVC扩展方法来实现,因此可能需要对语法稍作修改。也许构建器风格的API可以简化:
@(Html.MyHelper(p)
.Add(p => p.Author.Age)
.Add(p => p.Author.LastName, "Last Name")
.Build())
请注意,这允许您在需要时添加可选参数
代码应该是这样的
public static class Test
{
public static Helper<TModel> MyHelper<TModel>(this HtmlHelper helper, TModel model)
{
return new Helper<TModel>(helper, model);
}
}
public class Helper<TModel>
{
private readonly HtmlHelper helper;
private readonly TModel model;
public Helper(HtmlHelper helper, TModel model)
{
this.helper = helper;
this.model = model;
}
public Helper<TModel> Add<TProperty>(Expression<Func<TModel, TProperty>> expression)
{
// TODO
return this;
}
public MvcHtmlString Build()
{
return new MvcHtmlString("TODO");
}
}
公共静态类测试
{
公共静态帮助程序MyHelper(此HtmlHelper帮助程序,TModel模型)
{
返回新辅助对象(辅助对象、模型);
}
}
公营助理员
{
私人只读HtmlHelper助手;
私有只读TModel模型;
公共助手(HtmlHelper助手,TModel模型)
{
this.helper=helper;
this.model=模型;
}
公共助手添加(表达式)
{
//待办事项
归还这个;
}
公共MvcHtmlString生成()
{
返回新的MvcHtmlString(“TODO”);
}
}
考虑一下:
创建应用程序代码文件夹
将razor助手文件Templates.cshtml放入
如下所示:
@helper Print(string myvalue,string special="")
{
<pre> <input id="id" type="text" value ="@myvalue" data-val="@special"/> </pre>
}
@model IEnumerable<MvcApplication1.Models.Book>
@{
ViewBag.Title = "Books";
}
<h2>Books</h2>
@foreach(var book in Model)
{
@Templates.Print(book.Title,book.ISBN);
}
@helper打印(字符串myvalue,字符串special=”“)
{
}
这样,您就不必在C#文件中编写HTML。它非常方便
Authors.cshtml如下所示:
@helper Print(string myvalue,string special="")
{
<pre> <input id="id" type="text" value ="@myvalue" data-val="@special"/> </pre>
}
@model IEnumerable<MvcApplication1.Models.Book>
@{
ViewBag.Title = "Books";
}
<h2>Books</h2>
@foreach(var book in Model)
{
@Templates.Print(book.Title,book.ISBN);
}
@model IEnumerable
@{
ViewBag.Title=“作者”;
}
作者
@foreach(模型中的var auth)
{
@模板打印(授权名称);
}
books.cshtml如下所示:
@helper Print(string myvalue,string special="")
{
<pre> <input id="id" type="text" value ="@myvalue" data-val="@special"/> </pre>
}
@model IEnumerable<MvcApplication1.Models.Book>
@{
ViewBag.Title = "Books";
}
<h2>Books</h2>
@foreach(var book in Model)
{
@Templates.Print(book.Title,book.ISBN);
}
@model IEnumerable
@{
ViewBag.Title=“图书”;
}
书
@foreach(模型中的var账簿)
{
@模板.打印(book.Title,book.ISBN);
}
根据每个模型类的需要插入所有特殊属性。如果变得太复杂,请查看dynamic和expando对象。这取决于您的模型/视图模型有多复杂。我曾考虑过这种方法,但正如您所说的有点难看,我将把它作为“方案B”保留下来:)谢谢你的回答,Jon。在重新阅读了问题和我的回答之后,我意识到我的第一个示例无法处理对复杂属性值的访问