Thursday, September 23, 2010

So Dave Newman announces he’s leaving .NET

Dave Newman recently announced that he’s leaving .NET for Ruby. Fine. But he’s giving a bunch of reasons that I strongly disagree with.

Dave thinks that the .NET community is “cut off from the world”, because he’s seen “developers build their own libraries and frameworks in curiously terrible ways”. Sure, fine. But is that a problem with .NET developers?

I’ve been around for a while now, and I’ve worked in .NET, C, C++ and Delphi. Yes, that Delphi, but that period of my life is over. I’ve worked as a freelancer for many years, and I’ve been in a lot of projects using a lot of different development tools and I can tell you this: it’s not a .NET problem. The problem is everywhere. In every project, in every company, there’s always a bunch of problems that need to be dealt with that could have been dealt with using some open-source library somewhere, code that was already written by someone else, but that just wasn’t good enough to do it. Joel has written good stuff in defense of not-invented-here syndrome.

In fact, I wouldn’t consider the phenomenon a problem as there may be very well good reasons why such a problem should be tackled in-house. Whether it’s a problem in such case depends on a lot of factors. But there you are, thinking that this .NET thing is over and that the community sucks and that the grass is greener on the other side. That’s a natural thing. The grass is always greener on the other side because you’re too far off to see how not so much greener it really is.

Dave goes on bashing ASP.NET MVC and how not open source it is because Microsoft doesn’t accept outside contributions. Well, check your facts, Dave. Accepting contributions is not a requirement of a project to be open-source. Some really high-profile open source products don’t accept outside contributions. qmail is such a nice example. Does that mean that no-one can contribute to qmail? Of course not. There’s patches everywhere! Will they work when/if qmail 2.0 comes out? That remains to be seen…

If you want, Dave, you can go and fork ASP.NET MVC yourself, set it up somewhere and start making and distributing your changes. In fact, that’s what you’re talking about when you mention someone setting up a github repository that is dead in the water. Oh, I’m sorry, can you expect from Microsoft to support every little fork out there?

So is Ruby very different?

Ruby, first of all, is a language. So it doesn’t really compare to ASP.NET MVC. You should actually compare it to C#, which has a much bigger community than just ASP.NET MVC. Ruby on Rails, on the other hand, compares much better than ASP.NET MVC, and is equally open source. If you go out and fork Ruby on Rails, will you expect to get support from the Ruby on Rails community? I don’t think so. And if you implement your own feature on top of it, will it be taken up upstream, garantueed? I don’t think so either. Not with David Heinemeier Hansson’s seal of approval. He likes to keep things tight, y’know?

Lastly,

To those who want to be part of a vibrant community of passionate developers crafting new and innovative solutions, start hanging out with people who aren’t tied to a particular language or framework.

says the man who just jumped ship to Ruby.

Tuesday, September 14, 2010

The problem of “pre-owned games”

Today, while checking up on one of my favorite game franchises, I ran into this article about how Treyarch are going to “defeat” the problem of pre-owned games. For a minute I was under the impression this article was going to be about how difficult it is these days for gamers to trade, swap and resell their old games, but no. It seems the “problem” is not the game itself, but the honest gamer who paid good money to buy the game in the first place.

You see, they still don’t get it. They think that they’re going to lose money when those gamers are able to trade in or swap games, just like you could swap books or DVD’s. Surely that criminal activity must be stopped! I should note that the article itself details how Treyarch is going to handle the “problem” by providing more quality. Uhmm, sure. But between the lines you can read how the industry feels about its consumer base. How long do they think those honest gamers will stay honest? Surely none of them will have the guts to look for a no-cd patch or any other way to circumvent the roadblocks put in place to stop their malicious behavior.

It happened before with DRM on music, and it will happen again with games and tomorrow with ebooks. Copy protection has had and will have zero effect on those who are not willing to fall in line with the publisher’s demands. Instead, it will keep pushing the paying customers away from spending their hard-earned cash.

Monday, September 06, 2010

The Great App Bubble? I don't think so...

Another day, another "article" that gathers random facts and applies some strange statistics to it, only to come to some conclusion that, frankly, I don't see.

In this article, the blogger named Aaron Shapiro aims to take a stab at Apple's App Store, claiming that we're now in an App Bubble, that everybody wants to have an App whether it will make them money or not. 

In his first claim, that apps don't make money for developers, the author simply takes the grand total of revenue for all the apps and divides it by the number of apps available, while at the same time guesstimating what a 'typical' app would cost to make to conclude that the cost of a typical app is greater than the average revenue, and that therefore no profit can be made. First, there is no "typical" app, and can't see how you come to that "typical" cost of $35000 when most of the apps share a common platform on which they are built. Have a look at the list of recently released 'music' apps to find out how many of them are simply copies with different content and you'll see what I mean.

