Justin Hoffman

  Home  |   Contact  |   Syndication    |   Login
  9 Posts | 0 Stories | 22 Comments | 0 Trackbacks

News

ASP.net Forms Justin Hoffman

Archives

Wednesday, May 16, 2012 #

Recently I had a need to write some tests making Web Api calls that would be deployable to the build server without any special configuration.What I did was utilize the Self Hosting feature in the Asp.NetWebApi.  First, I created a simple Api controller .

 

// GET /api/values public class ValuesController : ApiController { public SomeObjects GetByName(string firstName, string lastName) { var someObjects= new List<SomeObject>() { new SomeObject { FirstName = "Tom", LastName = "Jones", Id= 1213 }, new SomeObject { FirstName = "Jane", LastName = "Doe", Id= 1523 }, new SomeObject { FirstName = "John", LastName = "Doe", Id= 3123 } }; return someObjects.Where(x => x.FirstName == firstName && x.LastName == lastName).SingleOrDefault(); } }


Next, I created a simple utility class to execute tests against the hosted service

 

public static class TestHostingUtil
    {
        public static void ExecuteHostedTest(Action<Task> continuationAction, string routeTemplate = null)
        {
            var routeTempl = "api/{controller}/{id}";
            if (!string.IsNullOrEmpty(routeTemplate))
            {
                routeTempl = routeTemplate;
            }

            var serverConfig = new HttpSelfHostConfiguration("http://localhost:8080/");
            serverConfig.Routes.MapHttpRoute("default", routeTempl, new { id = RouteParameter.Optional, folder = "TestApiControllers" });

            var server = new HttpSelfHostServer(serverConfig);
            
            server.OpenAsync().ContinueWith(continuationAction).Wait();
            server.CloseAsync();
        }
    }


Note the "folder" param in the HttpRoute registration. In the test project this is the folder under which I added the Api controllers, if they were not in a seperate folder you would leave this blank.

Finally, the usuage for your tests:

[TestMethod]
        public void Should_Perform_Get()
        {
            TestHostingUtil.ExecuteHostedTest(task =>
                                  {

                                      // Arrange
                                      var webUtil=
                                          new WebUtil
                                              <SomeObject, SomeObject>(
                                              "http://localhost:8080/api/values?firstName=Tom&lastName=Jones");

                                      // Act
                                      var result = webUtil.Get();

                                      // Assert
                                      result.FirstName.Should().Be("Tom");
                                  });
        }


Tuesday, March 06, 2012 #

During the upgrade of a solution from the Web Api Preview 6 to MVC 4 Beta Web Api, I encountered a bug with a custom MediaTypeFormatter.  In the Api controller I had a need to take the posted content object as well as some string identifiers/keys as parameters.  The problem I encountered as that if I utilized a custom MediaTypeFormatter, these values came in as Null

Take the following Api controller Post:

       // POST /api/values
        public HttpResponseMessage<SomeObj> Post(string key, string id, SomeObject value)

The string key always came in null.  However, if I switch and used one of the built in formatters XmlMediaTypeFormatter for example, this was not a problem.  It is my understanding that this issue will be fixed post Beta.  I posted my problem on MSDN and did receive a response, you can find it here.

In the meantime, something like the following can be used for a workaround.

Add an Action Filter to handle the parameter binding for you.  Below is an example of such a filter.  It currently would only work with string or int route parameterrs (but can be changed/expanded).

    public class ParameterFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
          if (actionContext.Request.Method != HttpMethod.Get)
            {
                var idParams = actionContext.ControllerContext.Request.GetRouteData();
                foreach (var item in idParams.Values)
                {
                    object value;
                    var result = actionContext.ActionArguments.TryGetValue(item.Key, out value);
                    if (result)
                    {
                        var parm = actionContext.ActionDescriptor.GetParameters().Where(x => x.ParameterName == item.Key).SingleOrDefault();
                        var type = parm.ParameterType;
                        // Check Action parameter type and convert if needed
                        if (type == typeof(int))
                        {
                             actionContext.ActionArguments[item.Key] = Convert.ToInt32(item.Value);
                        }

                        if (type == typeof(string))
                        {
                             actionContext.ActionArguments[item.Key] = item.Value.ToString();
                        }
                    }
                }
            }

            base.OnActionExecuting(actionContext);
            }
    }

Register the Action Filter in the Global.asax

        protected void Application_Start()
        {
            GlobalConfiguration.Configuration.Formatters.Clear();
           
            GlobalConfiguration.Configuration.Filters.Add(new ParameterFilter());
            AreaRegistration.RegisterAllAreas();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
            BundleTable.Bundles.RegisterTemplateBundles();
        }

