MVC 5 T4 Templates and View Model Property Attributes

Yesterday, I published a blog post demonstrating how to modify your T4 templates for your single project.  Today, I’m going to show you how to access an attribute from your Model in your T4 template.  Beginning with MVC 5, the T4 templates no longer use ModelProperty class to iterate over the properties in your Model.  ModelProperty has direct access to Attributes, and there are plenty of resources on the web on how to read the attributes in your T4 using that.  In MVC 5, however, ModelProperty has been replaced with PropertyMetaData, which does not have access to the attributes of your properties.  Further, I found it very difficult to find a solution for MVC5 on the web, until finally I came across an old MSDN Magazine article, which was only helpful because I could download the sample project and take a look inside.  The article itself is missing referenced images and code samples.  So, I’m taking what I learned, and am creating this, hopefully simple, post to demonstrate.

For our purposes today, we will be extending our project from yesterday to make the Biography property on the Person model a RichText field.  So, for a reminder, here is our Person model:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
 
namespace CustomViewTemplate.Models
{     
     [Table("Person")]
     public class Person
     {
         [Key]
         public int PersonId { get; set;}

         [MaxLength(5)]
         public string Salutation { get; set; }

         [MaxLength(50)]
         public string FirstName { get; set; }

         [MaxLength(50)]
         public string LastName { get; set; }

         [MaxLength(50)]
         public string Title { get; set; }

         [DataType(DataType.EmailAddress)]
         [MaxLength(254)]
         public string EmailAddress { get; set; }

         [DataType(DataType.MultilineText)]
         public string Biography { get; set; }     
     }
}

To demonstrate that we can also use a custom attribute, let’s create one.  Create a class called RichTextAttribute in the project root directory:

namespace CustomViewTemplate
{
     [AttributeUsage(AttributeTargets.Property)]
     public class RichTextAttribute : Attribute
     {
         public RichTextAttribute() { }
     }
}

Then, back to PersonModel, add the RichText attribute to the Biography property:

[RichText]
[DataType(DataType.MultilineText)]
public string Biography { get; set; }

Now, copy the following global template files from

C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates\MvcView\

to a folder called

CodeTemplates\MvcView\

  • Create.cs.t4 (OR Create.vb.t4)
  • Imports.include.t4
  • ModelMetaDataFunctions.cs.include.t4 (OR ModelMetaDataFunctions.vb.t4)

Now, remember the PropertyMetaData, doesn’t have access to the attributes, so we need to create a new class with a function that does the work for us, so let’s create a class called T4Helpers, with a public function called IsRichText.

using System; 

namespace CustomViewTemplate
{
     public static class T4Helpers
     {
         public static bool IsRichText(string viewDataTypeName, string propertyName)
         {
             bool isRichText = false;
             Attribute richText = null;
             Type typeModel = Type.GetType(viewDataTypeName);

             if (typeModel != null)
             {
                 richText = (RichTextAttribute)Attribute.GetCustomAttribute(typeModel.GetProperty(propertyName), typeof(RichTextAttribute));
                 return richText != null;
             }

             return isRichText;
         }
     }
}

We are using our custom RichTextAttribute here, but you could use any Attribute you would like.  What we are doing is returning true or false; indicating that the given property either does or does not have the attribute we are checking for.

Before we can use this function in our T4 template, we’ll need to Build our project and then add a reference to our assembly in the imports file.

Open your projects copy of Imports.include.t4 and add a reference to your assembly as line 4.  For me, it looks like this:

<#@ assembly name="C:\My Applications\Playground\CustomViewTemplate\CustomViewTemplate\bin\CustomViewTemplate.dll" #>

As you are typing the line of code above, if you are using tangible T4 Editor, you might see an inellisense option like so:

image

and you might be tempted to use it, but there is a bug that will prevent this form working.

Okay, now we can modify our T4 template.  Open Create.cs.t4 and look for the foreach loop that iterates over our list of properties.  You’ll find this around like 96:

<#
foreach (PropertyMetadata property in ModelMetadata.Properties) {
     if (property.Scaffold && !property.IsAutoGenerated && !property.IsReadOnly && !property.IsAssociation) {
         // If the property is a primary key and Guid, then the Guid is generated in the controller. Hence, this property is not displayed on the view.
         if (property.IsPrimaryKey && IsPropertyGuid(property)) {
             continue;
         }
#>

To avoid unnecessary calls to our new method, we’ll only check for the RichText attribute against properties that we will actually be rendering, so after the inner If statement (the one that checks for primary key’s and Guid’s), we can add this line:

bool isRichText = CustomViewTemplate.T4Helpers.IsRichText(ViewDataTypeName, property.PropertyName);

Now, the tricky part is using our new variable and finding the correct place for it.  For our purposes, we will put it inside the nested if statement that is checking for a checkbox.  So, that entire if block would look like this with our new code added:

             if (isCheckbox) {
#>
                <div class="checkbox">
<#
                 PushIndent("    ");
#>
                 @Html.EditorFor(model => model.<#= property.PropertyName #>)
<#
             } else if (property.IsEnum && !property.IsEnumFlags) {
#>
                 @Html.EnumDropDownListFor(model => model.<#= property.PropertyName #>, htmlAttributes: new { @class = "form-control" })
<#
             } else if (isRichText) {
#>
                 // TODO: Place RichTextBox HTML here
<#
             } else {
#>
                 @Html.EditorFor(model =>; model.<#= property.PropertyName #>, new { htmlAttributes = new { @class = "form-control" } })
<#
             }
          } else { 
#>
                 @Html.EditorFor(model => model.<#= property.PropertyName #>) 
<#
         }
#>

Now, you can see that I just put some generic text instead of an @Html helper, because the implementation details of RichText aren’t important for the purposes of this tutorial.

To verify everything is working properly, we can save changes, go to our PersonController and add a Create action, Right-Click inside to Add View, and add a Create view.

Inside the generated Create.cshtml, you can scroll down and see that our Biography field has our placeholder text, instead of a @Html helper function.

<div class="form-group">
    @Html.LabelFor(model => model.Biography, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        // TODO: Place RichTextBox HTML here
        @Html.ValidationMessageFor(model => model.Biography, "", new { @class = "text-danger" })
    </div>
</div>

If you’ve found this tutorial helpful, please leave a comment below!

MVC 5 Custom View Templates

The out-of-the-box template that comes with Visual Studio has improved a great deal since I first started building ASP.Net Web Pages several years ago. But, as nice as the new template is, it’s still nice to be able to adjust the T4 templates that are used to generate the Views. I’ve known for a long time that I could modify these templates globally, by modifying them directly in the Visual Studio folder, but, it wasn’t until earlier this week that I learned you can customize them for just your single project. This post will demonstrate how to do that. In my next post, I’ll demonstrate how to access the attributes on your Model’s properties, from inside the T4 templates.

For the purpose of this demo, I will be using Visual Studio 2013, MVC 5, EF 6, and .net 4.5.1.

For our sample, we will create a simple application that allows for basic CRUD of a person object. So, fire up NuGet and install EF 6 and add a calls called PersonModel to Models folder:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace CustomViewTemplate.Models
{
    [Table("Person")]
    public class Person
    {
        [Key]
        public int PersonId { get; set;}

        [MaxLength(5)]
        public string Salutation { get; set; }
        
        [MaxLength(50)]
        public string FirstName { get; set; }
        
        [MaxLength(50)]
        public string LastName { get; set; }
        
        [MaxLength(50)]
        public string Title { get; set; }
        
        [DataType(DataType.EmailAddress)]
        [MaxLength(254)]
        public string EmailAddress { get; set; }
        
        [DataType(DataType.MultilineText)]
        public string Biography { get; set; }
    }
}

Now, add a basic Person controller, an Empty one so that only the Index Get method is created:

image

namespace CustomViewTemplate.Controllers
{
    public class PersonController : Controller
    {
        // GET: Person
        public ActionResult Index()
        {
            return View();
        }
    }
}

If you right-click inside Index() and choose Add View, use scaffolding to add a List view.

image

Now, the view will open and you’ll see the default List view, which just happens to be a table.

Take special notice to lines 9-11:

<p>
    @Html.ActionLink("Create New", "Create")
</p>

Now, of course this will create a standard issue hyperlink, but what if, for the sake of this example, we want it to be a button instead. Well, we could just change the HTML, but if we wanted this to be a button by default, we would have to change the button. So, first things first, we need to identify the location of the global template files. That location is

C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates\

Now, you can modify the .t4 files in this directory, but ONLY do that if you want the changes to be global. To make changes only to the project you are working in, copy the direct to a new folder in your project’s root, called “CodeTemplates”. You only need to copy down the files you will want to change, but it is important that you copy the folder structure down. That is, we want to modify a file called List.cs.t4, so we need to copy our file to “~ProjectFolder~\CodeTemplates\MvcView\List.cs.t4”. So for me, that looks like this:

C:\My Applications\Playground\CustomViewTemplate\CustomViewTemplate\CodeTemplates\MvcView\List.cs.t4

Note, if you are using VB, you’ll want List.vb.t4.

IMPORTANT NOTE
You also need to copy over MvcView\Imports.include.t4 and MvcView\ModelMetadataFunctions.cs.include.t4

Back in Visual Studio, in Solution Explorer, you’ll need to “Show all files”, and do a refresh, but you should see your CodeTemplates folder now. Of course, you won’t need the templates for deployment, but if you are modifying them, you should add to source control, so go ahead and right-click and “Include in project”.

Now, you can open the t4 file, but beware, VS doesn’t come with a T4 editor out-of-the-box. That means, no styling and no intellisense. There are, however, some good T4 editor’s in the VS extensions. I use tangible T4 Editor 2.2.5 plus modeling tools for VS 2013 (free edition). Of course, you’ll need Visual Studio to be closed before you can run the installer.

Remember our hyperlink we want to change to a button, well, there’s no special t4 stuff going on with that, and this isn’t a tutorial on the basics of t4, so with List.cs.t4 open, look for our HTML snippet above and change it to:

<p>
    <button type="button" onclick="addNewPerson();">Create New Person</button>
</p>

Now save the file. Now, back to the controller and right-click inside the Index method, and select AddView and add a new List View, but overwrite the existing one.

Now, when you look at the new version of Index.cshtml, you’ll see that instead of a hyperlink, you now have a button!

Happy Templating!

Serializing a Dictionary

For better or for worse, dictionaries are not allowed in .net Web API’s.  List’s are allowed, and what’s a dictionary, if not a list of key, value pairs?  So, I wrote this extension method to “serialize” a dictionary.

public static class ExtensionMethods
{
     public static List<KeyValuePair<T1, T2>> Serialize<T1, T2> (this Dictionary<T1, T2>)
     {
          List<KeyValuePair<T1, T2>> list = new List<KeyValuePair<T1, T2>>();
          list.AddRange(dictionary);
          return list;
     }
}

Then, you call it like this:

Dictionary<int, string> wordsThatStartWithB = new Dictionary<int, string>()
{
     { 1, "Bob" },
     { 2, "Blue" },
     { 3, "Bone" },
     { 4, "Bow" }
};

return wordsThatStartWithB.Serialize();

Of course, there are probably better ways of doing this, which is why I will be open sourcing my extensions library in the coming weeks, and will have a blog post about each one. Do you have any recommendations on improving this?

Windows Phone Error code: 85010014

The Short Version

My phone suddenly began reporting Error Code: 85010014 when attempting to sync my work email.

The Fix

It turns out I had a calendar event with a status of “Meeting Elsewhere”.  Changing the status to “Free/Busy/Tentative/Out Of Office” fixed the problem immediately.

The Long Story

Four days ago, my work email suddenly refused to sync on my phone, giving me error code 85010014.  I searched the web, over and over and over and over, to no avail (which is why I’m posting this), so figuring it out was up to me.

What made it look like a device issue:

Because my Outlook and my Web Access functioned properly, from the business side anyway, it looked as though the problem was on my device somehow.  The first thing I attempted was to change my password, but that didn’t fix the problem.

Why a device issue just didn’t feel right:

There were several reasons why I was sure it wasn’t a problem on my device.  I observed the following:

  • Restarting device didn’t fix
  • Removing and Re-Adding the account didn’t fix
  • Removing and Restarting and Re-Adding the account didn’t fix
  • Outlook.com address was working
  • School email (exchange also) was working

Then, I started reading a forum post I came across, which linked to another post, which linked to another post….anyway, I eventually made my way to a post from a problem someone experienced several years ago on their WP7 device with similar symptoms.  They fixed it by unchecking all the boxes for “Content to sync”, then syncing, then adding “Email”, then syncing, then “Contacts”, sync, “Calendar”, sync, “Tasks”, sync.  He observed that by adding the services back one-by-one, the sync was successful and his problem went away.  So I did the same thing, and noticed the same thing…until I checked the box next to Calendar.  The sync failed.  So, I was able to observe that my account would sync, as long as I didn’t attempt to sync the calendar.  So, I pulled out my old WP 7 device and sure enough, I was able to duplicate this behavior.  So, this helped me narrow down the problem, and eliminate my device. 

I kept reading about “try refreshing your cache”, and I wasn’t sure if that was IE cache, or some special device cache or what, but the Microsoft support folks in the forums kept saying, “contact your device manufacture for instructions to clear the cache”.  So, I updated to Windows Phone 8.1.  I’ve been wanting it anyway, and it was released to developers that day, so I downloaded it.  I assumed it would clear my cache, but whether it did or not, the problem persisted.

So, I added my account to my co-workers phone (another WP8, different manufacturer), and was able to duplicate the behavior.  All the while, his account continued to function properly.

So, now I’m convinced that there is a problem with my account, or my calendar or something, but no one knows what to do, and officially, my employer doesn’t support email on devices.  So, I’ve been dealing with this for four days, and tonight I decided to really dig in and try to get this figured out.

I started by looking at the properties of my calendar in Outlook.  I noticed that I’ve never cleaned my calendar since I started with the company two years ago, so it had well over 400 items.  I thought surely not, but maybe-just-maybe, I’ve reached some sort of threshold and the sync process can’t handle so many calendar items.  So, I performed an archive and removed all events older than 90 days.  Still nothing.

So, I started looking through my upcoming events and found one that had my status as “Meeting Elsewhere”, because I’m trying anything and everything I can think of at this point, I change it to “Free” and try syncing on my phone, success!  I thought maybe it just took a while for the clean-up to take effect, so I changed the status back to “Meeting Elsewhere” and sure enough, it broke again, and to fix it, just switch it back to “Free”.

Configuration Specific Reference Location

Sometimes there is the need to have references dependent upon the configuration you are currently in.  You may have a dependencies folder that all your applications can access, and you have an assembly per configuration.  So when you are debugging your application, you want to use the project reference for your API, but when you deploy to your testing server, you want to use the testing version of the API, or Staging, or Release, or…well, you get the idea.

The solution to this is quite easy.

The details of our example will be as follows:

  • One solution with two projects
    • Consumer (Console application)
    • ServiceAPI
  • Our solution will have four configurations
    • Debug
    • Test
    • Stage
    • Release
  • Debug will use a project reference
  • Test, Stage, and Release will reference C:\Dependencies\Test, (Or \Stage or \Release)

Once you have your solution set up and configured, add a reference to your ServiceAPI inside your Consumer project.  Now we have the basics setup and are ready to modify our project file to have different API paths depending on our current configuration.

First, right-click on the Consumer project and select, edit project file.  If prompted to save changes to your solution, click Yes.

image

Inside your project file you will see a series of PropertyGroups and ItemGroups.  If you notice, you have one PropertyGroup for each configuration of your project, and an ItemGroup for reference items, one for compile items, and one for project references.

We want our project reference to only be a project reference if we are in Debug mode.  If you look through your project file, you will find something like this:

<ItemGroup>
  <ProjectReference Include="..\ServiceAPI\ServiceAPI.csproj">
    <Project>{94c972f3-2724-4258-a16f-b121d8502a27}</Project>
    <Name>ServiceAPI</Name>
  </ProjectReference>
</ItemGroup>

Add the following Condition attribute to the ItemGroup tag.  One thing to note here is that the Condition argument syntax is the same in both Visual Basic and C#.  The comparison operator used is the C# one and you would use the same even if this is a Visual Basic project.

<ItemGroup Condition="'$(Configuration)' == 'Debug'">
  <ProjectReference Include="..\ServiceAPI\ServiceAPI.csproj">
    <Project>{94c972f3-2724-4258-a16f-b121d8502a27}</Project>
    <Name>ServiceAPI</Name>
  </ProjectReference>
</ItemGroup>

Now, we want the ServiceAPI to have a specific folder based on it’s configuration, so add the following ItemGroup to the XML file:

<ItemGroup Condition="'$(Configuration)' != 'Debug'">
  <Reference Include="ServiceAPI">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>C:\Dependencies\$(Configuration)\ServiceAPI.dll</HintPath>
  </Reference>
</ItemGroup>

The HintPath is the location Visual Studio should look for the dll, and you can use the environment variables there, so you notice the hint path includes $(Configuration).  Now you can reload the project (Right click the project and select “Reload Project”.  If prompted to save, click “Yes”.

In Debug mode, you will notice the ServiceAPI is using the project reference, but as we switch between configurations, the path changes:

Debug Mode:

image

Release Mode:

image

Test Mode:

image

Stage Mode:

image

One thing to note as you cycle through the configurations.  The properties grid doesn’t update as you change configurations, but the project does.  That means if you switch from Test to Stage, the properties window will still stay Test, but the project will be using Stage.  If you click another reference and then click back to ServiceAPI, it will update the properties window and you will see the path is actually changed.

Feel free to leave any questions or comments in the comments section below!

Happy Coding!

Send a text message using .net

Sometimes, we want to send text messages from our application.  Whether it’s for sending out a secret code for two-tiered authentication, or sending out messages to subscribed members, it’s a feature that is occasionally needed, and one that I’ve recently needed.  There are many services out there that charge a fee, either monthly or per message, but the services are out there.  I’ve also seen several open-source applications that act as an instant messaging type service, in that they have a GUI and do not provide an API.  What I was unable to find was an API that handled all carriers, and was free.  So I built one, and here is how to use it.

Your first step is to download the single DLL, which is needed to use the API.  It’s open source, so you can get the code and make any changes you want and compile the DLL yourself, if you so desire.  You can get the DLL here, and the code here.

After adding the DLL as a reference to your project, it’s actually quite simple to use.  First, add the appropriate using:

using SMSOverEmail;

After the using statement is added, we are ready to start using the API.  The first step is to instantiate a new SMSOverEmail object.

SMSOverEmail.SMSOverEmail sms = new SMSOverEmail.SMSOverEmail

With our object instantiated, we need to set up the “From” part of our text message.  We are using SMTP to send the actual text message, so this is similar to setting up a SMTP client.

sms.EnableSsl = true;
sms.SMTPUserName = "userName@gmail.com";
sms.SMTPPassword = "password";
sms.Host = "smtp.gmail.com";
sms.Port = 587;
sms.From = new System.Net.Mail.MailAddress("userName@gmail.com");

These settings are correct if you are using Gmail, but you can use any email provider you wish, as long as you know the settings Smile

Once the “From” part is set up, we can set up the “To” part.  This is a text message, so we are delivering to a mobile device.  So, for the purpose of this API, you just need to know the 10 digit phone number, and the carrier it belongs to.  The API does the rest.

sms.PhoneNumber = "2345678901"; //a ten digit phone number
sms.RecipientCarrier = Carrier.SprintPCS;

The phone number is a string, and must be the 10 digit phone number belonging to the phone.  The Carrier enumerator has over 60 carriers to choose from.  In our example, we are sending a text message to (234)567-8901 which is held by a Sprint customer.

Now that the recipient and the sender are setup, we can send our text message.

sms.SendTextMessage("test message");

The SendTextMessage function has many different overloads, they are all documented inline, but you can see the full list on the projects CodePlex documentation page.

No tutorial would be complete without showing all of the code put together in a complete console application:

namespace SSMSTest
{
    using System;
    using SMSOverEmail;

    class Program
    {
        static void Main(string[] args)
        {
            SMSOverEmail.SMSOverEmail sms = new SMSOverEmail.SMSOverEmail();
            sms.EnableSsl = true;
            sms.SMTPUserName = "userName@gmail.com";
            sms.SMTPPassword = "password";
            sms.Host = "smtp.gmail.com";
            sms.Port = 587;
            sms.From = new System.Net.Mail.MailAddress("userName@gmail.com");

            sms.PhoneNumber = "2345678901"; //a ten digit phone number
            sms.RecipientCarrier = Carrier.SprintPCS;

            sms.SendTextMessage("test message");
        }
    }
}

The API also comes with a dictionary containing the actual carrier name, along with the enumerator.  For example, Carrier.ATT is AT&T.  Each enumerator is simple to understand, but I threw it in in case you needed a user-friendly list of carriers.  Assuming sms is the name of your SMSOverEmail object, then sms.CarrierNames is the dictionary.  It’s in the form of Dictionary, where Carrier is the enumerator, and string is the user-friendly name.

As always, I would love to hear from you.  Please leave your thoughts about this post below, especially if this post has helped you Smile  If you have any questions, I’ll be sure to do my best to answer those as well.

Problem accessing regedit after updating to Windows 8

At work, I recently upgraded to Windows 8, and I have to admit that the upgrade was very quick and painless, and, so far, I really enjoy the changes.  The new start menu is taking some time to get used to, but all-in-all I enjoy it.  I recently had a reason to modify the registry, and I got the following error:

image

I thought this was odd because I am an administrator on my box.  I even added myself to the local administrators group explicitly, and chose “Run As Administrator” from the context menu, but the error message still persisted.

Here are the steps I took to fix it:

  1. Open File Explorer (at some point, Windows Explorer needed a name change?) and navigate to C:\Windows\
  2. Right click on regedit.exe and select Properties
  3. Go to the Security tab and click the Advanced button so this screen appears:
    image
  4. Click the Continue button with the shield
  5. Click the Add button and the following window appears:
    image
  6. Click “Select a principle” at the top and the following window appears:
    image
  7. Enter your network ID, click Check Names just to make sure, and then click OK
  8. Click OK, OK, Yes

Now, you can run regedit.exe as normal.

I hope this post has helped you.  Please leave a comment below if you found this post helpful.