I am trying to create a replacement T4 template for the MVC5 Controller. I have everything working except for one issue.
I need to generate code for each property in the Model, and looping through ModelMetadata.Properties
is actually really easy. However it is not an array of PropertyInfo
s. Rather it is an array of PropertyMetadata
which doesn't seem to have any information about whether a property is required or not or if its type is nullable or not. So properties in your model of type int
and int?
both show up as type System.Int32
.
Furthermore there is no way to get a list of PropertyInfo
s being that you can't really get a Type
Object for the model which you are scaffolding, being that only the short type name is passed to the template.
In summation: Is there any way to know in a T4 template if a property is nullable?
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 : (click for full res)
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
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
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:
<#@ 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:
<#= 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 aAssemblyQualifiedName
like this:Type.GetType("Namespace.Qualified.TypeName,DllName")
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):
<#@ assembly name="C:\stash\cshn\App\MyProj\bin\MyProj.Business.dll" #>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With