Tuesday, February 28, 2012 #

After upgrading to the MVC 4 Web Api a problem came up where the custom MediaTypeFormatters we were using were no longer functioning.

You must now override, CanWriteType and CanReadType in your customer formatter, or it will return false by default. 

'Headers.ContentType' must be set before 'ObjectContent' can serialize its content"v

Ideally, some checks would be made here to determine if you truly can read or write the type..but for this example, return true.

Throwing the following error:

        /// <summary>
        /// Determines whether this <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter"/> can serialize an object of the specified type.
        /// </summary>
        /// <param name="type">The type of object that will be serialized.</param>
        /// <returns>
        /// true if this <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter"/> can serialize an object of that type; otherwise false.
        /// </returns>
        protected override bool CanWriteType(Type type)
        {
            return true;
        }

        /// <summary>
        /// Determines whether this <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter"/> can deserialize an object of the specified type.
        /// </summary>
        /// <param name="type">The type of object that will be deserialized.</param>
        /// <returns>
        /// true if this <see cref="T:System.Net.Http.Formatting.MediaTypeFormatter"/> can deserialize an object of that type; otherwise false.
        /// </returns>
        protected override bool CanReadType(Type type)
        {
            return true;
        }

Friday, April 08, 2011 #

Recently, while browsing Visual Studio extensions I came across the SharpKit tool.  It sparked my curiosity as I'm always looking for ways to improve my jQuery and Javascript in general.

Working off the example in my previous post about Updating the Content of a WebGrid async we can generate the same Javascript via the SharpKit project.  First, head to theSharpKit site to download and setup your first project (check out the Screencasts).  Or search "jQuery" in Visual Studio 2010 extension manager.

 So if we take the realodGrid function we can write it in C# with all the great benefits that come with C# coding.

 using  System.Reflection;
 using  SharpKit.JavaScript;
 using  SharpKit.Html4;
 using  SharpKit.jQuery;
 using  SharpKit.jQuery.UI;
 namespace  SharpKitLibrary
 {
     [JsType (JsMode .Global, Filename = "res/scriptFile.js" )]
     public  class  DataMapScripts  : jQueryContext 
     {
         static  void  loadGrid()
         {
             var  settings = new  AjaxSettings 
                                {
                                    url = "/Home/ReloadGrid/" ,
                                    cache = false ,
                                    data = "{}" ,
                                    dataType = "html" ,
                                    success = fillGrid
                                };
             jQuery .ajax(settings);
         }
 
         static  void  fillGrid(object  o, JsString  data, XMLHttpRequest  request)
         {
             J("#grid" ).html(data);
         }
     }
 }
 

The when you Build your SharpKit project, it will output the Javascript file for you.  Here is the result:

 //@AutoGenerated 
 function  loadGrid()
 {
 	var  settings = {url:"/Home/ReloadGrid/" ,
 	cache:false ,
 	data:"{}" ,
 	dataType:"html" ,
 	success:fillGrid};
 	$.ajax(settings);
 }
 function  fillGrid(o, data, request)
 {
 	$("#grid" ).html(data);
 }
 
 

Friday, April 01, 2011 #

Previously, I blogged about how to update the WebGrid in MVC 3 asynchronously.  This was when MVC 3 beta came out.  Later, I received a comment which confirmed that in fact my example no longer worked!!.  Hopefully, I can redeem myself with this post.

I took a slightly different approach in this example, using a partial view and reloading the grid with .ajax.

First, the ViewModel which is simple enough:

 

 using  System;
 using  System.Collections.Generic;
 using  System.Linq;
 using  System.Web;
 
 namespace  AsyncWebGrid.Models
 {
     public  class  ContactViewModel 
     {
         public  IEnumerable <Contact > Contacts { get ; set ; }
     }
 }

 

 
The Contact class:

 

 

 using  System;
 using  System.Collections.Generic;
 using  System.Linq;
 using  System.Web;
 
 namespace  AsyncWebGrid.Models
 {
     public  class  Contact 
     {
         /// <summary> 
         /// Gets or sets the id. 
         /// </summary> 
         /// <value> 
         /// The id. 
         /// </value> 
         public  int  Id { get ; set ; }
 
         /// <summary> 
         /// Gets or sets the first name. 
         /// </summary> 
         /// <value> 
         /// The first name. 
         /// </value> 
         public  string  FirstName { get ; set ; }
 
         /// <summary> 
         /// Gets or sets the last name. 
         /// </summary> 
         /// <value> 
         /// The last name. 
         /// </value> 
         public  string  LastName { get ; set ; }
 
         /// <summary> 
         /// Gets or sets the age. 
         /// </summary> 
         /// <value> 
         /// The age. 
         /// </value> 
         public  int  Age { get ; set ; }
     }
 }
 

