Brian Mains

Catch me on linked in at: http://linkedin.com/in/brianmains, or follow me on twitter at: @brianmains.

November 2009 - Posts

Location Formats in ASP.NET MVC

ASP.NET MVC is a wonderful thing.  One of the many great features is the ability to customize all of the .NET framework's code by swapping out one implementation and using another.  One such instance is creating a custom view engine, which you can do as illustrated in this example: http://www.singingeels.com/Articles/Creating_a_Custom_View_Engine_in_ASPNET_MVC.aspx.  The point of my article is not to illustrate how this can be done, but about how to customize it for your needs.  By default, the web forms view engine looks for views in the folder of the controller or the shared folder.  So if you try to trigger an action method "Index" within the controller of type CustomerController, a partial view (.ascx) or the view (.aspx) is sought for in the ~/Shared folder or ~/Customer folder.

Now, I tend to like to use partial views in order to separate and reuse functionality a lot.  So I tend to have a lot of partial views that tend to get reused across pages and I don't want everything to be in the shared folder (by default, partial views have to be in the shared folder or in the same folder as the controller).  So I added some code to the view engine that allowed me to create subfolders within the shared folder and for the view engine to look for the classes there.  Imagine this folder structure:

Shared
    Customers
    Orders
    Products

So the shared folder breaks up my partial views into the folder above. 

Thinking long-term, rather than hard-coding all these folder references and assigning them to the ViewLocationFormats and PartialViewLocationFormats properties, I wanted something that I wouldn't have to worry about changing later.  So in true ASP.NET MVC framework form, I created some extra code to create the ability to automatically add references to subfolders too.  In order to do this, it's required to use the VirtualPathProvider class to extract the URL, as in the following code:

public MyViewEngine() {
var locations = new List<string>
{
    "~/Views/{1}/{0}.aspx",
    "~/Views/{1}/{0}.ascx",
    "~/Views/Shared/{0}.aspx",
    "~/Views/Shared/{0}.ascx"
};

var dir = this.VirtualPathProvider.GetDirectory("~/Views/Shared");
var subs = dir.Directories.OfType<VirtualDirectory>();

foreach (var sub in subs)
{
    locations.Add("~" + sub.VirtualPath.Substring(sub.VirtualPath.IndexOf("/", 2)) + "{0}.ascx");
}

base.ViewLocationFormats = locations.ToArray();
base.PartialViewLocationFormats = base.ViewLocationFormats;
}

This is the constructor for the custom view engine.  It contains some additional code to use the VirtualPathProvider property (a property of our custom view engine) to extract the subdirectories of the shared folder.  You see the four hard-coded references at the beginning, and so we need to create virtual path strings (which start with "~" and work from the beginning of the virtual directory) to add to the custom list.  When working with folders using VirtualPathProvider, the issue becomes the way paths are referenced.  By default, the path may be:

/MyVirtualFolder/Views/Shared/Customers/

When you need:

~/Views/Shared/Customers/

