Problem
What needs to happen is to be able to crosswalk between the two entirely different classes
From: Microsoft.AspNet.Scaffolding.Core.Metadata.PropertyMetadata
To: System.Reflection.PropertyInfo
The PropertyMetadata
class provided by ASP.NET Scaffolding exposes a relatively limited data, and no direct link to the original type or it's attributes.
Here's a comparison of the two properties :
<sup>(click for full res)</sup>
because, honestly, why spend all this time working with a custom model generator without having access to the rich world of data provided by System.Reflection
Solution
-
We're going to build a T4 helper method right inside of our normal web assembly. This is just any static
method that can be consumed externally. We'll register it later inside of the T4 file so it can actually be used.
So add a file like this anywhere in your project:
UtilityTools.cs
<!-- language: lang-cs -->
using System;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
namespace UtilityTools
{
public static class T4Helpers
{
public static bool IsRequired(string viewDataTypeName, string propertyName)
{
bool isRequired = false;
Type typeModel = Type.GetType(viewDataTypeName);
if (typeModel != null)
{
PropertyInfo pi = typeModel.GetProperty(propertyName);
Attribute attr = pi.GetCustomAttribute<RequiredAttribute>();
isRequired = attr != null;
}
return isRequired;
}
}
}
-
Now let's register it by adding your assembly to the `Imports.include.t4: file:
<!-- language: lang-html -->
<#@ assembly name="C:\stash\cshn\App\MyProj\bin\MyProj.dll" #>
Much in the same way that other assemblies are loaded, this actually gives us access to any static methods inside of whatever assembly we've loaded, minimally providing accessing to UtilityTools.T4Helpers.IsRequired
-
Make sure you Rebuild your application (and possibly reload Visual Studio)
-
Now we can make use of that inside any of our T4 templates like this:
<!-- language: lang-html -->
<#= UtilityTools.T4Helpers.IsRequired(ViewDataTypeName, property.PropertyName) #>
Further Reading:
The above solution is heavily adapted from the following articles & questions:
Tip: Debugging T4 templates can sometimes be a bear, so I wrote this test template which helps dump out information from the model so you can easily evaluate ASP.NET is using as values to hydrate PropertyMetadata
Gist: https://gist.github.com/KyleMit/fc9ccfbc2af03462d660257103326509
Note: Type.GetType("Namespace.Qualified.TypeName")
only works when the type is found in either mscorlib.dll or the currently executing assembly. If your model is part of a business library, you either have to use a AssemblyQualifiedName
like this:
<pre><code>Type.GetType("Namespace.Qualified.TypeName<b>,DllName</b>")</code></pre>
or you can search through all of the currently executing assemblies like this:
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
return type;
}
return null;
}
in order for this to work, you'll have to add any business libraries that contain classes to the list of imported assemblies (same as we did earlier in the imports.t4 file):
<pre><code><#@ assembly name="C:\stash\cshn\App\MyProj\bin\MyProj.<b>Business</b>.dll" #></code></pre>