Monday, December 31, 2012

CookieContainer for HttpWebRequest

Happy New Year!

Are you making a series of HttpWebRequests where you need to persist cookies? For example, perhaps you are experiencing an infinite series of authentication hops. Don't worry, this is very easy to resolve by using the System.Net.CookieContainer class.

xUnit Example

[Theory]
[InlineData("http://www.yahoo.com", 0)]
[InlineData("http://www.google.com", 2)]
[InlineData("http://www.bing.com", 8)]
public void CookieJarCount(string requestUriString, int expectedCookieCount)
{
    var request = (HttpWebRequest) HttpWebRequest.Create(requestUriString);
 
    // Create a CookieContainer and assign it to the request.
    var cookieJar = new CookieContainer(); 
    request.CookieContainer = cookieJar;
 
    // Make the request like normal.
    var response = (HttpWebResponse) request.GetResponse();
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
 
    // Let's see how many cookies are in the cookie jar!
    Assert.Equal(expectedCookieCount, cookieJar.Count);
}
Shout it

Enjoy,
Tom

Friday, December 28, 2012

Best Emulators for Android

Happy Holidays!

If you were lucky and got an Android tablet or phone this holiday season, congratulations! If you got a bunch of gift cards and you don't know what to do with them, I strongly suggest that you take a look at the Nexus 7! (Although I personally have an ASUS Transformer Prime.)

Best of all, if you like old school video games, you can emulate just about anything made in the 90s on modern Android devices! (Don't know what emulators are? Here is a helpful article about video game emulation for newbies.)

Please remember that emulation is legal, so long as you own the console and game that you are trying to emulate. As always, please play nice and play fair!

The Best Emulators

Platform Emulator Extras Price
SNES Snes9x EX Free
NES Nesoid Free
Game Boy Advance GameBoid Free
Game Boy Color GBCoid Free
Sega Genesis GENPlusDroid Free with Ads
PlayStation FPse for Android Requires a BIOS file $3.63
DOS DosBox Turbo DosBox Manager $3.49

How To Install

You must manually install applications that are not purchased from the Google Play Store. Fortunately this is very easy to do:

  1. Copy the .APK file to your Android device.
  2. Allow installations from "Unknown sources".
    (Settings > Security > Device Administration > Unknown sources)
  3. Navigate to the APK file using a file manager.
    (I prefer ASTRO File Manager.)
  4. Launch the APK file, and accept the installation of application.
  5. For safety, I recommend you then undo your "Unknown sources" setting.

All you have to do know is copy your ROMs to your device, and then select them from the App.

Enjoy,
Tom

Wednesday, November 28, 2012

XNA and Visual Studio 2012 on Windows 8

Microsoft does not always make it easy to use their latest toys.

The Good
I recently rebuilt my computer and, wanting to stay on the forefront of technology, I of course installed Windows 8 and Visual Studio 2012. Both products come with a series of pros and cons, but the quick boot up times of Windows 8 and the parallel builds of Visual Studio 2012 make them both winners in my book.

The Bad
That being said, the first project that I wanted to work on after rebuilding my machine was a small XNA game that I have been writing for a while now. Unfortunately that XNA has been unofficially retired, and Microsoft is not making it easy for people to continue using their product.

And The Ugly
To use XNA on Windows 8 you must first download and install Games for Windows LIVE Redistributable. Then you must install XNA Studio into Visual Studio 2010 and manually copy the installed assemblies to Visual Studio 2012.

Here are the details:

I hope that helps,
Tom

Saturday, November 17, 2012

jQuery Refresh Page Extension

There are three scenarios when a web page refreshes:

  1. Honors your cache (does not make resource requests).
  2. Verifies your cache (causes resources to generate 304s).
  3. Ignores your cache (causes resources to generate 200s).

For a simple page refresh you want your webpage to generate as few server requests as possible. There any many ways to refresh a page in JavaScript, however they can all have different and sometimes undesirable results. Let's look some code snippets of the aforementioned scenarios in reverse order.

3. Ignore Cache

window.location.reload(true);

This scenario is not usually necessary unless you know that the resources of your page have been updated. Also, using a good minification framework is probably a better way of ensuring that your clients always consume the latest resources with every request.

2. Verify Cache

window.location.reload();

This is obvious similar to the first call, only we are calling an override of the reload method that tells the browser to respect cache. However, this will still cause a series of 304 requests!

1. Honor Cache

window.location.href = window.location.href;

This is obviously a very simple way to refresh the page, and it will honor cache without forcing 304 requests. However, this will not work if your includes a hash tag.

jQuery Plugin

This little jQuery plugin will refresh the page and try to cause as few resource requests as possible.

(function($) {
    $.refreshPage = refreshPage;
    
    function refreshPage(reload) {
        if (reload === true) {
            window.location.reload(true);
            return;
        }
        
        var i = window.location.href.indexOf('#');
        if (i === -1) {
            // There is no hash tag, refresh the page.
            window.location.href = window.location.href;
        } else if (i === window.location.href.length - 1) {
            // The hash tag is at the end, just strip it.
            window.location.href = window.location.href.substring(i);
        } else {
            // There is a legit hash tag, reload the page.
            window.location.reload(false);
        }
    }
})(jQuery);
Shout it

Enjoy,
Tom

Friday, October 26, 2012

xUnit Visual Studio Integration

Good news, everyone! It is actually very easy to get xUnit completely integrated with Visual Studio. You only need to install two plugins...

VS2010 - xUnit Test Runner Extension

This will support running tests with a Visual Studio test project.
This includes all of the VS features, such as code coverage!

https://github.com/quetzalcoatl/xvsr10/downloads

ReSharper - xUnit Contrib Plugin

This will allow ReSharper to detect and run xUnit tests.

http://xunitcontrib.codeplex.com/releases/view/92101
(If you are still running Resharper 6, then you will need the latest: v6.1.1)

Team Build (TFS) Integration

Integrating with xUnit your Team Foundation Server is a very tricky proposition, but it can be done. That, however, is a (rather long) blog post for another day!

Shout it

Enjoy,
Tom

Monday, October 22, 2012

How to Unlock a Configuration Section from IIS Manager

HTTP Error 500.19 - Internal Server Error
The requested page cannot be accessed because the related configuration data for the page is invalid.

This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false".

If you have ever experienced this problem then you probably have also experienced the fun of digging through your system configuration files to find where to unlock the related authentication sections. This is, to say the least, not fun.

Did you know that you can unlock configuration sections from IIS Manager?

  1. Launch IIS Manager.
  2. Select your Connection's home page.
  3. Open the Configuration Editor under Management.
  4. Navigate to the section that you need to unlock.
  5. Look to the right Action pane and click unlock!

Shout it

Enjoy,
Tom

Tuesday, September 25, 2012

Func Injection in Unity

Let your container be your factory. :)

If you are using LinqToSql and dependency injection, then you have probably created a factory with which you create DataContexts. But what if you could just let your IOC Container do that work for you? You can!

If you are using Unity then you can inject a Func<T> of any registered type. Unity will automatically bind the injected Func to the container's resolve method, thus preserving the resource Lifetime Manager.

Example Code

public class FuncInjectionTests
{
    [Fact]
    public void TransientLifetimeFuncIsThreadsafe()
    {
        var container = new UnityContainer();
 
        container
            .RegisterType<IUserService, UserService>(
                new ContainerControlledLifetimeManager())
            .RegisterType<IDataContext, DataContext>(
                new TransientLifetimeManager());
 
        var parallelOptions = new ParallelOptions {MaxDegreeOfParallelism = 100};
 
        Parallel.For(0, 1000, parallelOptions, i =>
        {
            var userService = container.Resolve<IUserService>();
 
            Parallel.For(0, 1000, parallelOptions, j =>
            {
                userService.Update();
            });
        });
 
        Assert.Equal(1, UserService.Count);
        Assert.Equal(1000000, DataContext.Count);
    }
}
 
public interface IUserService
{
    void Update();
}
 
public interface IDataContext : IDisposable
{
    void UpdateUser();
}
 
public class UserService : IUserService
{
    public static int Count;
 
    private readonly Func<IDataContext> _dataContextFactory;
 
    public UserService(Func<IDataContext> dataContextFactory)
    {
        _dataContextFactory = dataContextFactory;
        Interlocked.Increment(ref Count);
    }
 
    public void Update()
    {
        using (var dataContext = _dataContextFactory())
            dataContext.UpdateUser();
    }
}
 
public class DataContext : IDataContext
{
    public static int Count;
 
    public DataContext()
    {
        Interlocked.Increment(ref Count);
    }
 
    public void UpdateUser()
    {
        Trace.WriteLine(Thread.CurrentThread.ManagedThreadId + " - " + Count);
    }
 
    public void Dispose()
    {
    }
}
Shout it

Enjoy,
Tom

Saturday, September 15, 2012

The problem with the Nintendo eShop

Dear Nintendo,

I am a huge fan. I have owned every single Nintendo console that has ever been released (except for the Virtual Boy, naturally). You have made so many great games, and I love them all. I enjoy Mario, Zelda, Metroid, Donkey Kong, pretty much all of your franchises.

So, why won't you let me buy these games from you again?

The Nintendo 3DS eShop and the Ambassador Program

The eShop is Nintendo's online store and digital distribution system for the Nintendo 3DS. With it they sell small games, old emulated virtual console games, and they just recently selling complete feature titles via the eShop. It's a great idea that has been very successful, and it shows that Nintendo is trying to keep up with the times. Other companies, such as Valve and Apple, have made the vast majority of their profits in recent years by take percentages from their digital distribution services, Steam and iTunes respectively.

After the very sluggish start for 3DS sales, Nintendo inevitably had to commit to a price cut for their new hardware. To try and appease consumers who had already paid full price for their 3DS, Nintendo gifted early adopters with the "Ambassador Program". This granted all 3DS owners, whom had purchased their console prior to August 11th 2011, 20 free downloadable titles via the Nintendo eShop. These included 10 NES titles, and 10 GBA titles.

Nintendo has shown us that they can easily emulate software from previous consoles on the Nintendo 3DS.

The Current Predicament

Now that it is far too late to gain access to the Ambassador program, you can no longer download many of the virtual titles that were offered. Not even by PAYING for them! Not only are they completely unavailable now, but Nintendo has stated that they "currently have no plans to release those titles again."

When I look at the eShop's very small and weak game lineup, I find myself feeling very frustrated. Nintendo has so many classic games that I would love to pay for again and play again, and yet they refuse to release them to customers.

Nintendo is refusing to sell us software that they have already released; and they wonder why they are losing money for the first time in their company's history?

Nintendo eShop by the Numbers

Nintendo has been criticized for their handling both their Wii and DS eShops poorly, and I feel that this is rightly so. They do not have consistent new releases, they do a poor job of announcing and scheduling their releases, and worst of all they offer a very limited number of available titles.

  • Number of NES Games: 707
  • Number of GameBoy Games: 528
  • Number of GBC Games: 549
  • Number of SNES Games: 786
  • Number of GBA Games: 1017
  • Number of eShop Virtual Console Games: 68

Being that Nintendo owns the rights to their own games, and that they have already build the infrastructure to support selling and distributing these games on a global scale, I find these numbers to be embarrassing! Nintendo has already done all of the heavy lifting, why not put that system to better use?

A good analogy would be that Nintendo has build a transcontinental rail road system across the United States, but is only running a dozen trains on it.

Arguments Against Selling More Games

As I understand it, there are only three arguments against loading up a service like the eShop with an arsenal of downloadable titles:

  1. Oversaturation
    - By releasing too many games on the virtual console you could obscure smaller titles and reduce their sales.
    + I fail to see this as a problem, for as long as people are buy these old games (that would otherwise not be selling at all) you are still making money where you would have been before.
    + Additionally, there are over 1500 games available on Steam, and I have not seen Valve complain about that.
  2. Cross Promotional Releases
    - When a new game in a series is about to come out, there is often a sales spike for the previous games. This is partly because of the promotional advertise of the new game, but also because games often want to go back brush on the old games to prepare for next title, as well as some people just literally can not wait to play the game again.
    + Make some sales is always advantageous over making no sales, and sales, discounts, or bundles can work to help bolster sales prior to a game launch.
    + Again we can look to Steam for an example of how to make this work. Value uses every trick in the book to help promote new game sales, and it works out very well for them.
  3. Maintenance
    - The more games you release, the more games you have to provide support for.
    + Consoles are a controlled environment, there are not going to be issues with driver versions or third party software. All Nintendo has to do is support their emulators, which should (for the most part) be a one size fits all product. I can not see the cost in maintenance for this being overwhelming.
    + If open source software projects can do it in their spare time after work, then you Nintendo can too.

Summary: Nintendo should put more games on the Virtual Console.

So please, shut up and take my money,
Tom

Thursday, August 30, 2012

WebDrivers in Parallel

Here is a post that is long over due, and is finally getting published by request!

Concurrent WebDrivers

A WebDriver is not thread safe, but it is not required to be a singleton either. If you instantiate multiple drivers, they can all be run at the same time. If you cast a series of drivers into a collection of the IWebDriver interface, and then throw that into a Parallel ForEach, you have yourself one fun toy to play with!

So what are some uses of this?

Admittedly it is not a particularly common or practical use case to have multiple automated browser sessions running at the same time, but it can still come in handy. One fun application is to test website concurrency. You can do a minor load test of a webpage, or your can have two sessions trying to race each other for a limited resource.

...also, running WebDrivers in parallel can make for a killer demo!

Sample Code (xUnit Test)

public class ParallelDemo : IDisposable
{
    public IList<IWebDriver> Drivers { get; private set; }
 
    public ParallelDemo()
    {
        Drivers = new List<IWebDriver>
        {
            new InternetExplorerDriver(),
            new FirefoxDriver(),
            new ChromeDriver()
        };
    }
 
    public void Dispose()
    {
        foreach (var driver in Drivers)
        {
            driver.Close();
            driver.Dispose();
        }
    }
 
    [Fact]
    public void ParallelSearch()
    {
        Parallel.ForEach(Drivers, SearchForTom);
    }
 
    private static void SearchForTom(IWebDriver driver)
    {
        driver.Url = "https://www.google.com/#q=tom+dupont";
        var firstResult = driver.FindElement(By.CssSelector("h3 > a.l"));
        Assert.Contains("Tom DuPont .NET", firstResult.Text);
    }
}
Shout it

Enjoy,
Tom

Saturday, August 18, 2012

Linq to Sql Batch Future Queries

Batch queries are crucial feature of any good ORM...including LinqToSql!

Future queries are an unobtrusive and easy to use way of batching database queries into a lazy loaded data structures. Future queries were originally a feature of NHibernate, and have since been ported to other ORMs. However I am currently working on a project where I do not have access to those tools.

Thus I created LinqToSql.Futures, a simple extension that adds batched futures queries to LinqToSql.

Using Future Queries

  1. Get the LinqToSqlFutures NuGet package.
  2. Extend your DataContext to implement IFutureDataContext. (This is optional, see below!)
  3. Use future queries!

To batch queries together, simply call the ToFuture extension method on your IQueryables, this will return a Lazy collection of entities. The first time that one of the Lazy collections is accessed, all of the pending queries will be batched and executed together.

[Fact]
public void ToFuture()
{
    // SimpleDataContext Implements IFutureDataContext 
    using (var dataContext = new SimpleDataContext())
    {
        Lazy<IList<Person>> people = dataContext.Persons
            .Where(p => p.FirstName == "Tom" || p.FirstName == "Cat")
            .ToFuture();
 
        Lazy<IList<Pet>> pets = dataContext.Pets
            .Where(p => p.Name == "Taboo")
            .ToFuture();
 
        // Single database call!
        Assert.Equal(2, people.Value.Count);
        Assert.Equal(1, pets.Value.Count);
    }
}

To see the future queries working, you can use SqlProfiler to capture the batch query:

Extending Your DataContext

To use the code above, you need only extend your generated DataContext to implement the IFutureDataContext interface (which consists of a mere one property). Additionally, the sample code below overrides the Dispose method to optimize query disposal.

partial class SimpleDataContext : IFutureDataContext
{
    protected override void Dispose(bool disposing)
    {
        if (_futureCollection != null)
        {
            _futureCollection.Dispose();
            _futureCollection = null;
        }
 
        base.Dispose(disposing);
    }
 
    private IFutureCollection _futureCollection;
    public IFutureCollection FutureCollection
    {
        get
        {
            if (_futureCollection == null)
                _futureCollection = this.CreateFutureCollection();
 
            return _futureCollection;
        }
    }
}

Do you not have access to extend your DataContext? No problem...

Alternative Consumption

Your DataContext does not need to implement IFutureDataContext to use future queries. You can also create a FutureCollection and pass that into the ToFuture method. This will provide the future queries with a batch context.

[Fact]
public void FutureCollection()
{
    using (var dataContext = new SimpleDataContext())
    using (var futureCollcetion = dataContext.CreateFutureCollection())
    {
        Lazy<IList<Person>> people = dataContext.Persons
            .Where(p => p.FirstName == "Tom" || p.FirstName == "Cat")
            .ToFuture(futureCollcetion);
 
        Lazy<IList<Pet>> pets = dataContext.Pets
            .Where(p => p.Name == "Taboo")
            .ToFuture(futureCollcetion);
 
        // Single database call!
        Assert.Equal(2, people.Value.Count);
        Assert.Equal(1, pets.Value.Count);
    }
}

Links

If you enjoy the future queries or need to extend them, then please feel free to download the source from GitHub.

LinqToSql.Futures on GitHub
LinqToSql.Futures on NuGet

Shout it

Enjoy,
Tom

Sunday, July 29, 2012

Lazy Unity Injection

Adapting a legacy project to use proper Dependency Injection can be difficult.

Often times your services can not be singletons, and then those dependencies cause chains of other services to require transient lifetimes. Sometimes these service dependency trees can grow deep, and their initialization can become quite expensive. If you are working with a website, those big trees can add up and cost a lot of time and memory with every request.

One way to trim a deep or expensive dependency tree is to inject Lazy types of your services.

Lazy injection will prevent an optional resource from being instantiated until it is actually needed. While this is not a scenario that you want to architect your application into voluntarily, it can be a crucial performance optimization. Have you ever taken a memory dump of your website and found thousands of services floating in memory? I have, and let me assure you, it's not pretty!

LazyExtension for Unity

public class LazyExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.Policies.Set<IBuildPlanPolicy>(
            new LazyBuildPlanPolicy(), 
            typeof(Lazy<>));
    }
 
    public class LazyBuildPlanPolicy : IBuildPlanPolicy
    {
        public void BuildUp(IBuilderContext context)
        {
            if (context.Existing != null)
                return;
 
            var container = context.NewBuildUp<IUnityContainer>();
            var typeToBuild = context.BuildKey.Type.GetGenericArguments()[0];
            var nameToBuild = context.BuildKey.Name;
            var lazyType = typeof(Lazy<>).MakeGenericType(typeToBuild);
 
            var func = GetType()
                .GetMethod("CreateResolver")
                .MakeGenericMethod(typeToBuild)
                .Invoke(this, new object[] { container, nameToBuild });
 
            context.Existing = Activator.CreateInstance(lazyType, func);
 
            DynamicMethodConstructorStrategy.SetPerBuildSingleton(context);
        }
 
        public Func<T> CreateResolver<T>(
            IUnityContainer currentContainer, 
            string nameToBuild)
        {
            return () => currentContainer.Resolve<T>(nameToBuild);
        }
    }
}

Unit Tests

public interface ITestClass
{
    int HighFive();
}
 
public class TestClass : ITestClass
{
    public static int InstanceCount = 0;
 
    public TestClass()
    {
        Interlocked.Increment(ref InstanceCount);
    }
 
    public int HighFive()
    {
        return 5;
    }
}
 
[TestFixture]
public class TestFixture
{
    [Test]
    public void Test()
    {
        using (var container = new UnityContainer())
        {
            container.RegisterType<ITestClass, TestClass>();
            container.AddNewExtension<LazyExtension>();
 
            var testClass1 = container.Resolve<Lazy<ITestClass>>();
 
            Assert.AreEqual(false, testClass1.IsValueCreated);
            Assert.AreEqual(0, TestClass.InstanceCount);
 
            Assert.AreEqual(5, testClass1.Value.HighFive());
            Assert.AreEqual(true, testClass1.IsValueCreated);
            Assert.AreEqual(1, TestClass.InstanceCount);
 
            var testClass2 = container.Resolve<Lazy<ITestClass>>();
 
            Assert.AreEqual(false, testClass2.IsValueCreated);
            Assert.AreEqual(1, TestClass.InstanceCount);
 
            Assert.AreEqual(5, testClass2.Value.HighFive());
            Assert.AreEqual(true, testClass2.IsValueCreated);
            Assert.AreEqual(2, TestClass.InstanceCount);
        }
    }
}
kick it on DotNetKicks.com

Enjoy,
Tom

Tuesday, July 3, 2012

Game Design: How to Begin an RPG

Great games peak player interest in 10 minutes or less.

The opening moments are crucial to any story. Not only must an introduction immediately engage the audience, but it must also provide incentive to stick around for the entirety of the narrative. If a game does not spark interest quickly, the player will put it down and forget about it.

With regard to story, games must adhere to the same dramatic structure as any other medium, but with the additional challenge of including gameplay. A good game must be able to quickly move the player to an interesting inciting incident without sacrificing the fun of gameplay to get there.

To help us learn from our past, I have created a simple test to see if a game starts out well. Then, using this test, let us a review some past games to see how well they did!

10 minutes into the game, ask yourself the following questions:

  1. Who am I?
  2. What am I doing?
  3. Why do I care?

 

Final Fantasy 6

Who am I? I am a witch, one of the only magic users in the world.
What am I doing? I am driving a giant mech suit as I invade a town to capture some magic artifact.
Why do I care? As the opening narrative briefly explained, this artifact could return magic to the world and solidify the empires global consolidation of power. Also, I am a witch driving a mech, that is really cool!

Final Fantasy 6 opens perfectly. It has about 60 seconds of pitching the worlds history to you in the form of narration, during which it never drops any proper nouns such as names, dates, or locations, but in the background we are seeing characters that we will come to learn about later. It is a great setup in that it is quick and vague, yet it will pay off later. After that we have the beautiful and artistic scene of the mechs approaching Narshe, it sets the tone and atmosphere of the game well, but is literally completely skippable. After that we are dropped into combat as an overpowered group of warriors driving giant mech suites and destroying a town...ya, that is just FUN.

Verdict: Perfect Opening

 

Tales of the Abyss

Who am I? Some rich kid who gets headaches.
What am I doing? Wandering around my mansion, being scolded for talking to commoners, complaining that I am bored.
Why do I care? I don't.

Tales of the Abyss uses the cliche JRPG trope of an angsty teenager coming of age, only this time it is even worse because our protagonist is both rich and sheltered. When the inciting incident finally does occur a half hour into the game, it has no explanation or setup and it puts the lead characters motivations completely in question. All that the main character does for the first 30 minutes of the game is complain about how he wants to get out of the house, and then all he wants to do is go straight back home? Also, was there supposed to be a setup for an actual story hidden somewhere in there?

Verdict: Boring Introduction

 

Super Mario RPG: Legend of the Seven Stars

Who am I? I am Mario!
What am I doing? I am invading Bowser's castle! I am punching Goombas! I am jumping on Dry Bones! I am saving the princess!
Why do I care? I have played Mario in a dozen games before, but never in an RPG where I could actually beat up my enemies in isometric 3D!

This is the perfect example of how to leverage your existing IP when making an RPG. Players already have an emotional attachment to the characters and world that you are placing them in, so take full advantage of that. If a player has spent hundreds of hours trying to save a princess in the past, open the game up so that they are immediately feeling that reward. Then, after they are hooked, you can change to story to involve clouds that wear pants and talking dolls.

Verdict: Brilliant Opening

 

Golden Sun: Dark Dawn

Who am I? I don't know yet. I could be the same characters from the first game, or I could be their kids, I have not played as anyone yet!
What am I doing? I am listening to Garret and Issac groan on and on about how their world has changed. I played the first games and I don't even remember any of the places, events, or characters that they are name dropping. I do not know what I am doing now, much less what I will be doing in another 10 minutes.
Why do I care? Nothing has even happened yet; so far we have only talked about what is going on in the current scene, which seems to have nothing to do with the plot.

Dark Dawn falls into the age old sequel pitfall of trying to explain too much about the games that came before it. This exposition drags on for far too long and only serves to alienate the player from the game itself. When you can "play" a game for 20 minutes, and never actually get to play the game, something is very wrong.

Verdict: Horrendous Exposition

 

Cthulhu Saves the World

Who am I? I am Cthulhu.
What am I doing? I am going to bring insanity and destruction to the worl...oh, wait...I have to save the world? WTF?
Why do I care? Whether we know it or not Lovecraftian lore has permeated our culture; now we are being asked to comically flip our preconceived notions on their head, and man is it funny.

Cthulhu Saves the World takes the opposite approach to most modern RPGs: it is less dark and less serious than the source material which it is based on. Humor is a very powerful and seemly underrated tool in the game market, especially among RPGs. Players do not always want to see the world end. They do not need to watch characters brood. Sometimes gamers just want to have FUN. It amazes me that a little indie company like Zeboyd Games can figure that out, while a giant like Square Enix seems to have completely forgotten it.

Verdict: Hilarious Setup

 

Which other games can these opening questions be applied to?

Seiken Densetsu 3?
Final Fantasy Tactics?
World of Warcraft?

Game on,
Tom

Saturday, June 16, 2012

.NET BlockingQueue

Task.Factory.StartNew is great, but often abused.

Do you need to make a call that could take a long time to complete, but you don't care about the results? If so, then you need to make an async call. Should you make these calls by constantly creating and starting new Tasks? No, as this could use up a lot of resouces, exhaust your thread pool, or possibly even tear down your app domain.

I was recently introduced to System.Collections.Concurrent.BlockingCollection, and I absolutely love that class. However, 99% of my use cases with BlockingCollections are actually more specific to queuing. My solution: create a generic BlockingQueue!

Simple File Example

public class SimpleFile
{
    public string Path { get; set; }
    public string Contents { get; set; }
}
 
public class SimpleFileQueue : BlockingQueue<SimpleFile>
{
    public SimpleFileQueue(int threadCount) : base(threadCount) { }
 
    protected override void ProcessModel(SimpleFile model)
    {
        System.IO.File.WriteAllText(model.Path, model.Contents);
    }
 
    protected override void HandleException(Exception ex)
    {
        // TODO: Log me!
    }
}
 
public static class SimpleFileExample
{
    public static readonly SimpleFileQueue Queue = new SimpleFileQueue(3);
 
    public static void EnqueueSimpleFile(string path, string content)
    {
        Queue.Enqueue(new SimpleFile
        {
            Path = path,
            Contents = content
        });
    }
}

BlockingQueue<T> Implementation

public abstract class BlockingQueue<T> : IDisposable
{
    #region Private Members
 
    private const int Timeout = 60000;
 
    private bool _disposed;
    private readonly CancellationTokenSource _tokenSource;
    private readonly BlockingCollection<T> _collection;
    private readonly Task[] _tasks;
 
    #endregion
 
    #region Public Properties
 
    public int Count
    {
        get { return _collection.Count; }
    }
 
    public bool IsCanceled
    {
        get { return _tokenSource.IsCancellationRequested; }
    }
 
    public bool IsCompleted
    {
        get { return _tasks.All(t => t.IsCompleted); }
    }
 
    #endregion
 
    #region Constructor & Destructor
 
    protected BlockingQueue(int threadCount)
    {
        _tokenSource = new CancellationTokenSource();
            
        var queue = new ConcurrentQueue<T>();
        _collection = new BlockingCollection<T>(queue);
 
        _tasks = new Task[threadCount];
        for(var i=0; i<threadCount; i++)
            _tasks[i] = Task.Factory.StartNew(ProcessQueue);
    }
 
    ~BlockingQueue()
    {
        Dispose(true);
    }
 
    #endregion
 
    #region Abstracts
 
    protected abstract void HandleException(Exception ex);
 
    protected abstract void ProcessModel(T model);
 
    #endregion
 
    #region Methods
 
    public void Enqueue(T model)
    {
        if (IsCompleted)
            throw new Exception("BlockingQueue has been Completed");
 
        if (IsCanceled)
            throw new Exception("BlockingQueue has been Canceled");
 
        _collection.Add(model);
    }
 
    public void Cancel()
    {
        if (!IsCanceled)
            _tokenSource.Cancel(false);
    }
 
    public void CancelAndWait()
    {
        Cancel();
        Task.WaitAll(_tasks);
    }
 
    private void ProcessQueue()
    {
        while (!IsCanceled)
        {
            try
            {
                T model;
                var result = _collection.TryTake(out model, Timeout, _tokenSource.Token);
 
                if (result && model != null)
                    ProcessModel(model);
            }
            catch (OperationCanceledException)
            {
                break;
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }
    }
 
    #endregion
 
    #region IDisposable
 
    public void Dispose()
    {
        Dispose(false);
    }
 
    private void Dispose(bool finalizing)
    {
        if (_disposed)
            return;
 
        Cancel();
 
        if (!finalizing)
            GC.SuppressFinalize(this);
 
        _disposed = true;
    }
 
    #endregion
}
Shout it

Enjoy,
Tom

Saturday, June 9, 2012

Prometheus Review

I am a huge Alien fan, but I did not enjoy Prometheus.

First, let's start on a good note: Prometheus is visually stunning, the cinematography is gorgeousness, the art and atmosphere are stellar, and the acting is top notch. Technically speaking, it is a very well made film.

Unfortunately the story and screenplay are just f***ing terrible.

The characters are shallow, the dialog is absolutely terrible, the pacing is inconsistent, and the plot is sloppy at best. There are countless elements in the film that get setup but yield absolutely no pay off. Worse still is how many bad plot points there are; things that are completely and utterly illogical or contradictory, and staring the audience right in the face.

Movies do not need to resolve every plot point that they contain, they do not need to explain every event, nor do they need to answer every question that they raise. However this does not excuse plot holes, inconsistency, and bad writing.

Here are 150 unanswered questions that I had after watching Prometheus:

  1. If the space jockeys seeded life on Earth, how did people make cave paintings of them?
  2. What about the seventh piece of cave art was more compelling than the first six?
  3. Why is there a giant holographic display grid in a cargo bay?
  4. How does no one know why they are on this mission?
  5. What makes the main character an expert in the fields of archaeology, ancient civilizations, aliens, and astronomy?
  6. How did the main character convince one of the richest most powerful men in the world of anything by using a series of cave drawings?
  7. Why did the ship not scan the planet before going to land?
  8. How lucky would you have to get to just spot an alien building bu looking out the window?
  9. Why does everyone go out for their first expedition with only 6 hours of daylight left?
  10. How could anyone think that the pyramid was created naturally?
  11. How does the unsealed pyramid have a breathable atmosphere contained in it?
  12. Why is no one worried about taking off their helmets in the alien atmosphere?
  13. Why does no one ask the android to translate any markings?
  14. Where did the fresh goo on the wall come from?
  15. Why does no one ask the android how or why he activated the holographs?
  16. Why did the holographs play back that specific death scene?
  17. What were the space jockeys running from?
  18. Why did the advance automatic door not have a stop sensor like my primitive garage door?
  19. Why does the rock guy not take rock samples?
  20. Why do none of the scientists do anything remotely scientific?
  21. Why is there a giant head in the middle of the room?
  22. Why are the containers spaced around the room?
  23. Why after two thousand years are the containers just starting to ooze now?
  24. How do the two scientists with GPS and map get lost in the caves?
  25. Where did the freak storm come from?
  26. How do the same two guys not hear the radio transmission about the storm?
  27. Why do none of the crew direct them out of the caves?
  28. How does it take them 30 minute to realize they are lost, when everyone else made it back in less time?
  29. How does no one know that the other two didn't make it back, especially given that they took all the vehicles?
  30. Wasn't it supposed to be night time by now?
  31. What does the word quarantine mean in this universe?
  32. How do they know that the alien even has a nervous system that works like ours?
  33. Why would they want to fake the alien nervous system into thinking it is alive?
  34. Why do they stick that thing in the dead alien's ear to stimulate electricity?
  35. Why does waking up the brain also weak up the bacteria?
  36. Why does that make the head blow up?
  37. Why does no one do any more research on the blown up head?
  38. How is the DNA a 100% match if they are not human?
  39. If the DNA is a 100% match, did the space jockeys only seed human life on earth?
  40. If the storm was artificial or triggered by the humans, where did it go?
  41. Why is there a pile of dead space jockeys?
  42. What killed the pile of dead space jockeys?
  43. Where did the think that killed the dead space jockeys go?
  44. If the space jockey busted from the inside, what laid an egg in him?
  45. Where did the egg layer go?
  46. Where did the alien that hatched out of him go?
  47. Why does the captain ask where the two scientists are when they have a giant 3D map that shows exactly where they are?
  48. Why does no one send another probe to investigate the life form readings?
  49. Why does no one go to investigate the life form readings?
  50. Why does no one care about the life form readings?
  51. Why do the scared scientists go back to the room that scared them to go to sleep?
  52. Where did the biologist get that little blanket?
  53. Why do they think the alien worm is cute?
  54. Why does the alien worm have acid for blood?
  55. Why does the alien worm have unreasonably high blood pressure?
  56. How does the alien worm regrow its limbs?
  57. Where does the alien worm go after it kills the scientists?
  58. Why does no one see the video feed from the dead or dying scientists?
  59. How does no one notice the two scientists are dead?
  60. Why does the container have only one drop of black fluid if the other containers are oozing a gallon of goo apiece?
  61. Why would you freeze a bottle of wine?
  62. Why does talking about creating life piss off the main character so much?
  63. Why does the girl have sex with the captain?
  64. Why does the guy not tell anyone about having a worm in his eye?
  65. Why did the survey drone stop at the dead end door if it didn't tell anyone that it was at a door?
  66. Why did that drone detect life in an empty corridor?
  67. Why are the containers in the cargo bay not oozing like the other containers?
  68. Why are the containers in the cargo bay stacked differently than the other containers?
  69. Why does everyone stop caring about the other dead guys and the worms coming out of heads when another guy gets sick?
  70. Why does that guy not just take his helmet off if he want to die?
  71. If she is such a bad ass, why does that girl cry after using the flame thrower?
  72. Why does no one do an autopsy on the mutant body?
  73. How does the other mutant find the ship?
  74. Why did the other guy become a mutant?
  75. Why was he super strong, super fast, and super aggressive?
  76. Why does no one do an autopsy on the other mutant body?
  77. Why is David conducting a medical diagnosis of the main character instead of a doctor?
  78. Why does David need to decontaminate the cross, especially given that she is infected with an alien?
  79. Why doesn't the android just put the main character into cryosleep?
  80. How does the main character knock out the other two doctors?
  81. Why is the surgery capsule not programmed to work on women if it belongs to the other girl?
  82. If the surgery capsule is not programmed to work on women, how does it start working?
  83. How does the main character stay awake with all those pain killers?
  84. Why can the surgery machine not drug the patient?
  85. How do staples heal the tissue and stop the bleeding?
  86. How can the main character walk around after that surgery?
  87. How come no one cares that the main character just aborted an alien?
  88. Where did all of Weylands private medical staff come from?
  89. Weren't some of those doctors knocked unconscious by the main character just a few minutes ago?
  90. Why is Weyland played by a young guy in makeup if he is always old?
  91. What does it matter that the girl is Weyland's daughter?
  92. How does the captain know that this is a weapon facility?
  93. Why would a weapon facility have statues of giant heads?
  94. Why would the space jockeys leave a map that leads to a weapon facility?
  95. How can the main character go adventuring after that surgery?
  96. Why does that main character not care that the android killed her husband?
  97. Why does the android want to kill his maker, especially after helping keep him alive all movie?
  98. What is Weyland's plan when he meets the space jockey?
  99. Why do the space jockeys use flutes to unlock their computers?
  100. When did the android learn to speak space jockey?
  101. What did the android say to the space jockey?
  102. Why did that piss of the space jockey?
  103. Why did the space jockey wait for a few minutes before killing everyone?
  104. Why do bullets not hurt the space jockey?
  105. Why did no one bring a flame thrower?
  106. Why if the space jockey's mission was to kill all humans, was he asleep for thousands of years?
  107. Why, if hyper sleep is used for travel, was the space jockey asleep on a planet?
  108. Why is only one space jockey left alive?
  109. Why did the space jockey not care about any of the other space jockeys?
  110. Why does the space jockey immediately leave to destroy earth if he was arbitrarily woken up?
  111. How do they know the space jockey wanted to wipe out earth?
  112. How do they know the space jockey "changed his mind" about earth?
  113. Why would the space jockey want to wipe out earth?
  114. Why wouldn't the space jockey want to wipe out any other planets?
  115. Why does the space jockey not kill the main character?
  116. How can the main character run after that surgery?
  117. How did they not detect the giant launch doors two feet under the soil?
  118. Why does the girl decide that is the right time to go home and not earlier?
  119. Why does the flight crew decide to be suicidal heroes?
  120. Why does the girl run for the escape pod instead of try to stop the captain?
  121. Why do they throw their hands up before running in to the ship?
  122. How does the alien ship get damaged from the collision but not the fall?
  123. Why do the two girls run in the wrong direction every time?
  124. Why did the main character's suit run out of air so quickly?
  125. How did the baby squid not die earlier?
  126. How did the baby squid get so big?
  127. How did the space jockey survive the crash?
  128. How did the space jockey get out of his ship after the crash?
  129. How did the space jockey find the little escape module?
  130. How did the space jockey survive outside without his iconic helmet?
  131. Why does the space jockey now finally want to kill the main character?
  132. Why does the android wait all this time to finally call and mention that the space jockey is on his way?
  133. How did the android know that is where the space jockey was going?
  134. How does the main character's suit get refilled on air so quickly?
  135. How did the androids body not move during the crash?
  136. How does the android know about all the other ships?
  137. Why does the main character now trust the android again?
  138. If she is repelling down, how did the main character get into the crashed ship originally?
  139. Why does the main character take the androids useless body?
  140. Why didn't the space jockey go to use one of the other ships after he crashed?
  141. Why are there no other space jockeys in any of the other ships?
  142. Why are there no aliens or worms or goo in any of the other ships?
  143. How does the android know how to fly the other ships?
  144. What did the main character use to leave her final message?
  145. Why did the no one ever send any information or progress reports back to earth?
  146. What is the main character going to eat or drink on her voyage?
  147. If this was supposed to setup the original movie, how does the alien get to LV-426?
  148. Why is the alien hatched as a medium size alien instead of a small alien?
  149. If the alien is supposed to be an alien, then why does it not look like an alien?
  150. What questions left by the original 1979 film does Prometheus actually answer?

Ridley Scott used to be my absolute favorite director of all time. Unfortunately his recent streak of mediocre films had lessened my opinion of him, and now this is the final nail in the coffin. Mr. Scott may be a very skilled and highly technical director, but I can just no longer consider him to truly great film maker.

2 / 5

Sorry,
Tom

Sunday, May 20, 2012

MvcBundleConfig NuGet Package

MvcBundleConfig is now available on NuGet!

MvcBundleConfig is a very simple project that adds configuration and debugging features to MVC 4's bundling framework. Once you create a new MVC4 web application and install the MvcBundleConfig NuGet Package, then you need only update our layout to use the new bundle extension methods, and you are ready to go!

Please note that the System.Web.Optimization NuGet package is still in beta, and thus that dependency is not included in the current version of the NuGet package. However, if you have created a new MVC4 project then that assembly should already be included.

Sample Installation Steps

  1. Create a new ASP.NET MVC 4 Project
  2. Select Manage NuGet Packages
  3. Search for and install MvcBundleConfig
  4. Update the Layout
kick it on DotNetKicks.com

Enjoy,
Tom

Friday, May 4, 2012

Configuring WebClient Timeout and ConnectionLimit

Simplicity is nice, but not when it comes at the expense off accomplishing your goal.

Recently I have been using System.Net.WebClient to access some REST APIs. It is great how simple the WebClient class is to use, but unfortunately it does not natively support configuring it's timeout or connection limit. In fact, before I knew that the default connection limit was preventing me from making more than two concurrent requests at a time, it was actually causing me some serious issues while doing performance testing.

Good news, both of these issues are very easy to fix by simply extending the WebClient class.

Sample Class

public class ConfigurableWebClient : WebClient
{
    public int? Timeout { get; set; }
    public int? ConnectionLimit { get; set; }
        
    protected override WebRequest GetWebRequest(Uri address)
    {
        var baseRequest = base.GetWebRequest(address);
        var webRequest = baseRequest as HttpWebRequest;
        if (webRequest == null)
            return baseRequest;
 
        if (Timeout.HasValue)
            webRequest.Timeout = Timeout.Value;
 
        if (ConnectionLimit.HasValue)
            webRequest.ServicePoint.ConnectionLimit = ConnectionLimit.Value;
 
        return webRequest;
    }
}
kick it on DotNetKicks.com

Enjoy,
Tom

Saturday, April 14, 2012

xUnit Theory, the Data Driven Unit Test

Update: I have also written a post about NUnit's Data Driven TestCaseAttribute.

Do you like copying and pasting code? Neither do I.

A good set of unit tests often end up reusing the same code with varied inputs. Rather than copy and paste that test code over and over, we can use the pattern of data driven unit tests to help streamline our test fixtures. This is the practice of having a single test definition be invoked and count as multiple tests at run time. This also enables us to do other dynamic things, such as configuring our unit tests from external sources. :)

I frequently use MSTest, but it's data driven tests inconveniently require you to define a DataSource. (Updated) Come to find out NUnit does offer data driven unit tests with their TestCaseSource attribute. Meanwhile xUnit offers several lightweight and simple options for defining data driven tests, which it refers to as theories.

Let's take a look at some of xUnit's Theory data sources:

InlineData Example

public class StringTests1
{
    [Theory,
    InlineData("goodnight moon", "moon", true),
    InlineData("hello world", "hi", false)]
    public void Contains(string input, string sub, bool expected)
    {
        var actual = input.Contains(sub);
        Assert.Equal(expected, actual);
    }
}

PropertyData Example

public class StringTests2
{
    [Theory, PropertyData("SplitCountData")]
    public void SplitCount(string input, int expectedCount)
    {
        var actualCount = input.Split(' ').Count();
        Assert.Equal(expectedCount, actualCount);
    }
 
    public static IEnumerable<object[]> SplitCountData
    {
        get
        {
            // Or this could read from a file. :)
            return new[]
            {
                new object[] { "xUnit", 1 },
                new object[] { "is fun", 2 },
                new object[] { "to test with", 3 }
            };
        }
    }
}

ClassData Example

public class StringTests3
{
    [Theory, ClassData(typeof(IndexOfData))]
    public void IndexOf(string input, char letter, int expected)
    {
        var actual = input.IndexOf(letter);
        Assert.Equal(expected, actual);
    }
}
 
public class IndexOfData : IEnumerable<object[]>
{
    private readonly List<object[]> _data = new List<object[]>
    {
        new object[] { "hello world", 'w', 6 },
        new object[] { "goodnight moon", 'w', -1 }
    };
 
    public IEnumerator<object[]> GetEnumerator()
    { return _data.GetEnumerator(); }
 
    IEnumerator IEnumerable.GetEnumerator()
    { return GetEnumerator(); }
}
kick it on DotNetKicks.com

Happy testing!
Tom

Sunday, April 8, 2012

Migrating from NUnit to xUnit

I recently started using xUnit, and I have been really enjoying it!

If you are currently using NUnit to write your unit tests, then it is not at all difficult to migrate to using xUnit. The philosophical difference between the two is simply this: with xUnit you need to think of your tests as objects, rather than of methods. Or if you prefer acronyms: put some more OOP into your TDD. *bu-dum, tish!*

Anyway, here is a visual representation of equivalent commands between NUnit and xUnit:

xUnit

NUnit

[NUnit.Framework.TestFixture]
public class TestFixture
{
  [NUnit.Framework.TestFixtureSetUp]
  public void TestFixtureSetUp()
  {
    // 1) Set up test fixture  -------->
  }
  [NUnit.Framework.TestFixtureTearDown]
  public void TestFixtureTearDown()
  {
    // 8) Tear down test fixture  ----->
  }
 
 
 
  [NUnit.Framework.SetUp]
  public void SetUp()
  {
    // 2) Set up TestA  --------------->
    // 5) Set up TestB  --------------->
  }
  [NUnit.Framework.Test]
  public void TestA()
  {
    // 3) Run TestA  ------------------>
  }
  [NUnit.Framework.Test]
  public void TestB()
  {
    // 6) Run TestB.  ----------------->
  }
  [NUnit.Framework.TearDown]
  public void TearDown()
  {
    // 4) Tear down TestA  ------------>
    // 7) Tear down TestB  ------------>
  }
}
 
public class TestData : IDisposable
{
 
  public TestData()
  {
    // 1) Set up test fixture
  }
 
  public void Dispose()
  {
    // 8) Tear down test fixture
  }
}
public class TestClass
  : IDisposable, Xunit.IUseFixture
{
  public TestClass()
  {
    // 2) Set up TestA
    // 5) Set up TestB
  }
  [Xunit.Fact]
  public void TestA()
  {
    // 3) Run TestA
  }
  [Xunit.Fact]
  public void TestB()
  {
    // 6) Run TestB
  }
 
  public void Dispose()
  {
    // 4) Tear down TestA
    // 7) Tear down TestB
  }
  public TestData TestData { get; set; }
  public void SetFixture(TestData data)
  {
    // 2.5) Set fixture data for TestA
    // 5.5) Set fixture data for TestB
    TestData = data;
  }
}
kick it on DotNetKicks.com

Happy testing!
~Tom

Saturday, March 31, 2012

Configuring Bundles in MVC 4

We write a lot of JavaScript.

Thus the bundling, compression, and minification of JavaScript is important to the speed and performance of modern websites. This is why I love and have been a big advocate of tools like Combres, and also why I was so excited to hear that such features were (finally) coming built in to ASP.NET MVC 4.

Introducing MvcBundleConfig

MvcBundleConfig is a very simple minimalist project I wrote to add configuration and debugging features to MVC 4's bundling framework, and achieves all 6 six of the goals listed below. It requires only MVC4 to run, and you need only add one config file to your project, one line of code to your application start, and you are off and running.

NuGet Package: https://nuget.org/packages/MvcBundleConfig/

Source on GitHub: https://github.com/tdupont750/MvcBundleConfig

Before we get to the demonstration at the bottom, let's review the needs and wants of a good minification framework.

What I NEED in a minification tool:

  1. Compress resources into single files.
    • Multiple request take time and resources, neither of which are things that any of us have to spare. By compressing resources into single requests and can limit the overhead and load time on both our clients and our servers.
  2. Minify JavaScript and CSS content.
    • Minification removes as many unnecessary white spaces and characters as possible from your resource files, reducing file size by up to 80% on average. When then compounded with gzip, we can reduce the file size another 50%. This means that our web applications can be brought down to clients 90% faster.
  3. Make use of both client and server side caching.
    • Making 90% less requests is nice, and making those requests 90% smaller is even better, but only ever having to request or serve them once is the key to true performance. Unfortunately client and server caching can be a bit complex due to quirks of different technologies and browsers. A good minification framework should abstract these concerns away from us.
  4. Ability to turn off during debugging.
    • As fantastic as everything that we have listed about is for a production website, it is a nightmare for a development website. Debugging JavaScript is no less complicated or time consuming than debugging C#, and we need to be able to use a debuggers and other client side tools that are inhibited by minification. A good minification framework must expose a debugging mode that skips compression pipeline.

What I WANT in a minification tool:

  1. Simple and dynamic configuration.
    • I hate hardcoded configuration. It bloats my code, and it requires bin drops to deploy. Meanwhile I really like the ability to add simple configuration files to my site as often as I can. Config files are explicit, abstract, and can be updated at any time. Win.
  2. Take a few dependencies as possible.
    • I mentioned above that I like Combres and it has a reasonably sized code base, unfortunately the fact that it's NuGet package pulls down half a dozen additional dependencies makes it feel quite heavy. The fewer dependencies a framework takes the better.

MvcBundleConfig Examples

Bundles.config

<?xml version="1.0"?>
<bundleConfig ignoreIfDebug="true" ignoreIfLocal="true">
  <cssBundles>
    <add bundlePath="~/css/shared">
      <directories>
        <add directoryPath="~/content/" searchPattern="*.css" />
      </directories>
    </add>
  </cssBundles>
  <jsBundles>
    <add bundlePath="~/js/shared" minify="false">
      <files>
        <add filePath="~/scripts/jscript1.js" />
        <add filePath="~/scripts/jscript2.js" />
      </files>
    </add>
  </jsBundles>
</bundleConfig>

Global.asax.cs

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
    BundleTable.Bundles.RegisterTemplateBundles();
 
    // Only code required for MvcBundleConfig wire up
    BundleTable.Bundles.RegisterConfigurationBundles();
}

_Layout.cshtml

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
 
        @* Call bundle helpers from MvcBundleConfig *@
        @Html.CssBundle("~/css/shared")
        @Html.JsBundle("~/js/shared")
    </head>
    <body>
        @RenderBody()
    </body>
</html>

In the Browser

NuGet Package: https://nuget.org/packages/MvcBundleConfig/

Source on GitHub: https://github.com/tdupont750/MvcBundleConfig

kick it on DotNetKicks.com

Enjoy,
Tom

Real Time Web Analytics