The Controller has a couple different methods.  The important one is ReloadGrid.  This returns the partial view which actually has the WebGrid in it.  The ContactRepository is new'd up in the constructor for simplicity, obviously we would want to inject this normally.

 

 using  System;
 using  System.Collections.Generic;
 using  System.Linq;
 using  System.Web;
 using  System.Web.Helpers;
 using  System.Web.Mvc;
 using  AsyncWebGrid.Models;
 using  AsyncWebGrid.Repositories;
 
 namespace  AsyncWebGrid.Controllers
 {
     public  class  HomeController  : Controller 
     {
         private  readonly  IContactRepository  _contactRepository;
 
         public  HomeController()
         {
             _contactRepository = new  ContactRepository ();
         }
 
 
         /// <summary> 
         /// Indexes this instance. 
         /// </summary> 
         /// <returns></returns> 
         public  ActionResult  Index()
         {
            
             var  viewModel = new  ContactViewModel 
                                 {
                                     Contacts = this ._contactRepository.GetContacts(),
                                 };
 
 
             return  View(viewModel);
         }
 
         /// <summary> 
         /// Deletes the person. 
         /// </summary> 
         /// <param name="id">The id.</param> 
         /// <returns></returns> 
         [HttpPost ]
         public  ActionResult  DeletePerson(int  id)
         {
             this ._contactRepository.DeleteContact(id);
             return  null ;
         }
 
         /// <summary> 
         /// Reloads the grid. 
         /// </summary> 
         /// <returns></returns> 
         public  ActionResult  ReloadGrid()
         {
             return  PartialView("_Contacts" , this ._contactRepository.GetContacts());
 
         }
 
     }
 }
 
 

Finally the Views. First Index.cshtml

 

  @model   AsyncWebGrid.Models.ContactViewModel 
 
  @{  
     ViewBag.Title = "Index" ;
  }  
 
 <h2> Index</h2> 
 
  <div  id="grid"> 
          @  Html.Partial("_Contacts" , Model.Contacts)
  </div>
 

The PartialView _Contacts.cshtml, which is where everything happens along with the javascript function for reloading the grid:

  @model   IEnumerable <AsyncWebGrid.Models.Contact >
 
  @{          
     
     var  grid = new  WebGrid (source: Model,
                                     defaultSort: "FirstName" , 
                                     ajaxUpdateContainerId: "grid" ,
                                     rowsPerPage: 15);
   }  
 
            @  grid.GetHtml(
                   tableStyle: "grid" ,
                   headerStyle: "head" ,
                    alternatingRowStyle: "alt" ,
                   columns: grid.Columns(
 
                                             grid.Column(format: (item) => Ajax.ActionLink("Delete" , "DeletePerson" ,
                                                                         new  { id = item.Id },
                                                                         new  AjaxOptions  { HttpMethod = "POST" , OnSuccess= "reloadGrid" })),
                                                                         
                            grid.Column("Id" ),
                            grid.Column("FirstName" ),
                            grid.Column("LastName" ),
                            grid.Column("Age" )
                   )
           )
 

JS:

 function  reloadGrid() {
 
     $.ajax(
         { type: "GET" ,
             url: '/Home/ReloadGrid/' ,
             data: "{}" ,
             cache: false ,
             dataType: "html" ,
             success: function  (data)
             { $( ).html(data); } 
         })
 }
 

In my first attempts to correct this post one issue I kept encountering was that jquery.load would only reload the grid the first time it was called.  Each time after that nothing would happen.  I did some searching and came across this blog which helped me out with an alternative.


Friday, March 04, 2011 #

Using the HttpClient API from wcf.codeplex.com, you may encounter this error if respones are too large.  

Cannot write more bytes to the buffer than the configured maximum buffer size: 65536

In order to increase the size of the Response Buffer, just increase the MaxReseponseContentBufferSize as shown below. Increase it to something larger than the default: 65536 depending on your response sizes.

     
var client = new HttpClient
{
MaxResponseContentBufferSize = 196608,
BaseAddress = new Uri("http://myservice/service1/")

};


Wednesday, March 02, 2011 #

