如何将lambda表达式传递给Razor帮助方法?

13

我有一个剃须刀辅助方法,需要传入一个Func<>,该方法将返回一些HTML内容以打印出来。这是我最初的代码:

@helper node(string title, Func<HelperResult> descriptions)
{
    ....
    <div>@descriptions()</div>
    ....
}

@node("title", 
              new Func<HelperResult>(() => 
              {
                 return new HelperResult(
                     @<text>
                     <span>"desc1"</span>
                     <span>"desc2"</span>
                     </text>);
              }))

遗憾的是,我的文本从未被打印出来,也没有任何错误提示。

因此,我了解了内联助手,并将调用方法更改为:

@node("title",                     
              @<text>
              <span>"desc1"</span>
              <span>"desc2"</span>
              </text>)

然而,现在我遇到了编译错误,显示:

"委托“System.Func”不接受1个参数"。

但是我没有传递任何参数。

所以,如果我把它改成Func<object,HelperResult>,然后使用@descriptions(null)调用它,我会得到以下错误提示:

"不能将 lambda 表达式用作动态分派操作的参数,而不先将其转换为委托或表达式树类型"

我确定我哪里做错了,但我不确定是哪里。

编辑: 我认为我可能解决了那个问题,但这引入了一些其他问题。

我所做的是在传递给动态方法之前对 lambda 进行强制转换。我想这就是错误试图说的:

@node("title",                     
              ((Func<dynamic, HelperResult>)(@<text>
              <span>"desc1"</span>
              <span>"desc2"</span>
              </text>))

那个函数运行得很好,能正确地打印出标签。不幸的是,在调用这个Func时,我必须传递一个无用的参数。

现在我的问题是,我的真正函数不仅仅是写一些标签那么简单,而更像这样:

@node("title",                     
              ((Func<dynamic, HelperResult>)(@<text>
              <span>@Helpers.Format(resource.Description,"item")</span>
              </text>))

@Helpers.Format 是另一个辅助函数,而 resource 是页面模型中的(动态)变量。

当然,现在代码运行了,但是什么也没有被打印出来(在 <span> 标签内)。我在我的 Format 辅助函数内设置了断点,它被触发且所有参数都正确设置,所以我不确定为什么它不能正确输出。同样,如果我只是将代码更改为 resource.Description, 那么仍然没有任何输出。

由于它在此上下文之外工作得很好,我想知道 Razor 的内联辅助程序是否无法捕获外部变量?

2个回答

9

实际上,HelperResult是Microsoft不希望您使用的东西,这可以从文档中看出:

在System.Web.WebPages命名空间中,public class HelperResult : IHtmlString

摘要:此类型/成员支持.NET Framework基础结构,不建议直接从您的代码中使用。

您的问题可能有一个解决方案,那就是将您的描述函数包装在另一个helper中,然后将该helper作为method group传递给您的node helper,像这样:

@helper Node(string title, Func<HelperResult> descriptions)
{
    <div>@descriptions()</div>
}

@helper Description() {
    <span>desc1</span>
    <span>desc2</span>
}

@Node("title", Description)

无论如何,你的第一个想法都不应该起作用,因为类型为Func的参数实际上等于一个无参函数,在这种情况下,你需要像这样编写Lambda表达式:

myFunction( () => doSomething)

所以你的函数调用应该是:
@node("title", () =>                    
              @<text>
              <span>"desc1"</span>
              <span>"desc2"</span>
              </text>)

虽然这些辅助程序的未来有点不确定,但我会考虑转换为HtmlHelpers用于小段html代码或Partials用于大块内容。


希望HelperResult的未来不是“可疑”的情况!我们公司的流体引导库中广泛使用了HelperResult(等待更完善的版本分享到Github上)。当他们说:“不能直接从您的代码中使用”时,这并不意味着他们正在停止支持它,只是它不会常出现在您的代码中。但许多/大多数MVC工具集/底层库都依赖于这些东西。“这些辅助功能的未来有点不确定”-有人在那个链接中说过吗?幸运的是,我没有看到。干杯。 - Nicholas Petersen
1

例如,这使我们可以强大而美观地直接向库添加Razor代码:

public void Add(Func<object, HelperResult> html) { Add(html.ToString()); }

用户从未看到这些函数,他们只知道像魔术一样,他们可以在链式调用中调用somecontrol.Add(@<div><b>howdy</b></div>)。

该用例适用于流体类型库。
- Nicholas Petersen
@NicholasPetersen 一个主要的商业库,Kendo Grid,使用这些AFAIK,.Template(@<text>@item.Blah<text>),如果这让你感到放心的话。你有关于这些“MVC内联模板”的好文档吗?变化很多,搜索结果却与此无关。它们似乎很强大,我也想更多地使用它们。 - AaronLS
谢谢信息,Aaron!是的,这让我感到放心。我不知道你在“mvc内联模板”方面的问题是否理解,你是指Kendo如何使用它们吗?但是不了解的话,这种技术可以允许用户自定义输出的某些部分,这是有意义的(允许传递razor代码)。 - Nicholas Petersen
我来晚了,但是我想指出我所说的是@helper语法,而不是HelperResult类,尽管它们可能密切相关。我引用了曾在ASP.NET MVC上工作过的“marcind”的话:<<[声明性HTML帮助程序]并没有死亡,我们正在考虑提供一些附加组件(通过MvcFutures或nuget包)来解决这些问题。但是目前它们的未来还很“模糊”>>不过像你说的,我怀疑微软不会突然删除它,因为知道有许多库在使用它。 - Moeri

3
@Test(new Func<object, HelperResult>[]{@<text>hello</text>})

@Test(new Func<object, HelperResult>[]{@<text>hello</text>,@<text>world</text>})


@helper Test(params Func<object, HelperResult>[] results)
{
    foreach (var result in results)   
    {
        @result(null);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接