Second, not every app is made to be profitable. Sometimes the apps aren't the products being sold, but simply exist to support the products being sold. I bought a WiThings scale the other day, and installed the free app that comes with it. Does that mean that the app doesn't make them money? Of course it does, because it provides value to the product that is really being sold: the scale. Or have a look at all the apps that guide you through a music festival, or a museum. There's plenty of examples of apps that don't even need ads to be profitable.

In his second claim, Aaron states that apps don't make money for Apple. While that isn't a false claim, I don't see how it supports his idea of an App Bubble in the first place.

The rest of the article is an amalgam of unrelated facts and trivia, with little or no substance to validate his claim. "Marketers are spending money on iDevice apps at the expense of improving their mobile Web sites that everyone with a smart phone can access." How many? Who? Of course, there will always be people making the wrong decisions. Does that imply a climate? Of course not.

What's needed are bloggers that don't give in to primal urges to come up with sensationalist ideas about the world we live in and then try to validate their claims by retrofitting unrelated and unsupportive facts.

Wednesday, August 25, 2010

When debugging WCF ...

...remember this attribute:

[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]

Wednesday, August 18, 2010

The Web is not 'dead'

Got my eyes on this so-called article today, claiming that the Web as we know it is dead.

Just because there's a lot more traffic going to peer-to-peer and video applications doesn't mean that the Web is dead. I'm sure most of that video is fed over HTTP, the protocol that the Web is built on, and uses URLs to get found. Most of the third party applications that don't need a browser to talk to their servers using Web Services in one form or another, so I wouldn't consider that 'dead' either.

The fact that people are using more and more devices that can reach the Web without actually using a browser also doesn't mean that the Web is dead. In fact, more and more devices (handhelds, smartphones, music players, consoles) actually gain a modern useful browser that can be used to browse all of the Web instead of being limited to device-specific formats such as WAP. So I'd say there's more useful browsers these days than before.

The writer of that article clearly doesn't have a clue about what makes all these useful apps work. I just felt like pointing that out to anyone that takes that article at face value.

Friday, August 06, 2010

Adam.JSGenerator

Over the past few months I've been working on a side project at work, that my employer has now allowed me to publish as my very first open source project. I'm very excited.

Read more about it here.

Tuesday, March 16, 2010

Learning

Today I learned something.

Today I learned that a Stack Overflow is not just a good place to go when you're stuck with something or you want a second opinion. It's also a good place to go when you want to learn something.

Here's how to do it.

Go over there, and look for recent questions on the topic that you want to learn something about. Preferably questions that are not exactly completely answered, and that are non-trivial to answer. But, try to stay away from the "please do my homework for me" type of questions.

You don't have to have an answer ready for it. I usually browse SO and sometimes find answers that I can just type from the top of my head and be done with it. But that's not what I'm aiming for here. Try to find a question where you know you'll spend a good 15 minutes to half an hour figuring out what the real answer is, and then try to answer it. Ideally, you'll have an understanding of the problem, but you just know that if you spend some time, you'll come up with a really good answer.

You'll spend 15 to 30 minutes on it, because you'll have to do some research and you'll have to make some sample code (and test it too!). You don't need to write out an entire application. Sometimes it's enough to have a couple of lines of code in your answer that will do the trick, but you'll need to make that test app for yourself to make sure that what you write will work.

Don't be a rep whore. You won't gain much reputation points, because it may be an older question (older than 2 minutes) and the person who posed the question may have left permanently, or there may be less complete answers that have already been accepted. But the point it not to gain rep. The point is to learn.

That's what I did today. It took me little over half an hour. But I learned something. And I'm gonna do it again tomorrow.

Thursday, February 25, 2010

Visual Studio caches the project file and imported

Today I found this one out the hard way:
Visual Studio caches the content of project files and files imported by project files. If you edit a loaded project file, Visual Studio will automatically prompt you to reload the project so that the changes take effect. However if you edit a file imported by a loaded project, there will be no reload prompt and you must unload and reload the project manually to make the changes take effect.

Monday, January 25, 2010

Unable to start debugging on the web server. The object identifier does not represent a valid object.

When you get this message, first thing you should check is if Windows Authentication is turned on for this website.

That's all.

Saturday, September 19, 2009

Enabling client caching of dynamic content

In my last post, I was explaining how I wanted to return pictures from my ASP.NET MVC based application. In short, this is what I did:

[ReplaceMissingPicture(Picture = "~/Content/Images/nopicture.png", MimeType = "image/png")]
public ActionResult Picture(int id)
{
    var item = Service.GetItem(id);

    if (item.Picture != null)
    {
        var picture = item.Picture.ToArray();
        var mime = item.PictureMime;

        return File(picture, mime);
    }
    else
    {
        return null;
    }
}

Please see my last post for more information about what you see here.

