Tuesday, December 09, 2014

Things I've learned (forcibly) on 2014-12-09


  1. There are database systems that are actually implemented using the Julian (sort of) calendar. Why ?!
  2. It is perfectly legitimate to use script types other than type="text/javascript" in a

Labels: , , , ,

Saturday, December 06, 2014

Adding less to your MVC 5 project

I'm going to have to just redirect to this post, because I'm in a rush. Hopefully it's still around when you read this.

Note: In order to get this to work, I had my application pool running in 'Integrated Pipeline Mode' and IIS 8 didn't like that, so I had to get rid of the system.web/httpHandlers sections for the dotLess configuration.

Labels: , , , ,

Wednesday, November 26, 2014

Creating Azure Services with Visual Studio 2013 and Windows 8.1


  1. Download and install Visual Studio 2013
  2. Download and install the Web Platform Installer, v5.0 or greater
  3. From the Web Platform Installer, install:
    1. Windows Azure SDK and related Powershell utilities and command line tools
  4. Start a new solution (or open an existing one)
  5. From the NuGet package manager in your solution, ensure that you've installed the 'WindowsAzure.Storage' package, or at least have it in your cache. This is going to be required by the New Project wizard when generating the project.
  6. Add a new project
  7. In the 'Add new project' wizard, select the C# projects -> Cloud -> Windows Azure Cloud Service
  8. Follow this absurdly easy tutorial on implementing an ErrorHandler interceptor (behavior)
  9. Add logging to your application using the Microsoft Patterns & Practices Enterprise Library Logging Application Block (which can be found here). 
  10. Create a SQL server database in Azure using this tutorial on MSDN. When designing the user roles and authentication, it's recommended that you use the ASP.NET Identity membership design and design your tables accordingly around this.
    1. Microsoft has provided an extension to the ASP.NET Identity membership framework specifically for EntityFramework. They recommend that you use a code-first model for generating entities.
    2. Log in to Azure through the management portal: http://manage.windowsazure.com/
    3. Configure all of your connections and permissions to the database. e.g. you may want to have multiple users: one for read-only operations, another for read-write operations.
    4. Generate or write your data model. This article will show you how to create a code-first data model with Entity Framework. Ensure that you include users so that you can support proper application authentication and authorization via the ASP.NET Identity membership framework.
    5. The link above also includes instructions on using code-first migrations for when you update your data model.
    6. TODO: Elaborate on how to properly set up the database when performing a code-first database design
  11. Add an IoC container to your WCF service to enable you to easily develop and unit test it. I've chosen to use Ninject because despite a few minutes of inital frustration, it's actually exceedingly easy to integrate with WCF, especially now that there's the Ninject WCF extensions NuGet package. To integration Ninject with your WCF service, perform the following steps:
    1. Install-Package Ninject.Extensions.Wcf -Version 3.2.1.0 (Ninject and Ninject.Web.Common are installed as dependencies for you automatically).
    2. Create a NinjectModule descendant to bind your interfaces to concrete implementations. It should look something like the following:
      namespace MyService.CloudStorage.Support
      {
          using System.Diagnostics.CodeAnalysis;
          using System.ServiceModel;
          using Common.Interfaces;
          using global::Ninject.Modules;
          using global::Ninject.Syntax;
          using Services;
          using NinjectServiceHost = global::Ninject.Extensions.Wcf.NinjectServiceHost;
      
          /// <summary>
          /// A <see cref="NinjectModule"/> descendant for bootstrapping our application
          /// </summary>
          public class MyServiceCloudStorageNinjectModule : NinjectModule
          {
              /// <inheritdoc/>
              [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1604:ElementDocumentationMustHaveSummary", Justification = "InheritDoc")]
              public override void Load()
              {
                  this.Bind<IResolutionRoot>().ToConstant(Kernel);
                  this.Bind<ServiceHost>().To<NinjectServiceHost>();
                  this.Bind<IMyServiceStorage>().To<MyServiceStorage>();
                  this.Bind<ILoggingService>().To<MicrosoftEnterpriseLoggingBlockLoggingService>();
              }
          }
      }
      
    3. Create a Global Application Class (global.asax) if one's not already created: Right-click on your project -> Add -> New Item ... and in the window that comes up, go to Visual C# -> Web -> Global Application File
    4. Update your Global class to extend from Ninject.Web.Common.NinjectHttpApplication and override the CreateKernel() method and return a new CustomNinjectModule (as created above). The method should look something like this:
      namespace MyService.CloudStorage
      {
          using System.Diagnostics.CodeAnalysis;
          using System.Web;
          using Ninject;
          using Ninject.Web.Common;
          using Support;
      
          /// <summary>
          /// A global <see cref="HttpApplication"/> class for managing the lifecycle
          /// of the application
          /// </summary>
          public class Global : NinjectHttpApplication
          {
              /// <inheritdoc/>
              [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1604:ElementDocumentationMustHaveSummary", Justification = "InheritDoc")]
              [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1615:ElementReturnValueMustBeDocumented", Justification = "InheritDoc")]
              protected override IKernel CreateKernel()
              {
                  return new StandardKernel(new NinjectSettings(), new MyServiceCloudStorageNinjectModule());
              }
       }
      }
      
    5. Update your .svc file for your service(s) and add an extra XML attribute that looks like this: 
  12. Implement your business logic
    1. If you're designing your service properly, you'll need to ensure that communications between clients and your service are secure. Toward that end, you'll need to use SSL to encrypt your communications. There's a number of things involved in this:
      1. Generate certificates for your server and your client
      2. Ensure that your IIS server is correctly configured to use 'https' bindings on your site, along with the server-side certificate that you've generated.
      3. If you're using Windows Store Apps to access a WCF service, you'll need to ensure that you use a CustomBinding correctly configured with the right *BindingElement objects to create an SSL secured, HTTPS-transported binding.
        • For the server, in your service's concrete implementation, you'll need to remove any .config file configuration, and add a method specified according to a convention that WCF recognizes that looks like the following:
          /// <summary>
          /// The service certificate store name
          /// </summary>
          private const StoreName ServiceCertificateStoreName = StoreName.My;
          
          /// <summary>
          /// The service certificate store location
          /// </summary>
          private const StoreLocation ServiceCertificateStoreLocation = StoreLocation.LocalMachine;
          
          /// <summary>
          /// Configures the specified configuration.
          /// </summary>
          /// <param name="config">The configuration.</param>
          /// <remarks>
          /// A service endpoint configuration method determined by convention.
          /// <see href="http://msdn.microsoft.com/en-us/library/hh205277(v=vs.110).aspx"/>
          /// </remarks>
          public static void Configure(ServiceConfiguration config)
          {
           ServiceEndpoint serviceEndpoint = new ServiceEndpoint(
            ContractDescription.GetContract(typeof(IMyServiceStorage), typeof(MyServiceStorage)), 
            new CustomBinding(
             new TransportSecurityBindingElement(),
             new SslStreamSecurityBindingElement { RequireClientCertificate = false },
             new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, Encoding.UTF8),
             new HttpsTransportBindingElement()
            ), 
            new EndpointAddress("https://localhost/MyService.CloudStorage/MyServiceStorage.svc")
           );
          
           config.AddServiceEndpoint(serviceEndpoint);
          
           const string ServiceCertificateThumbprint = "[a 40 digit hexadecimal certificate thumbprint here]";
          
           X509Store certificateStore = new X509Store(ServiceCertificateStoreName, ServiceCertificateStoreLocation);
          
           certificateStore.Open(OpenFlags.ReadOnly);
          
           X509Certificate2Collection x509Certificate2Collection = certificateStore.Certificates.Find(
            findType: X509FindType.FindByThumbprint,
            findValue: ServiceCertificateThumbprint,
            validOnly: false
            );
          
           certificateStore.Close();
          
           X509Certificate2 serviceCertificate = x509Certificate2Collection.Cast<X509Certificate2>().FirstOrDefault();
          
           if (serviceCertificate == null)
           {
            throw new ConfigurationErrorsException(String.Format("No certificate representing the service with thumbprint {0} could be found in {1} store at {2} location", ServiceCertificateThumbprint, ServiceCertificateStoreName, ServiceCertificateStoreLocation));
           }
          
           ServiceCredentials serviceCredentials = new ServiceCredentials
           {
            IdentityConfiguration = new IdentityConfiguration
            {
             // TODO: Change this to authenticate the clients
             CertificateValidationMode = X509CertificateValidationMode.None
            },
            ServiceCertificate =
            {
             Certificate = serviceCertificate
            }/*,
            ClientCertificate =
            {
             // TODO: Resolve the client certificate
             Certificate = serviceCertificate
            }*/
           };
          
           config.Description.Behaviors.Add(serviceCredentials);
           // config.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true, HttpsGetEnabled = true });
          }
          

        • For the Windows Store App client, you'll need to have a similarly configured counterpart channel factory and binding for connecting to the service:
          this.channelFactory = new ChannelFactory<IMyServiceStorageChannel>(
           binding: new CustomBinding(
            new TransportSecurityBindingElement(),
            new SslStreamSecurityBindingElement(),
            new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, Encoding.UTF8),
            new HttpsTransportBindingElement()
           ),
           remoteAddress: new EndpointAddress(serviceUri)
          );
          
          this.passwordVault = new PasswordVault();
          
          // This IDispatchMessageInspector is a custom addition for our own brand of authentication
          this.channelFactory.Endpoint.EndpointBehaviors.Add(new ClientAuthenticationDispatchMessageInspector(this.passwordVault));
          
        • For the Windows Store App, you'll also need to have the server's public key (assuming it's not trusted, e.g. a self-signed certificate) added to the app's certificate declarations in the package manifest. There's a video on how to do it here on Channel 9. For the sake of convenience, I'll reproduce the steps here:
          1. Obtain your server's public key in DER-encoded .cer format.
          2. Open the Package.appxmanifest file for your App in Visual Studio
          3. Go to the Declarations tab.
          4. Under the 'Available Declarations' box, select 'Certificates' and click 'Add' if there's no Certificates declaration already added.
          5. Select the 'Certificates' declaration in the 'Supported Declarations' box.
          6. In the 'Certificates' group on the large pane, click 'Add New'.
          7. In the certificate parameters box that comes up, enter 'Root' in the 'Name' field, and select your public key file. Once you do this, Visual Studio will automatically import it into your project.
          8. Save the manifest.
  13. Publish it to Azure Services
    1. If you've designed your application correctly, you'll be using HTTPS for communicating with your clients. Read Microsoft's guide on MSDN to uploading a certificate with your service.
  14. Implement your Windows 8.1 client // TODO: Elaborate on this
  15. Unit test your Windows 8.1 client on your own local machine.
    1. Visual Studio 2012 / .NET 4.5 added the ability to do asynchronous unit tests to MSTest!
    2. In order to unit test the Windows 8.1 Metro client against services on your localhost, you'll have to read this. It describes the new security features in Windows 8(.1) and how to explicitly enable your application to communicate through the network loopback interface. 
    3. You can also read this. Bottom line, you have to ensure that you enable a loopback exemption for your unit tests.
  16. Unit / integration test it against your Azure service
    1. Ensure that you've created a 'Staging' area in your Azure configuration panel and you're not testing against production! Testing against production is extremely poor practice!
  17. Publish your App on the Windows Store if you so choose


Labels: , , , , ,

Tuesday, October 28, 2014

Correcting the default check-in action for associated work items in Team Foundation Server

I've found that it's a giant pain in the ass in TFS when associating work items to checkins because the default assocation action is 'Resolve' rather than 'Associate'. When I'm working on features, I like to do bits of functionality in units and check them in to source control in small batches to make my changes more manageable. Unfortunately, many times I've forgotten to change the association action when associating work items with change sets and it's resolved my issue instead of just associating the work item with the change set. This becomes problematic because it skews my records and metrics for how much time is spent working on a task. Fortunately one can change this in the registry:

HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\[Version Number]\TeamFoundation\SourceControl\Behavior

Quick fix.

Labels: , , , , , ,

Friday, October 24, 2014

Regarding pre-test invocation scripts when deploying to a Test Agent with TFS

So, I learned something interesting about TFS Test Agents today and they way they handle pre-test invocation scripts when running tests on a Test Agent. In Microsoft Test Manager, do the following:

  1. Connect to a Team Project
  2. Change into 'Lab Center' mode
  3. Click on 'Test Settings' in the top bar-ish area. This will open up the Test Settings Manager.
  4. Edit or create a new 'Test Settings' item and open it up.
  5. In the 'test settings' editor, under the 'Steps' column on the left-hand side, go to 'Advanced' -> 'Scripts'. You're now presented with the scripts page where you can specify scripts to be invoked before and after the execution of your test run.
Now, **here's the important thing** :
The script file(s) you specify in these boxes do not get copied to the Test Agent per se. Instead, their contents get read and merged with an automatically generated script that's created by the Test Agent. The script that actually gets run on the Test Agent will look similar to the following:

REM **************************************************************************** 

REM *  Generated by Microsoft Visual Studio 

REM *  Copyright (c) Microsoft Corporation. All rights reserved. 

REM *   

REM **************************************************************************** 

set ResultsDirectory=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1\Results 

set DeploymentDirectory=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1\DEPLOY~1 

set TestRunDirectory=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1 

set TestRunResultsDirectory=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1\Results\00C4EF~1 

set TotalAgents=1 

set AgentWeighting=100 

set AgentLoadDistributor=Microsoft.VisualStudio.TestTools.Execution.AgentLoadDistributor 

set AgentId=1 

set TestDir=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1 

set BuildDirectory=\\SOMESERVER\SomeShare\TFSDrops\HRST\CWSTAM~1.SPR\CWSTAM~3.1 

set DataCollectionEnvironmentContext=Microsoft.VisualStudio.TestTools.Execution.DataCollectionEnvironmentContext 

set TestLogsDir=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1\Results\00C4EF~1 

set ControllerName=TSTTFSTHR01:6901 

set TestDeploymentDir=C:\Users\Autotest\AppData\Local\VSEQT\QTAgent\23620\00C4EF~1\DEPLOY~1 

set AgentName=00C4EFD8-A65F-4B5E-AD5F-04F93895B543 

REM **************************************************************************** 

REM *  User Commands 

REM *   

REM **************************************************************************** 

echo "My actual user commands from my script file here"


This is important to keep in mind when you're writing the commands in the script to be executed, because no files get copied with the script file you reference in the Test Settings, and there's no mention of where that script file originally came from, so the script is effectively executed without any context except for that which is given to it by the TFS Test Agent in the prefixed lines.

Labels: , , , , , , , , ,

Sunday, October 19, 2014

A huge annoyance in Windows 8 store apps

Apparently Bindings are no longer TwoWay by default as they were in WPF. They're now OneWay, which was a huge annoyance and wasted a solid half hour of my time trying to debug my bindings.

Labels: , , ,

Monday, September 01, 2014

Creating a WPF app with Microsoft Prism Framework 5

To get started, do the following:

Labels: , , , , , ,