And so some additional work to format the path is needed (the substring strips off the virtual directory folder.  Now we have a component that will allow the MVC framework to look for partial views in all subdirectories in the shared folder.

Posted: Mon, Nov 16 2009 19:49 by bmains | with no comments
Filed under: ,
Telerik's Compressing and Combining Scripts in MVC

The latest Telerik MVC set of components features a ScriptRegistrar component that's responsible for compressing or combining scripts into a single file.  This compression utility is very easy to implement: to setup this component, use the following steps:

1. In your configuration file, add the following line to the httpHandlers element in <system.web> and <system.webserver>:

<add verb="GET,HEAD" path="asset.axd" validate="false" type="Telerik.Web.Mvc.WebAssetHttpHandler, Telerik.Web.Mvc"/>

2. Use the ScriptRegistrar and groups to group scripts together.  Preferrably, put this in the master page at the very end of the page before the ending </body> tag.

<% Html.Telerik().ScriptRegistrar().Scripts((s) =>
{
    s.AddGroup("MyGroup", (g) =>
    {
         g.Add("script1.js").Add("script2.js").Add("script3.js");
    }
}).Render(); %>

Now we have our scripts defined, but if you run the app, you will be disappointed by finding out three script files were added.  Instead, we need to add some additional properties to the group definition.

s.AddGroup("MyGroup", (g) =>
{
     g.Add("script1.js").Add("script2.js").Add("script3.js").Combined(true).Compress(true).CacheDurationInDays(10);
}

The Combined() method signals to combine all three scripts together within this group  The Compress() method signals to compress the scripts, and the CacheDurationInDays method specifies the number of days to cache the scripts.

This can also be performed by stylesheets, without the ability to combine and compress them.  You can group them together using the same approach, except using the StyleSheets method.

This setup produces the following output.

<script type="text/javascript" src="/asset.axd?id=mwAAAB-LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee--997o7nU4n99__P1xmZAFs9s5K2smeIYCqyB8_fnwfPyJ-8UfT9qNHH2WrVVlMs7aolnffbf90dpk107pYtR-NPrqkr_d2dh6O7413d3fujXfos-lHj9p6ndMvs48e3fv0_ugj-vm9X_zRitqeVMs2X7Z3XzOAhlqf83fVR492Rh8tqcVFXa1Xu-Ofbj76JSP-fNf7fI8___4v-f4v-X8AJmOKf5sAAAA%3d"></script>

 

<link type="text/css" href="/asset.axd?id=kwAAAB-LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee--997o7nU4n99__P1xmZAFs9s5K2smeIYCqyB8_fnwfPyJ-8UfT9qNHH7X5u_butGk-Gn10SX_u7ew8HN8b7-7u3Bvv0GfTjx619TqnX2YfPbr36f3RR_Tze7_4oxW1PamWbb5s757w2-f8efXRo53RR0v69nXR5mMA_iUj_nhXPm7zMq-Lt-Pq_LyY5tTdA2n0_V_y_V_y_wCso4bJkwAAAA%3d%3d" rel="stylesheet"/>

The other option for adding scripts is to use the DefaultGroup method, a method that uses the default group to setup scripts for.  For instance, we setup a custom group, but if you only need one group, we can use the DefaultGroup method to define all our scripts in.

Posted: Tue, Nov 10 2009 20:50 by bmains | with no comments
Filed under: ,
Working with Web Forms and MVC: Bridging the Gap - Context

I've spent some time figuring out how to bridge the gap between web forms and ASP.NET MVC style of development.  Just because the UI works differently, doesn't mean the actual framework works in a different way.  If you use .NET 3.5 SP 1 for both your web forms and MVC style development, it is helpful to come up with a helper component to serve up the context.  Why would I suggest this?  Well, in web forms, you can access the current context information by accessing System.Web.HttpContext.Current, which returns the current HttpContext being executed.  The HttpContext object has an array of services (request, response, etc.) that you can make use of.  This is available in ASP.NET MVC in the controller too, via the ControllerContext.HttpContext reference.

The difference between the two is that the former uses an HttpContext object, while MVC uses an object of type HttpContextBase in System.Web.Abstractions.DLL.  The context is a reference to HttpContextWrapper, which inherits from HttpContextBase, and provides you with all of the services previously mentioned.  So basically, you are working with the same thing, but not the same object.  But if you use 3.5 for all of your development, you can use the same object to leverage both, by creating a helper class and always referring to this for your context.

public static class HttpWebContext
{
    public static HttpContextBase GetContext()
    {
         return new HttpContextWrapper(HttpContext.Current);
    }
}

This works in web forms because this is how it works anyway, while MVC uses this in its underlying architecture (if you use a tool like Reflector you can dig down into the code to find it).  And so this common component shares the context across two environments, and its isolated  and follows the Singular Repsonsibility Principle [SRP].  Now what about testability, because your internal components will be using this, and the http context won't be available in a testing environment (since it's web framework specific).  For instance, your controller may do something like:

public ActionResult View()
{
    var context = HttpWebContext.GetContext();
    //use the context in some way

   return View();
}

So how can that type of method be mocked?  If you use TypeMock, this can be done easily:

var ctx = Isolate.Fake.Instance<HttpContextBase>();
var requestFake = Isolate.Fake.Instance<HttpRequestFake>();
Isolate.WhenCalled(() => ctx.Request).WillReturn(requestFake);
//Additional faking here

Isolate.WhenCalled(() => HttpWebContext.GetContext()).WillReturn(ctx);

For Moq, this is harder because it doesn't mock statics; so you could make it an instance-based approach, or you could use a provider approach.  For instance, create a provider that has:

public abstract class ContextProvider
{
    public HttpContextBase GetContext();
}

Have two implementations, one for returning the current context like we have above, and have another returning a faked http context that inherits from HttpContextBase, and then store this reference in the configuration  file or pass it in as a parameter to a class. You could get kind of crazy with it, but by using the provider approach, it helps you differentiate the web environment and test environment without exceptions occurring.

Would this approach work in .NET 2.0?  Well, the great feature about 3.5 is the System.Web.Abstractions, which contains the HttpContextBase class that handles this for you.  So if you can use 3.5, you can leverage this API for your needs.  Otherwise, you would have to create your own wrapper, which is possible but tedious (since it's such a big class).

Posted: Sat, Nov 7 2009 11:02 by bmains | with 2 comment(s)
Filed under: ,
IServices service loader approach to loading services

This is a continuation of a blog series on creating services.  You can find the previous articles here:

So the ApplicationContext object uses a service loader as one way to load the IService objects into it.  This class exists solely to load these services into the context.  This object is then linked into the Application context through the configuration file.  The below example is used for loading MVC services by default (you can inherit, override this method, and add additional ones you use as a way to make it easier for loading the services you need).  This object already exists in the Nucleo.Web.Mvc DLL, in the Nucleo.Web.Context namespace.

public class MvcApplicationContextServiceLoader : IApplicationContextServiceLoader
{
 #region " Methods "

 public virtual void LoadServices(IApplicationContextProvider provider)
 {
  provider.RegisterService<IApplicationStateService>(new MvcApplicationStateService());
  provider.RegisterService<IBrowserCapabilitiesService>(new WebFormsBrowserCapabilitiesService());
  provider.RegisterService<ICookieService>(new MvcCookieService());
  provider.RegisterService<INavigationService>(new MvcNavigationService());
  provider.RegisterService<IPostDataService>(new MvcPostDataService());
  provider.RegisterService<ISessionStateService>(new MvcSessionStateService());
  provider.RegisterService<IServerUtilityService>(new MvcServerUtilityService());
  provider.RegisterService<IUrlResolutionService>(new MvcUrlResolutionService());
 }

 #endregion
}

The LoadServices method is where the action happens.  The application context provider is used to receive these services and supply them to the ApplicationContext.  You don't actually have to worry about that; the service provider is used by the context to serve up these services.  This class is then linked up in the configuration file.  See the following example for the setup:

 <configSections>
 <nucleo>
  <section name="contextSettings" type="Nucleo.Context.Configuration.ContextSettingsSection,Nucleo" />
 </nucleo>
</configSections>


<nucleo>
 <contextSettings contextLoaderType="Nucleo.SampleClasses.Context.SampleApplicationLoader,Nucleo.OnlineTests" />
</nucleo>

The context settings section is the configuration object for specifying the loader you would like to use.  At runtime, the ApplicationContext object uses this to load the relevant services, so when you do:

context.GetService<ICookieService>();

A valid service will be returned to you.  We'll talk more later about the purpose of the context provider later, how that's configured .

 

 

Posted: Wed, Nov 4 2009 20:38 by bmains | with no comments
Filed under: ,
Moq and TypeMock in Comparison

I've been working on a new MVC project and man has it been enlightening!  It's my first MVC project, so I get to go through all the things I thought I knew from reading Pro ASP.NET MVC Framework (Apress by Steve Sanderson) to realizing I don't know squat,and have to really delve into the learning in order to finish the project, a task which I knew I was getting into and greatly accept the challenge.  Anyway, as I've been developing, I've been trying to use Moq to create my unit tests.  It's a free mocking library that enables you to write code that mocks your existing objects.  While most libraries heavily use interfaces for writing mocks, Moq can mock classes that have the methods or properties to override marked as virtual (and there are some other conditions as well; you can see an example on Stephen Walther's blog).  As you can see, Moq is great and is useful for MVC applications, and can be used in web forms in a more limited fashion.

TypeMock, however, offers you a few more conveniences over mocking.  For instance, Moq doesn't support mocking static methods, internal methods, and a few other scenarios.  In fact, Moq is a good approach if you design software using the DI pattern, as is illustrated below:

public class TestClass
{
     public TestClass(IDependency dep) { }
}

This is where a dependency is passed in to the constructor (or it can be via a property), which Moq can setup easily as in the following code:

var depFake = new Mock<IDependency>();
//Setup any mocking here

var tc = new TestClas(depFake.Object); //fake gets inserted here
// tc.Dep = depFake.Object; <-- this is an alternative approach

However, TypeMock allows you to use other approaches as well, which fits closer to my development style.  For instance, suppose you have this type of class that uses the dependency:

public class TestClass
{
    private TestClass() { }

    public static TestClass Create()
    {
         var c = new TestClass();
         c.Dep = DepResolver.Get();

         return c;
    }
}

With Moq, we have no inherent way to tap into this method.  This is where TypeMock comes in.  TypeMock allows us to tap into this method a couple of ways.  First, we could skip the Create method altoghether using:

var tc = Isolate.Fake.Instance<TestClass>();
Isolate.WhenCalled(() => TestClass.Create()).WillReturn(tc);

The first line creates our fake, similar to Moq's "new Mock<TestClass>" syntax.  Rather than returning a proxy, it returns an actual instance of our mocked class.  The second statement provides a way to override the static method and return directly our fake.  This may not be as useful, so let's look at the next way to tap into this method:

var dep = Isolate.Fake.Instance<IDependency>();
Isolate.WhenCalled(() => DepResolver.Get()).WillReturn(dep);

var tc = TestClass.Create();

When this code runs, the TestClass instance is actually created, and that code works as is.  But the code revolving around the IDependency is faked, so that the DepResolver returns our fake and doesn't actually run.  So you can see some of the ways that TypeMock allows developers to tap into their code without forcing a design.  We'll look at other ways this can happen later on.

Note: This doesn't mean Moq is bad by any means; designing your software for testability is a good thing, as it's good to leverage unit tests to ensure accuracy of your code doing what it was supposed to do.  It's just that TypeMock gives you a few other options, and this is one of the additional features that TypeMock provides.