TemplateCompilationError with RazorEngine and Layouts
Asked Answered
P

5

10

I've been trying to use RazorEngine in a little project of mine but can't get past this error when I try to use template layouts.

Unable to compile template. 'object' does not contain a definition for 'Description' and no extension method 'Description' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

My Setup: I have a template layout like this:

<html>
<head>
    <title>@Model.Description</title>
</head>
<body>
    @RenderBody()
</body>
</html>

And then a page template that looks like this:

@{
    _Layout = "Layout.cshtml";
}
<h1>@Model.Description</h1>

Here is a test Main function I'm using to try and figure this out:

    static void Main(string[] args)
    {
        // Configuration for RazorEngine
        var config = new TemplateServiceConfiguration
        {
            EncodedStringFactory = new RawStringFactory(),

            Resolver = new DelegateTemplateResolver(name =>
            {
                var file = name;
                var content = File.ReadAllText("Templates/" + file);
                return content;
            })
        };

        // Try to render output using Razor
        using (var service = new TemplateService(config))
        {
            string template = File.ReadAllText("Templates/Default.cshtml");

            dynamic model = new ExpandoObject();
            model.Description = "This is a test";
            string result = service.Parse(template, model);
            Console.WriteLine(result);
            if (Debugger.IsAttached)
            {
                Console.ReadLine();
            }

        }
    }

Any idea what I'm missing?

Update: It works if I replace the dynamic model object with a POCO with a Description property. I also tried the typed version of Parse with

dynamic

, ExpandoObject, and IDictionary<string, object> but they all have the same error.

Update: I found this project on Github that seems to make it work somehow: https://github.com/mikoskinen/graze/blob/master/src/core/Graze.cs#L174

Poplar answered 11/11, 2012 at 2:35 Comment(2)
Does it work using a regular POCO as the model?Sped
Yes it does but I need it to be dynamic.Poplar
G
4

I would think it to be more appropriate to use the template service's Parse overload that takes a ViewBag and use the ViewBag in your view rather than a model.

Edit:

public virtual string Parse(string razorTemplate, object model, DynamicViewBag viewBag, string cacheName)
Gregggreggory answered 19/11, 2012 at 3:22 Comment(5)
The application isn't an MVC app so I don't have a ViewBag object.Poplar
The current version of RazorEngine (github.com/Antaris/RazorEngine) has a Parse method that accepts a ViewBag. That's what I was suggesting (see edit).Gregggreggory
Sorry, I thought you were referring to the ViewBag property on ControllerBase. I haven't tried a DynamicViewBay. This method is confusing to me since I can pass in a "model" why would I want to use the viewBag?Poplar
You can use a model (a strongly typed class), a viewbag (a dynamic object) or both for your template. The choice is yours how you use them but seeing your choice is to pass dynamic data to the template rather than strongly typed data, the viewbag seems more appropriate.Gregggreggory
Thanks for workaround suggestion but it doesn't really help me understand what's wrong with what I wanted to do.Poplar
I
4

You can't pass an anonymous type into a dynamically-typed view because the anonymous types are compiled as internal. Since the CSHTML view is compiled into a separate assembly, it can't access the anonymous type's properties.

Incuse answered 22/11, 2012 at 19:4 Comment(1)
It looks like only the base layout is having problems. It works if I just have the single layout and no _Layout = "Layout.cshtml";Poplar
K
2

Create the model as you do above

In your Index template set the ViewBag.Description to the Model.Description

@{
    _Layout = "Layout.cshtml";
    ViewBag.Description = Model.Description;
}

<div>Hello John </div>

In the layout page use the viewbag instead of the model

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
    <head>
        <title>@ViewBag.Description</title>
    </head>
    <body>
        <div id="content">
            @RenderBody()
        </div> 
        @if (IsSectionDefined("Footer"))
        { 
            <div id="footer">
                @RenderSection("Footer")
            </div>
        }
    </body>
</html>

I don't know why the dynamic model cant be used in the layout page. This is how i use it in my project

Kenwee answered 23/11, 2012 at 16:40 Comment(2)
Thanks for another workaround. Still I'd like to know what's wrong with my initial approach.Poplar
I don't know why the layout page wont accept a dynamic model. Hopefully someone who knows the architecture of razor engine in depth will reply.Kenwee
C
1

Razor vs. MVC vs. WebPages vs. RazorEngine There is often a confusion about where Razor sits in this set of technologies. Essentially Razor is the parsing framework that does the work to take your text template and convert it into a compilable class. In terms of MVC and WebPages, they both utilise this parsing engine to convert text templates (view/page files) into executable classes (views/pages). Often we are asked questions such as "Where is @Html, @Url", etc. These are not features provided by Razor itself, but implementation details of the MVC and WebPages frameworks.

Charles answered 8/12, 2022 at 10:22 Comment(0)
C
0

try to use like this :

service.AddTemplate("layout", "<h1>@RenderBody()</h1>");
service.AddTemplate("template", @"@{Layout = ""layout"";}my template");
service.Compile("template");
Charles answered 8/12, 2022 at 10:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.