Home > Back-end >  How to include another razor template in a loop when using RazorLight?
How to include another razor template in a loop when using RazorLight?

Time:07-20

I have a very simple code sample where I am trying to include a razor template in a loop, but it does not work. Please, observe:

C:\temp\test> dir


    Directory: C:\temp\test


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         7/19/2022  12:23 AM            174 FullMessage.cshtml
-a----         7/19/2022  12:41 AM             17 MessageItem.cshtml
-a----         7/19/2022   1:02 AM           1462 Program.cs
-a----         7/19/2022  12:54 AM            592 Test.csproj

The main code is in Program.cs:

#pragma warning disable CS8619
#pragma warning disable CS8604
#pragma warning disable CS8622

using RazorLight;

var engine = new RazorLightEngineBuilder()
    .UseFileSystemProject(Directory.GetCurrentDirectory())
    .UseMemoryCachingProvider()
    .Build();

int? index = null;
string templateFilePath = "FullMessage.cshtml";

if (args.Length > 0)
{
    index = int.Parse(args[0]);
    templateFilePath = "MessageItem.cshtml";
}

var list = new[]
{
    new
    {
        Name = "John"
    },
    new
    {
        Name = "Jane"
    }
};
object model = list;
if (index.HasValue)
{
    model = list[index.Value];
    Console.WriteLine("Expected to get "   list[index.Value].Name);
}
else
{
    foreach (var item in list)
    {
        Console.WriteLine("Expected to get "   item.Name);
    }
}
Console.WriteLine(engine.CompileRenderAsync(Path.GetFileName(templateFilePath), model).GetAwaiter().GetResult());

The other files are much smaller and are listed at the end of this question.

So when I run it with 0 or 1, the code renders the first or the second item in the list provided in Input.json:

Build

C:\temp\test> dotnet build
Microsoft (R) Build Engine version 17.2.0 41abc5629 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  Restored C:\temp\test\Test.csproj (in 342 ms).
  Test -> C:\temp\test\bin\Debug\net6.0\Test.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:03.36

Run

C:\temp\test> .\bin\Debug\net6.0\Test.exe 0
Expected to get John
Name: John
C:\temp\test> .\bin\Debug\net6.0\Test.exe 1
Expected to get Jane
Name: Jane
C:\temp\test>

Everything is good. Now I will run without any parameters. In this case the entire list is rendered while including the item template:

C:\temp\test> .\bin\Debug\net6.0\Test.exe
Expected to get John
Expected to get Jane
Unhandled exception. Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot implicitly convert type 'void' to 'object'
   at CallSite.Target(Closure , CallSite , Object )
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
   at RazorLight.CompiledTemplates.GeneratedTemplate.ExecuteAsync() in /FullMessage.cshtml:line 6
   at RazorLight.TemplateRenderer.RenderPageCoreAsync(ITemplatePage page, PageContext context)
   at RazorLight.TemplateRenderer.RenderPageAsync(ITemplatePage page, PageContext context, Boolean invokeViewStarts)
   at RazorLight.TemplateRenderer.RenderAsync(ITemplatePage page)
   at RazorLight.EngineHandler.RenderTemplateAsync[T](ITemplatePage templatePage, T model, TextWriter textWriter, ExpandoObject viewBag)
   at RazorLight.EngineHandler.RenderTemplateAsync[T](ITemplatePage templatePage, T model, ExpandoObject viewBag)
   at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag)
   at Program.<Main>$(String[] args) in C:\Temp\test\Program.cs:line 45

What am I doing wrong?

The rest of the files are below:

FullMessage.cshtml

Count of issues with the source information: @Model.Count

@foreach (dynamic item in Model)
{
    <p>
    @await IncludeAsync("MessageItem.cshtml", item);
    </p>
}

MessageItem.cshtml

Name: @Model.Name

Test.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PreserveCompilationContext>true</PreserveCompilationContext>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="RazorLight" Version="2.1.0" />
  </ItemGroup>

  <ItemGroup>
    <None Update="*.cshtml" CopyToOutputDirectory="PreserveNewest" />
  </ItemGroup>

</Project>

CodePudding user response:

You need to use curly brace in IncludeAsync call in FullMessage.cshtml.

@{await IncludeAsync("MessageItem.cshtml", item);}
  • Related