简体   繁体   中英

MVC5 T4 ModelMetadata.Properties IsNullable or IsRequired

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?

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 : 物业比较 (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

Solution

  1. 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; } } }
  2. 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" #>

    进口.include.t4

    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

  3. Make sure you Rebuild your application (and possibly reload Visual Studio)

  4. Now we can make use of that inside any of our T4 templates like this:

     <#= UtilityTools.T4Helpers.IsRequired(ViewDataTypeName, property.PropertyName) #>

    IsRequired 助手用法

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 :

 Type.GetType("Namespace.Qualified.TypeName  ")

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.  .dll" #>

I solve this problem, with:

  1. you must import assemblies
  • <#@ assembly name="System.Web.Mvc.dll" #>

  • Current project Dll : <#@ assembly name="D:\\\\TestSample\\\\TestSample\\\\bin\\\\TestSample.dll"#>

  • and models namespace in your project: <#@ import namespace="TestSample.Models" #>

  1. Add this code in the right place
<# 
foreach (PropertyMetadata property in ModelMetadata.Properties) {
    var T = Type.GetType(ViewDataTypeName+", TestSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
    var ss = System.Web.Mvc.ModelMetadataProviders.Current.GetMetadataForProperty(null ,T ,property.PropertyName );#>

    <#= ss.IsRequired #>

Hope my code will help you

Yes, there is a way to detect if a type is nullable when creating a custom t4 scaffolding template.

In my Create t4 template I am able to compare the property TypeName , which is a string, to "System.Nullable<System.Int32>"
Or whatever the nullable type is. In my scenario I needed to detect a nullable datetime, so I was looking for the string "System.Nullable<System.DateTime>"

For example:

The default t4 scaffolding template has the following loop, which I have modified to detect if I have a nullable DateTime property:

string nullableDateTimeType = "System.Nullable<System.DateTime>";

foreach (PropertyMetadata property in ModelMetadata.Properties) {
    bool isNullableDateTime = property.TypeName.Equals(nullableDateTimeType);

    if( isNullableDateTime )
    {
        // do something
    }
}

Also note: if you want to detect a type that is not nullable, it would just be a comparison to a string "System.DateTime" or "System.Int32"

For more information on creating a customized t4 scaffolding template based on the default t4 templates, see How to Create Custom Scaffold Templates in ASP.NET MVC

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM