3.18.2013

PowerShell Script that removes VSS bindings and files from a Visual Studio Project

Below is a powershell script I found from the web that I have been using for a while to remove the bindings from SourceSafe.

First, unbind the project from VSS within Visual Studio, then run this script.
# This script removes VSS bindings and files from a VS Project
# Taken from http://blog.magenic.com/blogs/daniels/archive/2008/11/18/Removing-VSS-Bindings-to-Migrate-SSRS-2005-Solutions-to-TFS-Using-Powershell.aspx

# First thing to do before running this script is to specify where the project is located
$Folder = "C:\dev\vs\2008\SalesSync"
#first clear the read-only flag from all files
get-childitem "$folder" -Recurse | % {         
        # Test for ReadOnly flag and remove if present 
     if ($_.attributes -band [system.IO.FileAttributes]::ReadOnly) {  
            $_.attributes = $_.attributes -bxor [system.IO.FileAttributes]::ReadOnly 
   }
}

#next delete all files that are *.suo, *.user, and *.*scc - we don't want them in TFS
Get-ChildItem  $folder *.suo -Recurse -Force | Remove-Item -Force
Get-ChildItem  $folder *.*scc -Recurse -Force | Remove-Item -Force
Get-ChildItem  $folder *.user -Recurse -Force | Remove-Item -Force

#next get all the .sln file - and remove the VSS binding information
$files = Get-ChildItem $folder *.sln -Recurse 
foreach ($file in $files) {
 $fileout = $file.FullName + ".new"
 Set-Content $fileout $null
 $switch=0
 Get-Content $file.FullName | % {
  if ($switch -eq 0) {
   if ($_  -eq " GlobalSection(SourceCodeControl) = preSolution") {
    #we found the section to skip - so set the flag and don't copy the content
    $switch=1}
   else {
    #we haven't found it yet - so copy the content
    Add-Content $fileout $_
   }        
   }
  elseif ($switch -eq 1) {
   if ($_ -eq " EndGlobalSection") {
    #last line to skip - after it we start writing the content again
    $switch=2}
   }
  else 
   { #write remaining lines
    Add-Content $fileout $_}
 }
 #remove the original .sln and rename the new one
 $newname = $file.Name
 Remove-Item $file.FullName
 Rename-Item $fileout -NewName $newname
}

3.15.2013

Client-Side Validation for a Custom DataAnnonation in MVC 4

This is a continuation of my previous post. This is based from this excellent tutorial.

First step is to implement IClientValidatable interface in the FileTypesAttribute class:
public class FileTypesAttribute : ValidationAttribute, IClientValidatable {
        private readonly List _types;
        private readonly string specifiedTypes;

        public FileTypesAttribute(string types) {
            _types = types.Split(',').ToList();
            specifiedTypes = types;
        }

        public override bool IsValid(object value) {
            if (value == null) return true;
            var postedFile = value as HttpPostedFileBase;
            var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1);
            return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase);
        }

        public override string FormatErrorMessage(string name) {
            return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types));
        }

        public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
            var rule = new ModelClientValidationRule();
            rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
            rule.ValidationType = "filetypes";
            rule.ValidationParameters.Add("types", specifiedTypes);
            yield return rule;
        }
    }
Things to note here are the validationtype="filetypes" and the added parameter="types", which can be observed in the emitted html below:
<input data-val="true" data-val-filetypes="Invalid file type. Only xls, xlsx are supported."
data-val-filetypes-types="xls,xlsx" data-val-required="The AttachmentTrace field is required."
id="AttachmentTrace" name="AttachmentTrace" type="file" value="" />
The added parameter called types has a value of "xls,xlsx". This parameter and validationtype are significant because it will be used for the jquery validator and adapter as follows:
$(function () {
    // custom validation method
    $.validator.addMethod("filetypes",
        function (value, element, params) {
            if (value == null) return true;
            var fileExtension = value.split('.').pop().toLowerCase();
            var types = params.toLowerCase();
            return types.indexOf(fileExtension) !== -1;
        }
    );
    // add to unobtrusive adapters
    $.validator.unobtrusive.adapters.addSingleVal("filetypes", "types");
} (jQuery));
The value has the contents of the input element, while params has the parameter value of "xls,xlsx" from the data annotation.

3.13.2013

ModelState.IsValid always returning False for RegularExpression ValidationAttribute for a File Upload in MVC 4

I posted a question in stackoverflow and ended up answering it on my own. Some people gave me clues along the way so I'm thankful. I just want to do a recap of the solution that way I have it in my notes...

I was having issues with model validation using RegularExpression as indicated in my class below, the property AttachmentTrace is a file attachment that is uploaded during form post.
public class Certificate {
    [Required]
    // TODO:  Wow looks like there's a problem with using regex in MVC 4, this does not work!
    [RegularExpression(@"^.*\.(xlsx|xls|XLSX|XLS)$", ErrorMessage = "Only Excel files (*.xls, *.xlsx) files are accepted")]
    public string AttachmentTrace { get; set; }
}
All the while I thought there's something wrong with my Regex. But after looking closely on this issue, I found out that what is being validated on the server side is the string "System.Web.HttpPostedFileWrapper", and not the actual filename. That's the reason why ModelState.IsValid returns a false everytime, no matter how right the regex is. But I cannot simply switch the property type of string to HttpPostedFileBase in my model, because I'm using EF code-first migration, which will result into an unpleasant error message when adding a migration.

So the solution to this is to employ a ViewModel, instead of the Entity directly:
public class CertificateViewModel {
    // .. other properties
    [Required]
    [FileTypes("xls,xlsx")]
    public HttpPostedFileBase AttachmentTrace { get; set; }
}
The next step is to create a custom ValidationAttribute for the FileTypes:
public class FileTypesAttribute : ValidationAttribute {
    private readonly List _types;

    public FileTypesAttribute(string types) {
        _types = types.Split(',').ToList();
    }

    public override bool IsValid(object value) {
        if (value == null) return true;
        var postedFile = value as HttpPostedFileBase;
        var fileExt = System.IO.Path.GetExtension(postedFile.FileName).Substring(1);
        return _types.Contains(fileExt, StringComparer.OrdinalIgnoreCase);
    }

    public override string FormatErrorMessage(string name) {
        return string.Format("Invalid file type. Only {0} are supported.", String.Join(", ", _types));
    }
}
In the controller Action, use the ViewModel instead of the Entity, then map the ViewModel back to the Entity via AutoMapper:
public ActionResult Create(CertificateViewModel certificate, HttpPostedFileBase attachmentTrace, HttpPostedFileBase attachmentEmail) {
        if (ModelState.IsValid) {
            // Let's use AutoMapper to map the ViewModel back to our Certificate Entity
            // We also need to create a converter for type HttpPostedFileBase -> string
            Mapper.CreateMap().ConvertUsing(new HttpPostedFileBaseTypeConverter());
            Mapper.CreateMap();
            Certificate myCert = Mapper.Map(certificate);
            // other code ...
        }
        return View(myCert);
    }
For the AutoMapper, create a TypeConverter for HttpPostedFileBase:
public class HttpPostedFileBaseTypeConverter : ITypeConverter {

    public string Convert(ResolutionContext context) {
        var fileBase = context.SourceValue as HttpPostedFileBase;
        if (fileBase != null) {
            return fileBase.FileName;
        }
        return null;
    }
}
I know, it's a lot of work, but this will ensure the file extension validation will be done correctly. But wait, this is not yet complete, I need to also make sure that the client side validation will work for this and that will be the topic of my next post.

3.11.2013

Download Any File Type in ASP.NET MVC 4 FileResult

Just simply specify a generic Octet stream as the contentType:
        public FileResult Download(string filename) {
            filename = Server.HtmlEncode(filename);
            string filePath = ConfigurationManager.AppSettings["SignatureFilesPath"];
            string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
            return File(Path.Combine(filePath, filename), contentType, filename);
        }

Configure MachineKey When Deploying App In A Web Farm

One thing to keep in mind when deploying in a web farm is that if you are using form authentication, or ViewState, and you want to keep your session alive across the web farm, you need to specify an machineKey in the web.config file. This is how it looks like:
<system.web>
     <machineKey validationKey="C82FF1CB5C0B723C0C6682274C87AE0A32E957D05846F505A1968D30EB41B7C34175741FFC45C20D89915C05D133EDE69154E9EDC6B82F7B76B24009B09DF299" decryptionKey="A941AD982309BBC34B1D17C2E5C3A27298CB32EBB29C7AD94F513ACCE09B1F06" validation="SHA1" decryption="AES"></machineKey>
</system.web>
You should generate your own machineKey, here's a machineKey generator.
Reference from msdn.

Setting Up Folder Permissions For Uploading Files in IIS 6

There are so many solutions to this problem. But it all boils down to the permissions given to the account to be used in accessing a particular resource. Assuming the website is saving a document to a Windows Share on another server. You will have to give Read/Write access to your account of choice (a system account maybe that password never expires) to that particular Share. Then in the web.config file, configure impersonation to use that same account like so ...
<system.web>
     <identity impersonate="true" userName="dom1\sys-account" password="mypass"></identity />
Here's a post from msdn regarding IIS Authentication with impersonation. In particular about intranet scenarios.

3.08.2013

EF Code First Migrations - How To Sync To Production DB

It's very simple to push out the changes in the database across any db that would need the changes, be it your test, staging or production servers. All you have to do is to create a SQL script via Update-Database with a -Script flag:
PM>Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract
Because of the -Script switch, Code First Migrations will run the migration pipeline but instead of actually applying the changes it will write them out to a .sql file for you. Neat.

Complete write up from Microsoft is here.

If you are doing this for the first time, and you need all the changes so far, you just need to do this:
PM>Update-Database -Script -SourceMigration: $InitialDatabase
Here's a sample PM console session:
PM> Update-Database -Script -SourceMigration: $InitialDatabase
Applying code-based migrations: [201301240307501_Initial, 201302020143144_ModifiedEmailTableAndAddedSignatureTable, 201302020149599_AddedCreatorToSignatureRelation, 201302020158207_ChangeSignatureIdToNullable, 201302050122407_AddResultingImageFileToSignatureTable, 201302050154113_AddLabelToSignatureTable, 201302052134222_ChangedAddressToHave3LineItems, 201302090226121_ChangedSignatureFileToRequired, 201302121909589_AddedSalesItemClass, 201302192149411_AddedCustomerClass, 201302210141465_AddedLocationInIamProfileClass, 201302260305398_UpdatedCertificateClass].
Applying code-based migration: 201301240307501_Initial.
Applying code-based migration: 201302020143144_ModifiedEmailTableAndAddedSignatureTable.
Applying code-based migration: 201302020149599_AddedCreatorToSignatureRelation.
Applying code-based migration: 201302020158207_ChangeSignatureIdToNullable.
Applying code-based migration: 201302050122407_AddResultingImageFileToSignatureTable.
Applying code-based migration: 201302050154113_AddLabelToSignatureTable.
Applying code-based migration: 201302052134222_ChangedAddressToHave3LineItems.
Applying code-based migration: 201302090226121_ChangedSignatureFileToRequired.
Applying code-based migration: 201302121909589_AddedSalesItemClass.
Applying code-based migration: 201302192149411_AddedCustomerClass.
Applying code-based migration: 201302210141465_AddedLocationInIamProfileClass.
Applying code-based migration: 201302260305398_UpdatedCertificateClass.
Once the script is generated, Visual Studio opens the script for you to save. What I do is just simply copy and paste the resulting sql script to SQL Server Management Studio, then execute it there against the database. Done!

There's a little hickup:
I got this error message, which means that the script generator is unable to remove duplicate variable declarations in the sql. The solution is to simply delete such duplicate declarations.

