Classes in razor engine template

2019-02-07 20:43发布

问题:

Is it possible to create classes within a template? Something like...

@{
    public class MyClass {
        public MyClass() {
            Three = new List<string>();
        }

        public string One { get; set; }
        public int Two { get; set; }
        public List<string> Three { get; set; }
    }
}

Currently I get "Unable to compile template. Check the Errors list for details." when I try to do this. I would like to take XML content and use XmlSerializer to create an instance of MyClass within the template. I can't do the deserialization before hand and shove it into the model because the classes could vary depending on the template.

回答1:

I'll post my response from the CodePlex Discussion here:

I'm not sure that is currently possible. When you use codeblocks (@{ }), you're actually writing code within a method, e.g. your above code would do something like:

public void Execute()
{
    this.Clear();
    public class MyClass {
        public MyClass() {
            Three = new List<string>();
        }

        public string One { get; set; }
        public int Two { get; set; }
        public List<string> Three { get; set;}
    }
}

...which of course, is not valid C#. The other problem you will face, is that to use xml serialisation/deserialisation, the type must be known, but if you are defining your type within the template itself, how could you deserialise it in the first place?

What you could do, is use a custom base template:

public class CustomTemplateBase<T> : TemplateBase<T>
{
    public dynamic Instance { get; set; }

    public dynamic CreateInstance(string typeName)
    {
        Type type = Type.GetType(typeName);

        // You'd to your deserialisation here, I'm going to
        // just cheat and return a new instance.
        return Activator.CreateInstance(type);
    }
}

Using a dynamic property and dynamic return type, we've defined a method that will let us create an instance (through activation or deserialisation, etc.) and call member access on it. To use that in a template, you could then do:

@{
  Instance = CreateInstance("ConsoleApplication1.MyClass, ConsoleApplication1");
  Instance.One = "Hello World";
}
<h1>@Instance.One</h1>

Where "MyClass" is a defined somewhere in my application. The important thing is, I'm creating an instance per template.



回答2:

Yes, this is completely possible. Use the @functions keyword:

@functions {
    public class MyClass {
        public MyClass() {
            Three = new List<string>();
        }

        public string One { get; set; }
        public int Two { get; set; }
        public List<string> Three { get; set; }
    }
}


回答3:

I would suggest using a specific ViewModel class, which could have a dynamic property (ExpandoObject) allowing you to populate it with any custom data structure as needed while still communicating strongly typed for whatever else your view might need.

This also keeps your view models separate from the views themselves, which is good practice (html and code don't mix too well where readability is a concern).



标签: c# razor