This is all nice, but there’s a problem. Every time the client requests our “Picture” action method, the picture is returned with a HTTP status code 200. This is what you normally want, but my concern is that this happens every time. If this were a static file hosted by the web server, the web server would be smart enough to return an HTTP status code 304, meaning that the requested resource didn’t change since the last time it was requested.

How does this work? That’s relatively simple. When the resource is first requested, and the picture is returned, the web application should add a header to the HTTP response named “Etag”. This header contains a string that represents the “version” of the resource. This can really be anything.

When the client requests the same resource again, and the client has a cached copy of the resource, it sends a request containing an extra header named “If-None-Match”, meaning that if the resource still matches this version, it should return HTTP 304 and no content.

So how do we implement this?

For the version  information, I chose to place a timestamp column in the table containing the picture, and an associated property in the LINQ to SQL business object. The timestamp column type is not related to dates and times. It’s just a value that you can check for if you want to know if the record has changed in any way since you last read it. Which is perfect for this example.

So the first thing we need to do is send the version to the client. In order to keep this testable, I decided to implement it in a way that closely resembles this answer to my question on Stack Overflow. First of all, we need an interface that we can call from our Action method that deals with adding the tag and checking for it. This should do fine:

public interface ITagService
{
    string GetRequestTag();
    void SetResponseTag(string value);
}

When we add a property of this interface to our controller and initialize it in the constructor, we can decide what kind of implementation we use at runtime (whether that is the application hosted in IIS, or during testing). So we can use a mock for testing, and we can use the following class in IIS:

public class HttpTagService : ITagService
{
    public string GetRequestTag()
    {
        return HttpContext.Current.Request.Headers["If-None-Match"];
    }

    public void SetResponseTag(string value)
    {
        HttpContext.Current.Response.AppendHeader("ETag", value);
    }
}

So now our Picture action method looks like this:

[ReplaceMissingPicture(Picture = "~/Content/Images/nopicture.png", MimeType = "image/png")]
public ActionResult Picture(int id)
{
    var item = Service.GetItem(id);

    var responseTag = item.Version != null ? 
        Convert.ToBase64String(item.Version.ToArray()) :
        string.Empty;

    if (item.Picture != null)
    {
        var picture = item.Picture.ToArray();
        var mime = item.PictureMime;

        TagService.SetResponseTag(responseTag);

        return File(picture, mime);
    }
    else
    {
        return null;
    }
}

A simple ToBase64String() from our version column (which is mapped to a Binary object) should do just fine.

Before we go into how we’ll check for the tag (which is pretty trivial) we also need to know how to return the actual HTTP 304 status. The following needs to happen:

  • Response.SuppressContent must be set to true.
  • Response.StatusCode needs to be set to 304.
  • Response.StatusDescription needs a description.
  • Response needs a header “Content-Length” set to “0”.

This last one is important, as it will allow the client to keep the connection open for the next request while not expecting any more output from the current request.

In order to keep this testable, I chose to create a custom ActionResult called NotModifiedResult. Here’s the implementation:

public class NotModifiedResult : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        var response = context.HttpContext.Response;

        response.SuppressContent = true;
        response.StatusCode = 304;
        response.StatusDescription = "Not Modified";
        response.AddHeader("Content-Length", "0");
    }
}

This is exactly what we need, and ASP.NET MVC makes it easy again to keep this all neat and tidy. Our tests can simply check for the type of result returned, and ExecuteResult() never needs to be executed in a test environment.

So finally, our Picture action method looks like this:

[ReplaceMissingPicture(Picture = "~/Content/Images/nopicture.png", MimeType = "image/png")]
public ActionResult Picture(int id)
{
    var item = Service.GetItem(id);

    var requestTag = TagService.GetRequestTag() ?? string.Empty;
    var responseTag = item.Version != null ? 
        Convert.ToBase64String(item.Version.ToArray()) :
        string.Empty;

    if (responseTag == requestTag)
    {
        return new NotModifiedResult();
    }

    if (item.Picture != null)
    {
        var picture = item.Picture.ToArray();
        var mime = item.PictureMime;

        TagService.SetResponseTag(responseTag);

        return File(picture, mime);
    }
    else
    {
        return null;
    }
}

I’m sure this doesn’t need any explanation.

Two more things I’d like to mention. First of all, I decided to turn the Picture column into a delay loaded column by setting its Delay Loaded property to true in the LINQ to SQL designer. This way, when the table is queried, the column is not selected by default. Only when you access the picture column a new query is started specifically for the picture. This way, when you don’t need the picture (like in a table overview of the business objects) it’s not queried for and retrieved from the database. Also, this means that our action method requires two queries: one for the Item and one for the Picture column. Except that in the case of a client cached picture, the second query doesn’t need to be executed.

Second, I want you to know that this technique here doesn’t just apply to pictures, it applies to any sort of content that can be accessed as a resource. If the client can cache it, you can enable the web application to support that caching method.

Now all I need to do, is apply the same to my ReplaceMissingPicture filter that I talked about last time.