Msg 134, Level 15, State 1, Line 101 The variable name '@var0' has already been declared. Variable names must be unique within a query batch or stored procedure.

How To Setup SecurityGuard.MVC4 And MvcInstaller.MVC4 For ASP.NET MVC 4 Application

I love this open source project called SecurityGuard.  It is a complete ASP.NET Membership management system plugin for ASP.NET MVC 4 application.

To install via NuGet:
PM> Install-Package SecurityGuard.MVC4
To make it easier to create and setup the Membership database tables and roles, I recommend to use another Nuget package called MvcInstaller.MVC4:
PM> Install-Package MvcInstaller.MVC4
Follow these steps to setup SecurityGuard.
Follow these steps to setup MvcInstaller.

For the MvcInstaller, the following requirements should be met prior to running the installer for the first time:

1. Create the database, and make sure to create the db login account to be used in accessing this database.
2. Modify the installer.config file, example is as follows:
<installerconfig>
  <applicationname>ProductCoC</applicationname>
  <path>
    <relativesqlpath>App_Data</relativesqlpath>
  </path>
  <membership create="true" providername="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
  <profile providername="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
  <rolemanager providername="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <roles>
      <role name="Administrator">
        <users>
          <user email="sammydc@mail.com" password="ProductCoC" secretanswer="Green" secretquestion="Favorite Color" username="WBI\sammydc">
        </user></users>
      </role>    
      <role name="SecurityGuard">
        <users>
          <user email="sammydc@mail.com" password="ProductCoC" secretanswer="Green" secretquestion="Favorite Color" username="WBI\sammydc">
        </user></users>
      </role>
    </roles>
  </rolemanager>
  <sessionstate providername="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
  <database usetrustedconnection="false">
    <connectionstringname>MembershipConnection</connectionstringname>
    <datasource>MSSQ4\Instance1</datasource>
    <initialcatalog>ProductCoC</initialcatalog>
    <username>ProductCoC</username>
    <password>ProductCoC</password>
  </database>
</sessionstate></profile></membership></installerconfig>
3. Make sure that the AppInstalled setting in web.config is set to false
<appsettings>
 <add key="AppInstalled" value="false" />
</appsettings>
4. Now deploy the MVC 4 application. After deployment, from the browser, navigate to the install path, you should see this on the browser:



5. Make sure to select Use SecurityGuard, then click Install.

6. After a few seconds, it should display a message that the installer completed successfully.

7. Check the database to make sure that the tables and users have been created, in SQL Server Management Studio, it should look like this:



8. Finally, go to /SecurityGuard/Membership/Index, this is what you should see:



Success! Now we have an instant membership management for our app.

Issues encountered during implementation and deployment:

1. When I deployed this to the production server, I got an error like below because the installer.config and web.config are inaccessible:



To overcome this issue, I needed to temporarily add, the Everyone group in windows to have modify access to these files. Make sure to change back the permissions right after the setup.

2. Another issue that I encountered is that because the application is intended for the intranet and I'm using windows integrated security, there is a problem when using an account preceded by a domain name such as DOM1\sammydc, the backslash causes some issues with the UI and routing logic. I will write up the solution to this problem on my next post.

3.07.2013

Syntax Highlighter for Blogger using Prettyfy

Adding Prettify Syntax Highlighter to any CMS or webpage is very easy. For Blogger you just need to go to your blog’s Template tab, click “Edit HTML” button and add the below script before </head> tag.
<script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js">
As you can see that the project is hosted on Google Code. This is an auto-loader which will automatically load the required JavaScript API and the CSS file from the subfolders of SVN. So the last thing that you are left is to mention where it should highlight the codes by adding class="prettyprint" to pre and/or code tags within Blogger Post Editor, HTML view. Here is an example:
<pre class="prettyprint">
$(function() {
 alert('I am using Prettify as Syntax Highlighter');
});
</pre>
There are also some styles available that you can call and load the stylesheet in one URL, here is an example:
<script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js?skin=sunburst"></script>