I wanted to try to use T4 to read a web.config and generate all of the appSettings and connectionStrings as properties of a class.  I elected in this template only to output appSettings and connectionStrings but you can see it would be easily adapted for app specific settings, bindings etc.  This allows for quick access to config values as well as removing the potential for typo's when accessing values from the ConfigurationManager. One caveat: a developer would need to remember to run the .tt file after adding an entry to the web.config.  However, one would quickly notice when trying to access the property from the generated class (it wouldn't be there).  Additionally, there are other options as noted here.

The first step was to create the .tt file.  Note that this is a basic example, it could be extended even further I'm sure.  In this example I just manually input the path to the web.config file.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly Name="System.Configuration" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="System.Xml.Linq" #>
<#@ assembly name="System.Net" #>
<#@ assembly name="System" #>
<#@ import namespace="System.Configuration" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Net" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="System.Xml.Linq" #>



using System;
using System.Configuration;
using System.Xml;
using System.Xml.Linq;
using System.Linq;


namespace MyProject.Web
{
public partial class Configurator
{

<#
var xDocument = XDocument.Load(@"G:\MySolution\MyProject\Web.config");
var results = xDocument.Descendants("appSettings");
const string key = "key";
const string name = "name";
foreach (var xElement in results.Descendants())
{#>

public string <#= xElement.Attribute(key).Value#>{get {return ConfigurationManager.AppSettings[<#= string.Format("{0}{1}{2}","\"" , xElement.Attribute(key).Value, "\"")#>];}}

<#}#>

<#
var connectionStrings = xDocument.Descendants("connectionStrings");
foreach(var connString in connectionStrings.Descendants())
{#>
public string <#= connString.Attribute(name).Value#>{get {return ConfigurationManager.ConnectionStrings[<#= string.Format("{0}{1}{2}","\"" , connString.Attribute(name).Value, "\"")#>].ConnectionString;}}
<#} #>

}


}

The resulting .cs file:

using System;
using System.Configuration;
using System.Xml;
using System.Xml.Linq;
using System.Linq;


namespace MyProject.Web
{
public partial class Configurator
{


public string ClientValidationEnabled{get {return ConfigurationManager.AppSettings["ClientValidationEnabled"];}}


public string UnobtrusiveJavaScriptEnabled{get {return ConfigurationManager.AppSettings["UnobtrusiveJavaScriptEnabled"];}}


public string ServiceUri{get {return ConfigurationManager.AppSettings["ServiceUri"];}}


public string TestConnection{get {return ConfigurationManager.ConnectionStrings["TestConnection"].ConnectionString;}}
public string SecondTestConnection{get {return ConfigurationManager.ConnectionStrings["SecondTestConnection"].ConnectionString;}}

}


}

Next, I extended the partial class for easy access to the Configuration. However, you could just use the generated class file itself.

using System;
using System.Linq;
using System.Xml.Linq;

namespace MyProject.Web
{
public partial class Configurator
{
private static readonly Configurator Instance = new Configurator();

public static Configurator For { get { return Instance; } }

}
}

Finally, in my example, I used the Configurator class like so:

        [TestMethod]
public void Test_Web_Config()
{
var result = Configurator.For.ServiceUri;
Assert.AreEqual(result, "http://localhost:30237/Service1/");
}

 


Tuesday, November 16, 2010 #

I spent the better part of a day trying to figure out why all of the sudden, I was unable to properly debug, add quick watches or see the values of fields etc at all.  I found it was because I had installed ASP.NET MVC3 along side AsyncCTP. 

I found instructions to resolve the issue here http://bartwullems.blogspot.com/2010/11/dont-install-async-ctp-and-aspnet-mvc-3.html


Friday, October 29, 2010 #

Recently, I was tasked with customizing a Build work flow to allow for configurable assignment of build bugs.   This is not a difficult task, however locating the proper Activity in the workflow can be a bit of an annoyance. 

There are two things which need to be done in this case.

  1. Open your Build Process Template
  2. Add an Direction = In, Type =String.  Enter a default value if you like

As show here:

This will handle the configurable portion of this request.  In the future, when this build process template is used, a Miscellaneous argument will show up for input in the Build Definiton.

Now, to get to the Acitivty which Creates the Work Item on build failure, out of the box the path should be as follows:

Run on Agent>Try Compile, Test>Compile, Test and Associate Changesets and WorkITems>Try Compile and Test>Compile and TEst>For Each Configuraiton in BuildSettings.PlatformConfigurations>Compile and Test for Configuration>If BuildSettings.HasProjectsToBuild>For Each Project in BuildsSettings.ProjectsToBuild>Try to Compile the Project>

Go into the Catch>HandleException>If CreateWorkItem> then all the way down to CreateWorkItem

On the CreateWorkItem activity, set the AssignedTo field to the argument you created earlier.