Welcome to Manicprogrammer Sign in | Join | Help

Meu primeiro post em Português – Venha workar na globo.com

Dear reader, if you don't understand this post that's alright. This post is in portuguese and aimed at Brazilians. Please disregard if you don't fit in those categories.

Caros leitores Brasileiros,

Você quer trabalhar com o melhor que a tecnologia pode oferecer?

Quer trabalhar em uma empresa que leva AGILE a sério no Brasil?

Quer trabalhar em uma empresa que vai investir na sua formação (acadêmica e eventos)?

Quer morar numa cidade maravilhosa (Rio de Janeiro)? (Totalmente Biased - Guilt as Charged)

E por fim, quer trabalhar em projetos desafiadores (maior portal do brasil)?

VENHA TRABALHAR COM A GENTE NA GLOBO.COM! (TOTALMENTE 0800-14-06)

Se você manja muito de um ou mais dos seguintes:

  • Agile Development
  • Test Driven Design
  • Web Development com Django ou outro framework web
  • Python, Ruby ou outra linguagem funcional (Lua, sei lá)
  • JQuery (de verdade, ok? Não "Eu fiz um alert legal pro JQuery")
  • Html/CSS (novamente, Hello World com tabelas não conta!)

ME MANDE E-MAIL bernardo at corp dot globo dot com, deixe um comentário aqui, escreva uma carta pra mim, mande uma mensagem telepática, pombo correio ou qualquer outro meio que você pensar, mas não deixe de falar comigo porque isso pode mudar sua carreira!

Espero comentários, e-mails, telepatia (you got my meaning!)...

Posted by heynemann | 1 Comments

Grape release of Pynq is out there!!!

Grape release of Pynq is out! You can grab it in the usual places (Installing Pynq).

I’ll go over all the details of this new release, but first I’d like to introduce the HOTTEST feature of this new release: DOCUMENTATION!

Pynq now has comprehensive (as much as I can think of anyway) documentation of all it’s features at this url: http://wiki.github.com/heynemann/pynq

 

This is really cool for people starting with Pynq.

Now let’s talk code!

Introduction

For those of you that are not familiar with Pynq yet I seriously advise you to check the wiki, but I’ll try to explain in as few words as possible.

Pynq is a Python implementation of Microsoft’s Linq (Language Integrated Query). We want it to be more than just a mere implementation of Linq for Collections in Python. We are going for a full featured expression-tree parser and provider infrastructure implementation. As such Pynq now features only the Collection Provider, but in release Mango (0.5.0) we’ll be doing a Twitter Provider as a means to demonstrate how to create a provider (I’ll blog about it later when we do it).

Querying

Since Pynq parses all your commands to expression trees it gives a LOT of freedom to the given Provider to do whatever it wants with it. This is where the real power behind Linq lies. You can’t forget, of course, the nice syntax it adds to your code. Where once you read:

http://gist.github.com/112032

Or something like that, now you'll read:

http://gist.github.com/112035

As you can see this is a much clearer code. The code gets a lot clearer once you start adding more to the query, like getting only a couple fields for instance:

http://gist.github.com/112040

The code is still clear, even though we have increased the complexity of our query A LOT!

New Release Features

This new release allows users to retrieve expressions as columns, instead of only being able to select columns. Here’s an example:

http://gist.github.com/112042

This makes it really easy to retrieve calculated columns on the fly, which can make the code a lot clearer (instead of looping through the collection to retrieve it).

To retrieve the calculated columns you use “dynamic_” and the index of the column in the select() method, like dynamic_4. This is not as cool as having an “as label” way of defining the name of the attribute. We might work it differently in the future. Suggestions are VERY WELCOME!

Another cool feature is sorting with expressions. Let’s say you did retrieve salary + bonus and now you want to order by greatest salary + bonus to lowest salary + bonus:

http://gist.github.com/112043

Even though the syntax is a little weird (the negative sign), think of it this way: order_by *ALWAYS* does ascendant order. By placing a negative sign before the order by expression you want, you are negating the order, thus inverting it. The parenthesis are REALLY important, because otherwise you would be doing negative salary + bonus ascending. Do not forget the parenthesis if what you want is a descending order.

Where to get it

You can get Pynq from:

  1. PyPI page at http://pypi.python.org/pypi/Pynq/
  2. Github Downloads at http://github.com/heynemann/pynq/downloads
  3. Through a debian repository. For more info on this check the installing page at http://wiki.github.com/heynemann/pynq/installing

Conclusion

Pynq is evolving pretty fast and we’ll keep adding more features in the upcoming releases.

Stay tuned and CONTRIBUTE!!!

Cheers!

Ps: sorry for the links to gists, but I couldn't make the god damn 'embed' feature of github to work with my blog!

Posted by heynemann | 1 Comments
Filed under , , ,

Pynq, Scribbler, Skink, PyoC and Pyccuracy

Introduction

Wow, that’s a big title.

In approximately 2 months of python coding I already collected 5 projects to manage and nurture.

Pyccuracy – The first one, a port of Stormwind Accuracy for .Net – a BDD Acceptance Testing framework.

PyoC – A port of the Windsor IoC container. This one really needs more nurturing, since it’s only been used by Pyccuracy and doesn’t implement all the features of the Windsor container.

Skink – An “as simple as it gets” build Server. Skink might be my best work while coding in Python. Heavily influenced by Integrity (A CI server in Ruby), using skink to build your own projects is a bliss. You can check skink building my projects at http://skink.skinkci.org.

Pynq – The best thing to ever come out of Microsoft is LINQ (Language Integrated Query). If you haven’t tried you are missing on one hell of a language improvement. Linq makes your code a lot more expressive and really helps on tedious repetitive tasks. As such I really missed it while coding in Python. I decided I had to implement my own Expression Tree version of Linq. I think I have been successful at such endeavor, considering how easy it was to implement a provider for collections that would parse the expression tree and apply it to a given collection.

Scribbler – At the company I work for right now we are considering the use of Selenium Grid to run our acceptance tests (since they are freaking slow). The main issue here is not actually using Grid, is running the tests in parallel, since there is no parallel test runner in Python out-of-the-box. So Scribbler is a test runner for python that parallelizes as many threads as you want to. The code for it is REALLY simple, so you should check it before using it to make sure it’s what you expect.

Where do I get those?

As usual from my github page at http://github.com/heynemann. Feel free to create issues in any of them if you feel like it, or to fork them and contribute to the projects.

What’s Next?

I’ll be blogging about each of those projects individually. All of them are at a point where I think they are useable, and as such, releasable.

Out of the five only Pynq and Scribbler haven’t had proper releases yet. But they are coming soon. Stay in touch!

Posted by heynemann | 1 Comments

Audio Acceptance Testing

What's more natural than speaking?

This is why Pyccuracy will now support audio BDD Acceptance Testing.

From version 0.5.0 onwards, you'll be able to just shout your tests at the test engine.

Just Shout it

Whenever you need a test done, use your voice and let all your peers hear it.

Let's use the example that Pyccuracy uses, searching Google.

Just download Pyccuracy trunk, fire pyccuracy_audio_server.py and start shouting in your microphone:

"Hey you, given that I'm in charge, when I go to google and search for 'Fool' then I see a lot of stuff"

Even though these actions seem a little generic, they were introduced to cather for audio support.

Neural Net

"Hey, how are you interpreting the audio commands?"

Ah ha! We have NeuroNet© 14.2 RC1 that evaluates what you say and translates that into a sequence of basic Pyccuracy Actions.

The logic behind what the neural net does is beyond the scope of this post.

Shouting to the Sky

What if you don't want to host your own audio infrastructure?

We have a MAJOR BREAKTHROUGH! Google is sponsoring Pyccuracy and now we have AUDIO SUPPORT IN THE CLOUD!

Don't believe me? Go to the nearest window and shout your test to the sky. Google Earth's servers will capture your test (but don't forget to shout your google account username and password, so they can authenticate you - we want it to be safe after all). Then go to Google Earth, to your location and watch the magic happening. The report for your test IS THERE!

I got a little worried about Google monitoring me in that level, but in the end they already have all my data, anyway. Might as well know everything I say.

BIG KUDOS TO GOOGLE for another inovative idea.

Conclusion

Pyccuracy keeps moving forward! Next step is sattelite-based peer-to-peer asynchronous synthetic testing! We'll get there soon! Thanks for the support people!

Posted by heynemann | 1 Comments

People behind the software

How many open source tools you use?

Cool, now of how many of those you actually know who’s behind them. By knowing I mean a face, a name and some bio on the person.

I expect the answer to be just a few or none, right?

Pyccuracy sets the example

Well you can increase your list if you use Pyccuracy. Just go to http://www.pyccuracy.org/projectinfo.html and check out the people behind Pyccuracy. You can even contact them if you feel like it. You’ll see that they won’t have any issues to reply if you are polite!

Well, *BIG KUDOS* to the team behind Pyccuracy for not being afraid to show who they are and why they are doing it!

Keep up the good job guys!

Bernardo Heynemann

Proud Pyccuracy Commiter

Posted by heynemann | 1 Comments

Pyccuracy Release 0.4 IS OUT!

After a lot of hardwork by the team, the new release is out!

Go ahead, try it, find what you don’t like and tell us.

Release Notes for 0.2, 0.3 and 0.4 (it’s worth mentioning that the docs for all of them are in www.pyccuracy.org as well):

Release Notes - Pyccuracy - Version 0.2 – 01/Mar/2009

Bug

  • [PYCCURACY-91] - Running tests in linux was killing the wrong process (selenium)

Improvement

  • [PYCCURACY-90] - Remove all infrastructure code that manages dependencies to IoC container

New Feature

Task

Release Notes - Pyccuracy - Version 0.3 – 15/Mar/2009

Bug

  • [PYCCURACY-100] - __main__ using invalid parameters
  • [PYCCURACY-101] - When the test fails, an exception is raised, polluting the result output
  • [PYCCURACY-104] - Make string actions explicit by prefixing with special character

New Feature

Release Notes - Pyccuracy - Version 0.4 – 22/Mar/2009

Bug

Improvement

New Feature

Conclusion

Just some statistics for you:

Total number of Tickets for Pyccuracy to date: 116 tickets

Closed tickets until release 0.4: 88 (76%)

Tickets still open: 28 (24%)

Open Tickets that are New Features: 22 (79% of the open tickets)

Open Tickets that are Improvements: 3 (11% of the open tickes)

Open Tickets that are Bugs: 1 (3% of the open tickets)

Open Tickets that are Administrative Tasks: 2 (7% of the open tickets)

Days between releases:

0.1 => 0.2 – 13 days

0.2 => 0.3 – 14 days

0.3 => 0.4 – 7 days

We are expecting to keep a tight iteration loop of 7 days, but 14 days is ok.

These numbers server to prove how hard the team behind Pyccuracy is working!

A BIG ROUND OF APPLAUSE TO ALL MEMBERS OF THE TEAM! YOU DESERVE IT!

Posted by heynemann | 1 Comments

Finding out if a given selector (XPATH, ID or Whatever) is Enabled in Selenium

I just spent a whole day trying to figure out how to define if a given element (defined using an xpath) is an enabled (i.e. not disabled) element in "Selenese", using Python bindings.

Finally cracked it:

def is_element_enabled(self, element):
  script = """this.page().findElement("%s").disabled;"""
   
  script_return = self.selenium.get_eval(script % element)
  if script_return == "null":
  is_disabled = self.__get_attribute_value(element, "disabled")
  else:
  is_disabled = script_return[0].upper()=="T"
   
  return not is_disabled

I know it's not pretty but after that many hours I DON'T CARE! I'm hoping someone will come and say: "There's a much easier way of doing that. Just use xxx" and then I'll fix it. For the time being it stays.

BTW http://www.pyccuracy.org is up and running and Pyccuracy got it's 0.3.1 release already.

Cheers,

Pyccuracy 0.2dev-r934 RELEASED!

Pyccuracy 0.2dev-r934 has been released. It has been a lot of work, but it’s done!

Release Notes - Pyccuracy - Version 0.2

Bug

  • [PYCCURACY-91] - Running tests in linux was killing the wrong process (selenium)

Improvement

  • [PYCCURACY-90] - Remove all infrastructure code that manages dependencies to IoC container

New Feature

Task

Conclusion

Grab the latest bits while they’re hot. As you can see the focus in this release was on new features (specifically new actions) and documentation. Now the updated documentation at http://packages.python.org/Pyccuracy/ features the an improved overview and kind-of a tutorial. Check it out.

As usual, just use easy_install pyccuracy and you’re good.

Hope it’s worth something to someone.

Posted by heynemann | 1 Comments

<frustrated>Am I that stupid?

God am I frustrated. It’s 3 am and I’ve been trying to setup a decent Python environment in Ubuntu since 10 am yesterday!

Well, I gotta say that I’m very well impressed with Ubuntu, though. It’s a pretty cool OS with an awesome user experience.

My problems start when I push a little harder than a regular user would.

Introduction

As you might have noticed from my previous posts I’m developing in Python now. In the company I’ll be working they are completely against not-open source products, which I find pretty cool.

As a result of that, I’ll be using Ubuntu (or any other distro for that matter) as my host OS. So I figured why not learn it before I start working there. That way I won’t be a burden on my team.

So I created a VM, installed Ubuntu 64 and started updating, installing programs, downloading SVN trunk for my projects and all that stuff you are familiar with.

The great news is that Ubuntu 64 comes with Python and Java installed. “GREAT”, I thought, I’m almost set.

Not so fast, buddy

My good friend Claudio Figueiredo told me that very likely the IDE I’ll be using at my new job is Eclipse with PyDev. I’ve had less than optimal experiences with Eclipse before, but hey, since I’m trying a hell of a lot of new things, I might as well try the new version.

It looks promising with code completion, code analysis and unit testing support baked in, so I did as the tutorial asked me to. Downloaded the latest release, unziped it in my personal folder, and opened Eclipse.

Cool, it works! How smart am I?!

Well, not all I need to do is install the plug-in. Followed the tutorial in PyDev and it installed FLAWLESSLY. At this point I’m feeling like a God (root). The next step is to create a PyDev project (Eclipse is all about workspaces and projects, go figure). So I create a project. It asks for what version of Python I’m aiming. Very smart! So I choose Python 2.5. Next it asks for the interpreter I want to use. Cool, except I don’t have any interpreters configured (which PyDev points to me in a nice message with a link to configure one). Ok, I missed that step. No problem. So I go to terminal and execute:

which python

That results in “/usr/bin/python” (not that much of a newbie, am I?).

Cool, so I set the interpreter to be /usr/bin/python. At this point PyDev gives me a nice message showing all the paths I would like to add to PYTHONPATH. Awesome, it recognized the Python version! I click ok and it recognizes all of Python 2.5 built-ins, which is really cool.

Dear reader, from here onwards is just a spiral of pain, agony and frustration. If you want to stop reading I won’t be mad at you.

Well, when I click OK, I get an error. The issue here is that Eclipse is so smart that it handles the Error Dialog for me, so I couldn’t see the error message. After a lot of digging, I got the error message. A NullPointerException in Java. Ok, google-digging time. After a lot of googling and a lot of dead-ends, Claudio advised me to install Ubuntu 32 instead, since it probably was a compatibility issue with 64 bits apps.

“Hmm that makes sense”, I thought. So after reinstalling a whole new OS in a new VM, and repeating and rinsing the above procedures, I STILL CAN’T GET THE FREAKING PYDEV THING TO WORK WITH THE SAME CRYPTIC ERROR MESSAGE!

Ok, I can live without Eclipse

I have lived before and I’ll still be doing it in the future. So let’s check what are my options as far as IDEs go.

Well, I have thoroughly researched Python IDEs before when doing Python coding in Windows. There are not as many as you’d think, considering the amazing language that Python is.

When doing Windows development I settled for Wing, which has a pretty decent editor (doesn’t even begin to compare to Visual Studio 2008 with Resharper – THERE I SAID IT!), code completion and project management. It has one small issue, though. It’s neither free nor open source. That’s ok, though, considering the Personal license costs mere 30 dollars. The professional one which offers some features I want goes for a hundred and something. I don’t care, if they offer a great experience I’d gladly pay for it. But my employer probably won’t, as a company policy. I’m not sure of that, though, and before people start talking, THIS IS MY VIEW, NOT THAT OF ANY OF MY EMPLOYERS, PREVIOUS, CURRENT OR FUTURE!

So I started digging options for IDEs for Ubuntu. One might think that I’m just lazy as far as IDEs go, and that’s absolutely right! I want to write production code. I don’t want to jump through hurdles just to get a breakpoint done, or have to remember every single command in the language, or even when renaming a variable go through every single place that references it to change it. Those things are productivity killers for me. I would pay a lot of money (at least a lot as far as my financial standards go) to have all that work done for me by the IDE.

So, one of two things happened. Either I am too dumb to use/find good Python IDEs for Ubuntu/Windows or there aren’t any. I’m REALLY hoping for the first one, because I might learn in time. If it’s the latter, hey I might start one and sell it (like I have the time for it, anyway).

So far I’m really really disappointed with the whole experience I got trying to setup an Ubuntu environment for Python development.

Wing

I´m downloading Wing for Ubuntu from Wingware. I really hope that my experience with it is better than what I got with the other ones.

Just for the sakes of comments, the other ones are:

- Eclipse (well couldn’t even try, since I can’t make it work)

-SPE (installed through Add/Remove Apps)

-Genie (installed through Add/Remove Apps)

-PIDA (installed through Add/Remove Apps)

-ERIC (installed through Add/Remove Apps)

-BOA (installed through Add/Remove Apps)

Conclusion

I know I have high standards as far as an IDE go. I know I’ll get flames for saying that Visual Studio is a great IDE from people saying that IntelliJ is a lot better. It might be, the guys at Jetbrains are THAT GOOD! If only they released a Python IDE! I’d buy without even trying it.

The thing is, I WANT TO CODE, not do boring tasks that the IDE can easily take care for me.

Oh and when people come and say that I’m not giving credit to those hard-working open source developers behind the above projects, that can’t be further from the truth! What I’m saying here is that I’m a professional developer, and as such I cannot settle for less than optimal performance. So if ANY, that’s right, ANY of the developers of the above products would like to talk about insight on how to improve their IDEs, I’d LOVE to talk (that’s if they want to listen what I have to say anyway, who am I to think they would ask me for feedback).

This post is actually just me ranting about being a newbie on the environment I am trying to setup. I’m pretty sure that people can code lightning fast with their VIM/Emacs/Notepad-like apps, except I’m not that cool, so I need an IDE.

Sigh…

</frustrated>

UPDATE:

Netbeans for Python has an alpha release and it's looking really good. It's the IDE I'm using for python coding now. It has some very nice features like reminding you of unused imports and stuff like that. The refactorings do not work as I expect, but I can live with that.

I hope it becomes a killer IDE! Go Netbeans guys, GO! Link: http://wiki.netbeans.org/Python

Posted by heynemann | 7 Comments

Pyccuracy 0.1.0

First of all I want to say that I’m sorry for the infamous name that I gave this project, but I simple couldn’t resist converting Accuracy to Pyccuracy (watch out for Accuruby, lol).

Anyway, the project is here. I got almost a version that could be released, even though it has only a handful of actions, thus not being able to properly test even the simplest of sites (I can only test Google so far!).

I won’t go into detail here but Pyccuracy (at least the first version, since it’s very loosely coupled) uses Selenium for underlying manipulation of browser instances. So more than half the glory goes to the amazing teams at ThoughtWorks for doing such a great job with Selenium-RC.

The Language

Since Python gives me much more freedom in defining a DSL than .Net does, I went for an external DSL to do the trick here. So far the DSL is defined in .acc files (from Accuracy, since .pyc is already used by python), such as:

As a Google User
I want to search Google
So that I can test Pyccuracy

Scenario 1 - Searching for Hello World
Given
    I go to "http://www.google.com"
When
    I fill "q" textbox with "Hello World"
    And I click "btnG" button
Then
    I see "Hello World - Pesquisa Google" title

Scenario 2 - Searching for Monty Python
Given
    I go to "http://www.google.com"
When
    I fill "q" textbox with "Monty Python"
    And I click "btnG" button
Then
    I see "Monty Python - Pesquisa Google" title

This test starts Selenium Server, uses Selenium R/C to navigate a browser (defaults to Firefox) to http://www.google.com, fills a textbox with the name of “q” with the text “Hello World”, clicks a button called “btnG” and checks that the title for the browser instance is “Hello World – Google Search”, after the next page (after the click) finishes loading. The second scenario is analogous.

This may seem like a trivial thing, but it’s got immense possibilities! This is how you run your tests:

pyc = Pyccuracy()
pyc.run_tests()

How about running tests for more than one browser?

pyc = Pyccuracy()
pyc.run_tests(browsers=(“iexplore”, “firefox”))

Want to use a different root folder for your tests?

pyc = Pyccuracy()
pyc.run_tests(root="/path/to/test/files”)

And so on, and so forth.

The great advantage that this technique has over the .Net cousin is that due to it’s easy nature of parsing the external files, it turns out to be REALLY easy to include new actions and to evolve the DSL.

Current Situation

We already have a google groups for the project at http://groups.google.com/group/pyccuracy (Fixed thanks to Chris Read!). If you want to help feel free to join the group and comment on discussions or suggest anything.

At the google groups page there’s also a cheat sheet of urls for the project, including the SVN repository and the JIRA tickets server. JIRA is a great way to see what’s coming next.

My good friend Claudio Figueiredo will create a Pypi project (egg) soon, and when we release this version you can easy_install it.

So far that’s it!

Cheers,

Posted by heynemann | 4 Comments

Django Gotchas

Hey guys, I´m in Python land now! Doing Django development! Thought I’d share some gotchas I got. I’m reading the very good book “The Definitive Guide to Django – Web Development Done Right”, and since it’s a little outdated I got some issues that I thought I´d show how to solve.

Urls.py for Admin module

The book says you should use this code for the urls portion:

from django.conf.urls.defaults import *
urlpatterns = patterns('',
    (r'^admin/', include('django.contrib.admin.urls')),
)

The issue here is that this is not the right pattern anymore (not sure if it ever was). The right code now would be:

from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
    (r'^admin/(.*)', admin.site.root),
)

The problem is that after fixing that I got another error:  “'WSGIRequest' object has no attribute 'user'”. Onto that error.

WSGIRequest object has no attribute user

The problem here is that following the book exercises, the book told me to comment middleware apps, which I did. It just failed to tell me to uncomment them. Since the message is pretty unclear, I decided to have it here in my blog so it gets indexed by google and people find it.

If you got this message just uncomment (or include) the following in your settings.py file:

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
)

Models with ImageField

If you get a SuspiciousOperation Exception like I did a likely cause is that:

a. The folder does not exist

b. You used a path that starts with a slash, like “/tmp”. Remove the slash, like “tmp”.

Conclusion

Til’ next issues, guys!

Cheers

Bernardo Heynemann

Posted by heynemann | 2 Comments

Forms, Repeatable Items, MVC, Validation, JSON – Phew, I got it!

Introduction

Following my last post, I want to introduce what I was really working in. A multi-item templated client-side mechanism. Fancy name, huh?

The idea is that in a form we have some single-value fields and some multi-value fields. I need both to be mapped to my DTO. That way in my Action all I have to do is map from the DTO to domain entities and save.

Let’s assume as a DTO the following classes:

    public class Process
    {
        public Process()
        {
            Companies = new List<Company>();
            Customers = new List<Customer>();
        }
 
        public string Number { get; set; }
        public IList<Company> Companies { get; set; }
        public IList<Customers> Customers { get; set; }
    }
 
    public class Customer
    {
        public int Id { get; set; }
    }
 
    public class Company
    {
        public int Id { get; set; }
    }

So the UI I want is somewhat like this:

(courtesy of Balsamiq Mockups – definitely worth buying)

The behavior of this UI is:

1) When the page first loads there are no companies or customers.

2) Whenever you click add company one row is added.

3) Whenever you click add customer one row is added.

4) When the user clicks Create, the form needs to be posted to an action that takes an object of type Process.

5) If the process passes validation (below), an alert should be displayed to the user.

6) If the process fails validation (below), the error messages should show in a div for the user above Process Number.

7) The validations are:

7.1) The process number needs to be filled.

7.2) At least one company must be added.

7.3) At least one customer must be added.

Preferably all of that should happen without the screen flickering (post), thus using AJAX for it.

Challenges

Ok, sounds easy, but let’s analyze some challenges imposed by this problem.

First there’s how to easily have the multi-row thing. The structure that adds new companies or customers and properly names them so they get properly bound in the MVC action. There’s another catch here. I want this to be templateable, meaning I want to use the same infra-structure for both areas of the form, just change the row template (and button text).

Then there’s the form binding (explored in my last post) so that I can save the information provided by the user. This is not trivial since the fields should be named properly, like process.Customers[0].Id and such.

Last but not least there’s the validation which should be executed using ajax. If validation passes we should show a success message.

Let’s tackle each of the challenges individually.

Multi-Value Fields

As I said before when I want to develop something for my own usage, I first try to find out how I want to use it and only then I try to build it.

So let’s see how would be the ideal way of creating this multi-value fields.

<div 
    class="repeater" 
    additemtext="Add Company" 
    itemTemplateSelector="#company_item_template">
</div>

That sounds good, now let’s check the template called company_item_template:

    <div id="company_item_template" style="display: none;">
        <table border="0" cellspacing="0" cellpadding="0" width="100%">
            <tr>
                <td>
                    Company:
                </td>
                <td>
                    <select name="Process.Companies[{index}].Id">
                        <option value="1">Company A</option>
                        <option value="2">Company B</option>
                        <option value="3">Company C</option>
                        <option value="4">Company D</option>
                        <option value="5">Company E</option>
                    </select>
                </td>
            </tr>
        </table>
    </div>

As you can see that’s all standard HTML, except for {index}. What do I mean by {index} is the row number, which is pretty obvious. This number must be filled by the client-side component so that the MVC binding can occur properly.

Ok, now that I know what I want, let’s go to how to do it.

JQuery Components

There’s a very good intro on how to do JQuery components here. I won’t dive in that subject. Suffice to say that you can extend JQuery in a way that allows you to add new functions to the JQuery Element. This allows us to enable the user to do:

$(‘div.repeater’).toRepeatable();

The code for the component is pretty self-explanatory, but if anyone wants any help with it, just leave a comment:

var NO_ITEM_TEMPLATE_ERROR_MESSAGE = 'No item templates were specified, please include an attribute with the item template element selector called \'itemTemplateSelector\' in the repeatable div!';
 
var repeatable_layout = '<table border="0" cellspacing="0" cellpadding="0" width="100%">';
repeatable_layout += '  <tr>';
repeatable_layout += '    <td align="right">';
repeatable_layout += '      <button class="btnAddItem">{buttonText}</button>';
repeatable_layout += '    </td>';
repeatable_layout += '  </tr>';
repeatable_layout += '  <tr>';
repeatable_layout += '    <td>';
repeatable_layout += '      <div class="repeaterItems"></div>';
repeatable_layout += '    </td>';
repeatable_layout += '  </tr>';
repeatable_layout += '</table>';
 
 
jQuery.fn.toRepeatable = function(options) {
    $(this).each(function() {
        var div = $(this);
        transformDivToRepeater(div, options);
        bindToEventsIn(div, options);
    });
 
    function transformDivToRepeater(div, options) {
        var repeaterBody = (options != null && options.layout == null) ? options.layout : repeatable_layout;
        var addItemText = div.attr('addItemText').toString();
        repeaterBody = repeaterBody.replace("{buttonText}", addItemText == '' ? "Add" : addItemText);
        div.append(repeaterBody);
    }
 
    function bindToEventsIn(div, options) {
        var btn = div.find('button.btnAddItem');
        btn.click(function() {
            if (div.attr('itemTemplateSelector') == null || div.attr('itemTemplateSelector') == '') { alert(NO_ITEM_TEMPLATE_ERROR_MESSAGE); return; }
            var item_template_selector = div.attr('itemTemplateSelector').toString();
            var item_template_object = $(item_template_selector);
            if (item_template_object == null || item_template_object.length == 0) { alert(NO_ITEM_TEMPLATE_ERROR_MESSAGE); return; }
 
            var itemCount = numberOfItemsFor(div);
            incrementItemCountFor(div);
            var itemTemplate = item_template_object.html();
            var itemsDiv = div.find('div.repeaterItems');
            itemsDiv.append('<div class="item' + itemCount + '"></div>');
            var currentItemDiv = itemsDiv.find('div.item' + itemCount.toString());
            currentItemDiv.append(replaceCountsIn(itemTemplate, itemCount));
 
            return false;
        });
    }
 
    function replaceCountsIn(itemTemplate, itemCount) {
        while (itemTemplate.toString().indexOf('{index}', 0) > -1)
            itemTemplate = itemTemplate.toString().replace('{index}', itemCount.toString());
 
        return itemTemplate;
    }
 
    function numberOfItemsFor(div) {
        return parseInt(div.attr('numberOfItems'));
    }
 
    function incrementItemCountFor(div) {
        if (div.attr('numberOfItems') == null || div.attr('numberOfItems') == '')
            div.attr('numberOfItems', 1);
        else
            div.attr('numberOfItems', parseInt(div.attr('numberOfItems')) + 1);
    }
};

Ok, one part down. I can do multi-value fields now. Let’s get to the binding portion.

Binding to the DTO

As outlined in my last post, the ASP.Net MVC model binder is not fully-functional. Again as I said the Monorail one is. My dear friend Claudio Figueiredo just pointed that I CAN use Castle binder. There’s an excellent blog post by Steve Gentile here on how to use it.

Suffice to say that it works as I expected and maps all my fields properly. The strategy I adopted is to have a BindingController as base class that does all the required infra-structure stuff.

Validation and Saving

All that’s left is validating and saving. I’ll first show the code for my Create action.

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create([CastleBind] Process process)
        {
            var errors = new List<string>();
            if (string.IsNullOrEmpty(process.Number))
                errors.Add("The process number must be filled!");
            if (process.Customers.Count == 0)
                errors.Add("At least one customer must be selected!");
            if (process.Companies.Count ==0)
                errors.Add("At least one company must be selected!");
 
            var data = new ResultJson("Success");
            if (errors.Count > 0)
            {
                data.Errors = errors;
                data.Result = "Failure";
            }
            var json = new JsonResult {Data = data};
            return json;
        }

As you can see if any errors happen I set the result to Failure and add the error messages. Whatever happens I return a JSON representation of the result object. The JsonResult object is just a POCO:

    public class ResultJson
    {
        public ResultJson(string result)
        {
            Result = result;
        }
 
        public string Result { get; set; }
 
        public List<string> Errors { get; set; }
    }

What happens here is that whoever calls this action will get a JSON object as the result so that the validation errors can be shown to the user. How to do it, though?

We’ll use the JQuery.Forms plugin to post our form using AJAX. That way we can inspect the return. There’s a catch, though. We need to instruct JQuery Forms to interpret the result as JSON. This can be easily done using the following code:

    var options = {
        dataType: 'json',
        success: function(result) {
            if (result.Result == 'Failure') {
                $('#errors').html('');
                for (i = 0; i < result.Errors.length; i++) {
                    $('#errors').append('<div class="errorMessage">' + result.Errors[i] + '</div>');
                }
            }
            else {
                alert('Process saved successfully.');
            }
        }
    };
    $("#form").ajaxForm(options);

The code above checks if the result is a failure, and if it is appends error messages to a div called “errors”.  Otherwise just gives an alert that that process was saved.

Of course you could return more data in the result and redirect to another page or anything that you need. This is only a proof-of-concept as usual.

Conclusion

Using these structures, it’s really easy to have a complex form validated and saved in a very simple controller. What could turn into a hell hole can be solved with very very little UI Code, thus relying in the domain and strengthening the notion of Domain-Driven design.

I hope this code helps someone out there! Anyway, any doubts, suggestions, anything, just let me know!

Cheers,

Posted by heynemann | 1 Comments

Asp.Net MVC Model Binding

Introduction

I´m doing a JQuery based component for dynamic rows using Asp.Net MVC.

What that means is that the following HTML would get generated:

...
<tr>
  <td>
    <input type="text" name="model.Items[0].Name" />
  </td>
  <td>
    <input type="text" name="model.Items[0].Description" />
  </td>
</tr>
<tr>
  <td>
    <input type="text" name="model.Items[1].Name" />
  </td>
  <td>
    <input type="text" name="model.Items[1].Description" />
  </td>
</tr>
...

What that means is that I need that code to be translated to a model object with two items featuring name and description in my action. Something in the lines of:

public class Model
{
  public IList<SubModel> Items { get; set; }
}
 
public class SubModel
{
  public string Name {get;set;}
  public string Description {get;set;}
}

Binding to Models in ASP.Net MVC – Flawed?

My first guess was to use DefaultModelBinder like this:

public ActionResult Save([ModelBinder(typeof(DefaultModelBinder))]Model model)
{
  //do something with model.
}

You can imagine my surprise when I found out that it does not work. Coming from MonoRail I might’ve been spoiled, but I really expected it to work seamlessly. That’s when I learned that if you want advanced binding in ASP.Net MVC you have to do it yourself.

“What? Implement one binder for each model? Then get the values from the Request MYSELF??? You gotta be kidding me?” – exactly my thoughts dear reader. Thus I implemented a binder that does exactly what the above says. The only issue with it is that it only uses Request.Form values for the List Items values. That can be changed if need be. I didn’t, so I didn’t bother. The code for the advanced binder is as follows (as usual it is “as-is” code and please don’t give me annoying criticism on quality of code since this is a proof-of-concept):

    public class AdvancedModelBinder : IModelBinder
    {
        public ModelBinderResult BindModel(ModelBindingContext bindingContext)
        {
            var result = Activator.CreateInstance(bindingContext.ModelType);
            BindPropertiesToResult(bindingContext.ModelType.Name, result, bindingContext);
            return new ModelBinderResult(result);
        }
 
        private void BindPropertiesToResult(string path, object result, ModelBindingContext bindingContext)
        {
            if (DepthCountFor(path) > 10) return;
            var properties = result.GetType().GetProperties();
            foreach (var property in properties)
            {
                var propertyValue = ReflectionUtils.IsList(property) ? GetListValuesFor(path, property, bindingContext) : GetValueFor(path, property, bindingContext);
                if (propertyValue != null) property.SetValue(result, propertyValue, null);
            }
        }
 
        private object GetListValuesFor(string path, PropertyInfo property, ModelBindingContext context)
        {
            var newList = ReflectionUtils.CreateListFor(property.PropertyType);
            var propertyPath = path + "." + property.Name + "[";
            if (!PathExists(propertyPath, context)) return null;
 
            var maxIndex = -1;
 
            foreach (string key in context.HttpContext.Request.Form.Keys)
            {
                if (!key.ToLowerInvariant().StartsWith(propertyPath.ToLowerInvariant())) continue;
                int index;
                var numericProperty = key.ToLowerInvariant().Replace(propertyPath.ToLowerInvariant(), string.Empty);
                var indexOf = numericProperty.IndexOf("]");
                if (indexOf == -1) continue;
                numericProperty = numericProperty.Substring(0, indexOf);
                var isNumeric = Int32.TryParse(numericProperty.Replace("]", string.Empty), out index);
                if (isNumeric && index > maxIndex)
                {
                    maxIndex = index;
                }
            }
 
            AddItemsToList(path + "." + property.Name, ReflectionUtils.GetListType(property.PropertyType), maxIndex, newList, context);
 
            return newList;
        }
 
        private object GetValueFor(string path, PropertyInfo property, ModelBindingContext context)
        {
            var propertyPath = path + "." + property.Name;
            if (!PathExists(propertyPath, context)) return null;
 
            if (!property.PropertyType.IsClass || property.PropertyType.Equals(typeof(string)))
            {
                var value = context.ValueProvider.GetValue(propertyPath);
                return value!=null ? value.AttemptedValue : null;
            }
 
            var newObject = Activator.CreateInstance(property.PropertyType);
            BindPropertiesToResult(propertyPath, newObject, context);
            return newObject;
        }
 
        private void AddItemsToList(string path, Type itemType, int index, object newList, ModelBindingContext context)
        {
            for (var i = 0; i <= index; i++)
            {
                var obj = Activator.CreateInstance(itemType);
                BindPropertiesToResult(string.Format("{0}[{1}]", path, i), obj, context);
                ReflectionUtils.InvokeMethodOn(newList, "Add", obj);
            }
        }
 
        private bool PathExists(string path, ModelBindingContext context)
        {
            foreach (string key in context.HttpContext.Request.Form.Keys)
            {
                if (key.ToLowerInvariant().StartsWith(path.ToLowerInvariant()))
                    return true;
            }
            return false;
        }
 
        private int DepthCountFor(string path)
        {
            return path.Count(c => c == '.');
        }
    }
 
    public static class ReflectionUtils
    {
        public static bool IsList(PropertyInfo property)
        {
            var propertyType = property.PropertyType;
            return propertyType.IsArray ||
                   propertyType.FullName.StartsWith(typeof(IList<>).FullName) ||
                   propertyType.IsSubclassOf(typeof(List<>)) ||
                   propertyType.GetInterfaces().ToList().Contains(typeof(IList<>));
        }
 
        public static object CreateListFor(Type type)
        {
            var listType = typeof(List<>);
            var genericType = listType.MakeGenericType(GetListType(type));
            return Activator.CreateInstance(genericType);
        }
 
        public static Type GetListType(Type listType)
        {
            return listType.GetGenericArguments()[0];
        }
 
        public static object InvokeMethodOn(object obj, string methodName, params object[] parameters)
        {
            var methodInfo = obj.GetType().GetMethod(methodName);
            return methodInfo.Invoke(obj, parameters);
        }
 
        public static object GetPropertyValue(object obj, string propertyName)
        {
            var property = obj.GetType().GetProperty(propertyName);
            return property.GetValue(obj, null);
        }
    }

Conclusion

This new model binder can be used like this:

public ActionResult Save([ModelBinder(typeof(AdvancedModelBinder))]TestModel testModel)

This will enable the scenario that I need, I´m just sharing with whomever might need it.

Hope it helps! Cheers,

Posted by heynemann | 6 Comments
Filed under ,

Plans for 2009

I think that a post is due, and the time of the year couldn’t be better.

Personal News

Well, a lot has changed in my life in 2008. Usually I don’t give personal details about my life here in my blog, since I tend to use this space for my tech ramblings.

I want to take a moment to talk about my amazing job at ThoughtWorks. I left the company this December. I stayed there only for an year and still I’ll always remember what a great company it is. If you live in one of the countries where they are and you think you’re up for the job go for it. They rock!

Why did I leave then? Because me and my wife had a very hard time in London. We missed home a lot, our families, friends…

I´m back in Brazil now (in sunny Rio de Janeiro). Still unemployed, which means that now is the time for your company to get a new agile tech lead / developer. I´m eager to work with .Net as well as new technologies and would love to share the expertise I got in my experience in the UK with ThoughtWorks with my new employer.

Stormwind

I haven’t been THE most active committer in Stormwind right now, since I just moved continents, so a lot to do. I think I got most of the stuff figured out now, so I’ll probably restart coding soon.

Some updates:

Stormwind.Accuracy – This is one of the most rewarding endeavors I’ve ever been in. A great project, amazing results using it in our last project. The codebase has evolved A LOT and our last release is completely production ready. If you need or would like to know a more on how to easily write acceptance tests that are not brittle, just check this project.

Stormwind.PowerFixtures - I´ve been using this in some tests to generate test data and I´ve grown quite fond of it. I have plans of expanding this. It already supports data persistence for test data generation.

Stormwind.Accord – Following my last post, I need to get a project going so people can start to contribute and use it. The idea behind this project was very well received by the people I spoke to. I got a lot of feedback and plan on incorporating that.

Stormwind Website – I know, I know. Our website needs to improve drastically. I decided to throw away the whole portal idea and stick to a simple website that I can update as needed. I´m still thorn between a static or dynamic website. We´ll see. But things will change.

Personal Projects

My first and by far the largest project is the book I´m writing on reducing friction in software projects. The first chapter is behind me, even though there are (at least) ten chapters on the way. I’ll keep updating this project here in the blog.

The other is some articles for magazines I’m trying to get done. This is very good in that it forces me to think about work. To find patterns and anti-patterns and to write about them. Or just demo some nice technology. Or showcase some cool project. You know, the works.

Conclusion

The conclusion is that 2009 is coming, it’s going to be great and it’s going to be huge.

If you own or work in a company in Rio de Janeiro or São Paulo, Brazil and need an agile tech lead for .Net technologies (or any other interesting one that I can learn), just let me know! Leave a comment here with your e-mail or e-mail me at heynemann at gmail.

Hope to hear some good news soon!

Business Rules are tough to read! Are we in Accordance?

Hey, catchy title, huh? Before the flame wars start, I'm not saying that all business rules are poorly written, nor am I saying that I came up with a magic solution. Before I even begin talking about my idea let me give you some background.

Background

In my previous project, the domain was fairly complex for 3 different communicating applications. As it happens in any line-of-business application, the business rules had several conditions to be met before they could be executed. What I mean with conditions is pre-requisites for the rule being run.

There are some possible problems with that approach (all of which I could identify in the 3 different codebases we had):

  1. The code is harder to read, since it's very nested (think if's, else's, switches or any other branching mechanism) and intertwined with calls to methods in different classes. This, eventually makes the actual business rules behind a given service call impossible to decipher for most of the original team members, not to mention for new people joining.
  2. Unit testing these rules is REALLY hard, since you have to come up with all sorts of different scenarios that can match all the possible combinations of pre-requisites and results. This, in turn, results in less tests and "coverage mislead"*.
  3. Makes the service classes (or whatever classses you put your business rules in) really big, really fast, since all the branches are in the same file. This has diverse negative effects like:
    1. Hard to parse files if you use tools like resharper. You could split this into more files, but that just makes item 2 worse.
    2. Hard to understand files. Small files are easier for us to read then big ones. If I want a book, I'll go read one.
    3. Yuck effect. Related to 3.2. This is the general feeling of someone joining the project. I consider morale to be one huge aspect of every agile team. Having huge files makes developers want to cry, thus hurting the team morale pretty bad. People already in the team feel bad about it and people joining feel bad about complaining (not me though, lol).

As you can see, these are some pretty serious issues. I haven't worked in a single project where this does not repeat itself, which is weird. I consider this to be my fault for not noticing these issues earlier, or failing to take action to prevent that.

What annoys me the most is that I only acknowledged all of that after reading this very good post by Ayende (http://ayende.com/Blog/archive/2008/11/23/aspects-of-domain-design.aspx). Anyway, better late then never.

How to prevent it

Ok, now that we know some of the issues, I want to think about what are business rules and what are they composed of. No, I'm not starting a business rule farm and wondering what they eat! I just need to think of how we can split them in a way that's intuitive, readable and testable.

From what I gathered, business rules are made of:

  • Requirements
  • Data Manipulation

"Well, duh! Why am I even reading this?! Everyone knows that!"

Yeah I know, dear reader, but sometimes you have to look at the obvious concepts to acknowledge that you need to dig a little deeper. With that in mind, let's define both concepts.

Requirements are pre-conditions that need to happen in order for a given business rule to be executed. These are really common and tend to change fairly often as the project evolves.

Data Manipulation are actions that change application state like ORM calls, database calls and whatever else changes any state in the application or the domain.

"Still stating the obvious... Boooring..."

Ok, calm down! I'm getting there! Given that we agree with these two definitions, what do you say we split them?

"Uh?"

Yes, let's get rid of all the requirements in our business rules and dump them somewhere else. That way our business rules can stick to what they should be doing, changing state.

"Wait, I heard that before. Please say it's not another Business Rules Engine! I don't even want to read any further!"

Please keep reading! I swear it's not just another business rules engine. Bear with me just for a while!

Getting rid of Requirements - Learning by Example

I do believe that the best way of learning something is by example. Sometimes it's very hard to grasp some concepts just by themselves. Having an example really helps. So let's get one going.

Let's assume that we have this service:

The business rules requirements are:

  • For Debit (takes an account and an amount)
    • The account needs to be active
    • The amount being debited must be lower than the amount in the account (you can't take more money than you have)
  • For Transfer (takes from account, to account and amount)
    • Both accounts need to be active
    • The amount being transferred must be lower than the amount in the from account (due to the rule that you can't take more money than you have)
    • The amount to be transferred must be greater than 10 (we are ignoring currency for this example).

Pretty simple rules, huh? Let's check at how an implementation of this would be:

   1:      public class ClassicAccountManagementService : IAccountManagementService
   2:      {
   3:          public void Debit(Account account, decimal amount)
   4:          {
   5:              if (account.AccountStatus!=AccountStatus.Active)
   6:                  throw new InvalidOperationException("The account must be active in order for an operation to be performed.");
   7:              if (amount > account.Amount)
   8:                  throw new InvalidOperationException("The account does not have enough money for a debit operation.");
   9:   
  10:              account.Amount -= amount;
  11:          }
  12:   
  13:          public void Transfer(Account from, Account to, decimal amount)
  14:          {
  15:              if (from.AccountStatus != AccountStatus.Active || to.AccountStatus != AccountStatus.Active)
  16:                  throw new InvalidOperationException("The account must be active in order for an operation to be performed.");
  17:              if (amount > from.Amount)
  18:                  throw new InvalidOperationException("The account does not have enough money for a debit operation.");
  19:              if (amount < 10)
  20:                  throw new InvalidOperationException("To transfer money between accounts the minimum amount is 10.");
  21:   
  22:              from.Amount -= amount;
  23:              to.Amount += amount;
  24:          }
  25:      }

You can easily see that lines 5-8 and 15-20 are requirements for the rules and lines 10 and 22-23 are the data manipulation actions for the rules.

How about we change that to read like this:

   1:      public class AccountManagementService : IAccountManagementService
   2:      {
   3:          public void Debit(Account account, decimal amount)
   4:          {
   5:              account.Amount -= amount;
   6:          }
   7:   
   8:          public void Transfer(Account from, Account to, decimal amount)
   9:          {
  10:              from.Amount -= amount;
  11:              to.Amount += amount;
  12:          }
  13:      }

Hey, that looks good! Except it does not meet our criteria for the business rules. We would have a lot of failing tests with this. I still like it a lot, though.

Specifying Requirements

Usually when I'm trying out some new idea I tend to write code that matches how I would like to use it, then make it work. Sometimes I find some spikes down the road, but usually this approach works for me.

Let's write the code for our service the way I think I would like to read:

   1:      public class AccountManagementService : IAccountManagementService
   2:      {
   3:          [IsSatisfiedBy(AccountIsActive.Requirement)]
   4:          [IsSatisfiedBy(AccountHasEnoughMoneyForDebit.Requirement)]
   5:          public void Debit(Account account, decimal amount)
   6:          {
   7:              if (Context.Failed) return; //means some of the requirements were not met. Could throw instead.
   8:   
   9:              account.Amount -= amount;
  10:          }
  11:   
  12:          [IsSatisfiedBy(FromAccountIsActive.Requirement)]
  13:          [IsSatisfiedBy(ToAccountIsActive.Requirement)]
  14:          [IsSatisfiedBy(FromAccountHasEnoughMoneyForDebit.Requirement)]
  15:          [IsSatisfiedBy(TransferMustMeetMinimumValue.Requirement)]
  16:          public void Transfer(Account from, Account to, decimal amount)
  17:          {
  18:              if (Context.Failed) return; //means some of the requirements were not met. Could throw instead.
  19:   
  20:              from.Amount -= amount;
  21:              to.Amount += amount;
  22:          }
  23:      }

Now, that's a lot closer to what I'd like to read. Some clear advantages of this code:

  1. It's easier to read. You can clearly see what each rule does and at the same time know all the pre-requisites that need to be in place for this rule to be executed properly. What this means is that when you get back to this code 6 months from now (and if you don't someone will), you can still easily understand what it does and why.
  2. This code is a lot more testable. Writing simple code pays off when you have to test it. Since I abstracted all the requirements to the business rule, I got left only the Data Manipulation actions. What that means is that my unit tests are as simple as checking that when executed, this method changes the data in the expected way.
  3. Another point for testability. Since each requirement has been abstracted to its own class, I can test them in isolation, making writing scenarios for them a lot simpler.

Let's see the actions now:

   1:      public class AccountIsActive : IRequirement
   2:      {
   3:          public const string Requirement = "AccountIsActive";
   4:   
   5:          public void Execute(Account account)
   6:          {
   7:              if (account.AccountStatus != AccountStatus.Active)
   8:                  Context.FailWith("The account must be active in order for an operation to be performed.");
   9:          }
  10:      }
   1:      public class AccountHasEnoughMoneyForDebit : IRequirement
   2:      {
   3:          public const string Requirement = "AccountHasEnoughMoneyForDebit";
   4:   
   5:          public void Execute(Account account, decimal amount)
   6:          {
   7:              if (amount > account.Amount)
   8:                  Context.FailWith("The account does not have enough money for a debit operation.");
   9:          }
  10:      }
   1:      public class TransferMustMeetMinimumValue : IRequirement
   2:      {
   3:          public const string Requirement = "TransferMustMeetMinimumValue";
   4:   
   5:          public void Execute(decimal amount)
   6:          {
   7:              if (amount < 10)
   8:                  Context.FailWith("To transfer money between accounts the minimum amount is 10.");
   9:          }
  10:      }

As you can see the actions are pretty simple and very, very easy to test. Now that we got all of them in place, finally, I'll explain my idea.

Wiring it all together

Whenever I start a new project the first infrastructure component I setup is the IoC container. There's a lot of friction involved in managing components dependencies, and I want all of that to stay the hell away from my production code.

Since my tool of choice is Windsor, it won't surprise you that the only container I'm integrating with so far is Windsor. The structure I built, as you'll see, is decoupled enough that it should be straightforward writing the other container's integration.

Let me show a test that checks that you can't transfer money from one account to another because the from account is not active.

   1:          [Test]
   2:          public void ShouldNotTransferBetweenAccountsSinceFromAccountIsInactive()
   3:          {
   4:              AchloraContext.ThrowOnError = false;
   5:              var expectedError = new System.Collections.Generic.List<string> { "The account must be active in order for an operation to be performed." };
   6:              var from = new Account(500, AccountStatus.Inactive);
   7:              var to = new Account(500, AccountStatus.Active);
   8:   
   9:              var service = IoC.Resolve<IAccountManagementService>();
  10:   
  11:              service.Transfer(from, to, 200);
  12:   
  13:              Assert.That(AchloraContext.Failed, "It should've failed since the from account is not active.");
  14:              Assert.That(from.Amount, Is.EqualTo(500.00M));
  15:              Assert.That(to.Amount, Is.EqualTo(500.00M));
  16:              Assert.That(AchloraContext.Errors, Is.EquivalentTo(expectedError));
  17:          }

Let's tackle each line at a time.

At line 4 we have AchloraContext. So far I haven't talked about a project have I? Yeah I have. In the title. The project name is Stormwind.Accord (I told you it was catchy!), codenamed Achlora (just for the proof of concept). This context class is responsible for letting our methods know about what happened with the requirements. This is really useful if you don't want to throw automatically, or you need to inspect the results of the requirements.

In lines 5 to 11, we setup some test data and call our service method passing the data.

From lines 13 to 16 we assert that the requirements were executed properly and that no data was changed due to requirements not being met.

Well, moving on...

Windsor Integration

"WAIT JUST ONE SECOND THERE!!!"

What? What's going on?

"How the hell did those requirements get executed in the Transfer method? I didn't see any calls to Requirement.Execute!"

Oh, sorry, my bad! I forgot to explain that since I really like Windsor, we are fully integrated to it and because of that we intercept your calls (if you added any IsSatisfiedBy attributes). The requirements then get executed before executing your method. That's how you get your nice context in the method.

The only requirements for all this magic is that you register a facility in the container and all the requirements in the container.

"Why would I want to register requirements in the container?"

Hmmm... So the container can manage their dependencies as well? Say you have a requirement that needs to query some repository on some database stuff (pretty common, huh?). If your requirement is resolved from the container, all you have to do is inject the repository into your requirement, and off you go.

Just as a sample, this is the code that sets up the container for the tests that I've written for Accord:

   1:          [TestFixtureSetUp]
   2:          public virtual void TestFixtureSetUp()
   3:          {
   4:              container = new WindsorContainer();
   5:              container.AddFacility(WindsorIntegrationFacility.Key, new WindsorIntegrationFacility());
   6:              container.Kernel.Register(AllTypes
   7:                                            .FromAssembly(typeof(AchloraTest).Assembly)
   8:                                            .BasedOn<IService>()
   9:                                            .WithService.FirstInterface());
  10:   
  11:              container.Kernel.Register(AllTypes
  12:                                            .FromAssembly(typeof(AchloraTest).Assembly)
  13:                                            .BasedOn<IRequirement>()
  14:                                            .Configure(registration => registration.Named(GetKeyFor(registration.ServiceType))));
  15:          }

As you can see, not that hard to setup, now is it?

Conclusion

There's a lot more than just that, since we want to reuse rules (why have an IsAccountActive, IsFromAccountActive, IsToAccountActive, when we can have just AccountIsActive and just call the right arguments). That is implemented with parameter mapping as you can see in the test cases for the tool.

If you'd like to take a look, it's in my personal area right now under the codename Achlora. The project is completely self-contained, meaning you can just download it and try it. As usual the code is provided "as-is" with no warranties whatsoever, bla bla bla. Read more about it in Stormwind License. This one is still not even alpha. It's just a proof of concept. Gotta do some polishing in it to turn it to a Stormwind project.

I'd really like some feedback on this, since I feel it's an issue that scares most devs and plagues most projects.

I might not even be on the right track, but please just give me some honest feedback, it really helps! I'd be most thankful if you take 10 minutes to just download the code and try it. Just read, think about it and if you do like it talk to me about contributing! (oh damn, it's not even a project yet and I'm already asking for contributors! It's force of habit)

The code can be checked out from http://svn.stormwindproject.org/svn/Personal/Heynemann/Achlora.

Happy coding!

PS: Sorry for all the bad jokes, lol... I just can't resist!

* Even assuming you got good coverage, you'd hardly tested the rule properly for all different combinations of inputs, which lessens the confidence that it's going to work in a production environment. We all know users come up with the most unlikely combination of input data. That reminds me I should post about coverage mislead.
Posted by heynemann | 5 Comments

ASP.Net MVC, Windsor and some Singleton trouble

What?

I just managed to create Stormwind.Accuracy demo app using ASP.Net MVC (more on that later).

The first thing I setup in any project I do is an IoC container. I won't do code without it unless it's a REALLY simple spike. Period.

So, there I go setting my MVC app in a way that it uses Windsor to get the controllers and everything under them. Fine, works like a charm. In theory.

I have an url that looks like: http://localhost:9999/Posts/View/1, where 1 is the id of the post (yeah blog engine, not very original, I know!).

It works fine. Then I tried http://localhost:9999/Posts/View/2. I got the same post as before. Wait, there's something wrong! Let's debug that!

I have this action in PostController:

public ActionResult View(int id){
//Retrieves post and returns View();
}

To my complete and utter surprise, id was being passed as 1 in both requests. "Probably some IIS issue. Let's restart IIS." Nothing.

Ok, probably the routes are wrong. Let's check the Route Values Dictionary:

? this.ControllerContext.RouteData.Values.Keys
Count = 3
    [0]: "id"
    [1]: "controller"
    [2]: "action"
? this.ControllerContext.RouteData.Values.Values
Count = 3
    [0]: "2"
    [1]: "Post"
    [2]: "View"

What??? So it is getting the right value for Id from the route! At this point my colleagues started noticing a big "?" in my forehead.

The Light

That's the point where you keep scratching your head and then it hits you.

How does the MVC framework invoke actions. My guess was that it stores in a table somewhere the controller, action and parameters to invoke with. The issue here is that it stores this data in the actual controller instance.

Usually that's not a problem, since ASP.Net MVC will create a new instance every time it needs to call an action.

Remember that I'm using Windsor to resolve it, though. By default, the lifestyle of my controllers are singleton. I won't discuss here whether they should or shouldn't be singletons, even though I think there is no good reason why they can't be.

Conclusion

Changing the lifestyle to Transient or PerWebRequest did the trick.

Bottomline: If using Windsor to resolve your controllers, STAY AWAY FROM SINGLETONS!

Posted by heynemann | 7 Comments

Accuracy New Release - 0.4.0.127 is out!

Hey guys, it's that time again!

Acccuracy just got a new release, and this one is full of goodies!

Go on, read the release notes to see what's new, and grab the bits while it's hot!

Cheers,
Posted by heynemann | 0 Comments

Enums + Extension Methods = Sweet

Intro

Have you ever wanted to get an enum's description instead of it's ToString() representation? I know I did.

Just override ToString()? Enum's don't allow overrides. Just use some method that switches based on the enum value? Switches are evil! 

Enter extension methods. Do you know that you can extend an enum?

The following is perfectly legal code:

public static string DoSomethingWith(SomeEnum some){ //... }

From that I got the idea of extending Enum itself to add the following to the given enum:

public enum SomeEnum{

    [Description("Some Value")] 

    SomeValue = 1

  1. GetValue() - Returns the enum value as integer - i.e.: SomeEnum.SomeValue.GetValue(); - Returns 1
  2. GetName() - Returns the description for a given enum value (use DescriptionAttribute to assign a description to each value) - i.e.: SomeEnum.SomeValue.GetName(); - Returns "Some Value"
  3. GetValueAsString() - Returns GetValue().ToString() - i.e.: SomeEnum.SomeValue.GetValueAsString() - Returns "1"

Conclusion

I included the project that uses that as an attachment! I'll definitely be using that in the future. Happy coding!

Posted by heynemann | 6 Comments
Attachment(s): EnumDemo.zip

Bowling Scorecards - GREAT AGILE Practice

Intro

"Wait, are you crazy? Bowling and Agile?"

No, dear reader, I'm not crazy. The team I'm working in right now just stumbled over a new technique (not sure if someone else does it, just before people start complaining about copyright infringement) that really make our lives better.

We now have bowling scorecards for our repetitive tasks.

"Come again?"

Yeah, that's exactly what it is.

Building my Bowling Scorecard

Say we as a team agree that having good code coverage is actually an important thing (it's just an example, who cares about coverage or testing anyway?!). Then, after thorough investigation we find out that we have 20 classes with poor coverage. Cool! All we need to do is draw the following card:

Code Coverage

[MATRIX of 5x4 or 4x5]

You can check the picture of a bowling scorecard here (stolen from Stuart's blog): 

Every time someone completes the task outlined in the card they get to score a spare (one mark in one box). When they have tested it and committed it properly, they get the strike (another mark in the same box).

The scorecard is considered done when all the boxes have Strikes.

What's the purpose?

"Oh, so you track the number of tasks done. So? Big deal! I have a bug-tracking tool "XYZ" that does it for me!"

No, no, no! Bad reader! That's not what it is! It's not a bug-tracking tool!

The main purpose of the bowling scorecard (as very well said by Stuart - link at the end) is to remind us that we CHOOSE to change something that hurts and how far we are at it.

That's very important, since it's a very visual artifact. Every time you go to the wall looking for stories or moving a story you think: "Hmm... I might fix a couple of those and get some strikes myself!"

Before you know, the card is gone and you have a much better codebase.

Side-Effects

There are a couple of very interesting side effects for me.

The first one is that we get a very nice morale boost from the scorecard. Every time we get spares and strikes, the team feels more confident about the work we're doing. Finishing a whole scorecard has a major effect on team morale! We celebrate the end of it!

The second side effect we noticed is that since the spares (one mark only) are very visual (and unappealing since you want your strike to go as fast as possible), having many spares in the scorecard reminds you that you need to commit. That has a very positive effect, since we don't try to grab more than we can chew. Frequent commits have been discussed many many times before, so I won't even go into that area. Suffice to say we don't chew more than 3 or 4 at a time, except in very rare occasions.

Conclusion

The bowling scorecards might not be revolutionary, but they worked magic in our team. We really pay more attention to making the codebase better and pairs take turns at improving it between delivering business value.

You can read more about at the blog of Stuart Caborn, here. I couldn't put the link first, otherwise who would read mine, lol! Just go read his description! It's a lot better than mine! :)

Happy agile coding!

Posted by heynemann | 0 Comments

Stormwind.Accuracy - Community Feedback

I know I haven't blogged in a long time, and I didn't intend to break the ice so soon, but I have some acknowledgments to make.

First of all I'd like to say that the feedback I'm getting on Stormwind.Accuracy is brilliant! People are really loving it. That's as much as I could ask for. You can keep track of the things we'll be implementing in the future in Stormwind.Accuracy's Jira.

Even though I couldn't ask for more, more is coming!

Thanks a LOT Martin Nilsson for all the feedback on Accuracy and for the patches you submitted for it. I really appreciate and I'm pretty sure you just helped the whole .Net community by doing so (I hope people will find as much value in Accuracy as the ones I heard of have).

We got more patches, but I'm not sure I'm allowed to name them here, since they don't have blogs and stuff. Anyway, THANKS A LOT, you know who you are! You just helped make something great even greater!

Now the part where I ask for help (It seems I'm always asking, huh?). People, we are in real need of help with Accuracy's wiki. This is a great opportunity for you to get more acquainted to this amazing Acceptance Testing framework AND help the whole community! Win-win, huh? Hope to hear from you guys soon!

Cheers,

Bernardo Heynemann

Posted by heynemann | 0 Comments

Book, Book blog and YES I AM ALIVE

I know it has been a long time, but I have been doing several things at the same time.

One of them is the reason for this post. I've started writing a book on reducing friction.  Friction meaning the kind of things that slow down software development projects, specially focused on agile practices.

You can read more about the book and keep up-to-date with the books development in the books blog at: http://reducingfriction.wordpress.com/.

I hope I get feedback from you guys on what would you consider friction and what would you like to add to the book.

The partial list of topics and sub-topics of the book is published at the book's blog at http://reducingfriction.wordpress.com/books-topics-and-sub-topics/.

What about Stormwind? There's some stuff in the pipeline that I'm working in as well as the other committers. I'll talk about that later.

Cheers,

Stormwind.Accuracy 0.3.0 - GRAB WHILE IT'S HOT

Hey my three readers? How have you guys been? Hope you're good! :)

It's that time again! The time when I come back from a long "non-writing" period. Usually when I'm not writing blog posts it means I'm working on something. This time I've been working in a couple things, though.

The one that matters for this post is Stormwind.Accuracy, our shiny and fancy Acceptance Testing Framework.

Release 0.3.0

After delaying this release way more than I should, I've finally reached a point where releasing it makes sense. It's stable enough (we've been using it in some projects and I received some e-mails of people using it), it has a bunch of new features, and improved speed.

First let me put here the Release Notes (which you can read here):


Release Notes - Stormwind Accuracy - Version 0.3.0.0

Bug

Improvement

New Feature

Task


Now that you got the release notes you know what I've been working in. The most interesting bit is the last one: "Update WatiN version".

Updating WatiN Version and a Blazing Fast Accuracy

This was very interesting, since the new WatiN introduces some breaking changes, and now all the elements have interfaces (IButton, ICheckBox, you get the idea). This is a LOT better, since then I can have my BrowserDriver working with interfaces.

The main upgrade Accuracy got was in speed of execution. When changing the way WatinBrowserDriver used WatiN, I decided to do a major refactor on the way that WatinBrowserDriver was designed. This allowed me to fine tune a lot of the pain points that I had with WatiN and apparently the whole framework is more stable now (no more failing builds after the refactoring!!!).

This comes to prove that you should optimize any time you feel the pain, but ONLY THEN, never before. I might never have gotten to the point that Accuracy is now if I tried to over-optimize in the beginning.

Fluent Interface - I.Can.Do.A.Lot.Of.Stuff

Yeah, yeah, I know you guys only care about new features, so as you can see in the Tickets there are a lot of newly added syntaxes. I'm pretty sure that we are still light-years away from covering most scenarios. So if you have one scenario that we haven't covered and would like to create a new action for it, please DO, and then send us the patch! :)

Just so you guys know, here's the upcoming features for 0.4.0 (something you want might already have a ticket, so you might as well vote in it):


Release Notes - Stormwind Accuracy - Version 0.4.0.0

Bug

Improvement

New Feature


If you don't feel like implementing something you need and would like help from us, WE ARE GAME! Just create a new ticket in JIRA and we'll try our best to help you out!

The Goodies

As usual, the release comes with updated binaries, references and source.

So, if you are using, would like to use, or just wanna see what all the fuzz is about, GO GRAB THE LATEST VERSION here!

See ya guys,

Stormwind Project - Maturity Point

I've been doing Stormwind work since 4th September, 2007. The projects we are managing in Stormwind started around the beginning of 2006, though. It's been a very rewarding experience all over.

When we started (me and Claudio Figueiredo), we were not aiming at something as big as what we have now.

I just wanted to do a brief summary on the whole experience before it gets lost in the midst of time.

Childhood

When Stormwind started, we were very worried about the actual infrastructure for the community, like forums, portals, build server, e-mail, domain and stuff like that. These are very time-consuming to get right.

We fooled around with several different web portal solutions for our main page, until we settled with Joomla. It kind of gets what we need done, but we would like it to be a bit more extensible. There is right now a project called Stormwind.Fjords which aims at being a more extensible web portal for the .Net platform.

This is the point at we turned to some companies for sponsorship. Handling an open-source community is very expensive if you think about time consumption alone. Now, imagine if you have to pay for all the resources involved like:

  1. Web Hosting - This is a very expensive resource, since depending on your community activity it can involve a lot of trafic.
  2. Source Control Management Hosting - Another expensive resource, since if you have active committers that means a lot of trafic and even more disk space in the SCM provider.
  3. Build Server - Another expensive resource, since for this one you need a Server to keep building code. We were lucky enough as to get a Virtual Private Server that's available all the time.
  4. E-mail and Lists - This wasn't expensive since we got Google apps for your domain and google groups. Thanks google!

After setting all this up, we had to crank some code. At this point we wanted to build NMVP 1.0.0. Right now, I'm glad we didn't. C# didn't have the language constructs required to make NMVP work the way we envisioned it at the time (it does now, though!). We spent a HUGE amount of time here making our build scripts work.

Young Adulthood

I think this is the phase we are now.

We have very stable infrastructure, featuring:

  • Email groups
  • Subversion Server
  • JIRA Bug tracking
  • Confluence Wiki for documentation
  • Web Portal for general information
  • Fisheye and Crucible for Code Review
  • Several sponsored licensed software like: CCNet plugins, Resharper, Mingle, among others

We have several projects featuring releases that are stable enough for production use. This is actually a major turning point for me.

We finally arrived at a point where all our efforts are being turned in actual value for the development community in general. This is a sign that Stormwind has reached a more mature age. An age where we'll start focusing more in WHAT to deliver instead of HOW to do it.

A proof of this maturity is that yesterday we applied our first community-contributed patch. This does not sound like much to the likes of Castle or NHibernate, but if you ask their committers you'll see how hard it is to get real, constructive involvement from the community. That really makes me proud to be part of this effort and makes me want to deliver more and more fun stuff.

The Future

We have several projects running and we are trying to get Project Managers to contribute helping us be more organized, yet still agile as an open-source community should be. More on that in another post.

We are looking forward more interaction with the community and releasing more features for our projects. We also need to work heavily in our documentation. So there's a lot of work ahead. I don't think we could be more confident that we can do it, though.

It's been a thrilling experience all along, and I'm sure it's still going to be for a while.

Conclusion

This was just a quick summary of where we are now in Stormwind.

If you think any of our projects is interesting and want to be a part of it, just let me know. Send me an e-mail, comment here, send e-mail to Stormwind Community Dev group (stormwindcommunitydev@googlegroups.com).

I can tell you from experience, when you make a difference in an effort that big, it really pays every single minute you put into it. Really.

Posted by heynemann | 0 Comments

WCF Service Extreme Make-over - Part 3

IMPORTANT: It's really important that you read part 1 and part 2 before reading Part 3. I'll assume you did, so a lot might not make any sense unless you've read it.

Introduction

So in the previous two posts, we've established how we wanted to use and host our WCF Services, and how we actually hosted them.

Just some things to remember:

  1. Our Service should be usable by it's URL alone, which is configured using an AppSettings value with Key of "IMathServiceUrl".
  2. We want to access this service using var mathService = ServiceResolver.Resolve<IMathService>();

So, without further ado, let's have our service working. Oh, wait... Almost forgot I work for ThoughtWorks now. So let's have a failing test.

The very beginning - Our Failing Test

Let's have a test that calls on our Math Service, like this one:

[TestFixture]
public class TestServiceCall
{
    protected IMathService mathService;
 
    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        mathService = ServiceResolver.Resolve<IMathService>();
    }
 
    [Test]
    public void ShouldCallAdd()
    {
        Assert.That(mathService.Add(1, 2), Is.EqualTo(3));
    }
}

As you can see we initialize an instance of IMathService in our Fixture SetUp, and then just use it whenever we want it. Let's check the config file for the Test Project, just to make sure we are not using any WCF sorcery:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="IMathServiceUrl" value="http://localhost/MathService/MathService.svc" />
  </appSettings>
</configuration>

As promised we are just setting the Url for that service's endpoint. Let's run our test and check that it fails.

The actual Service Resolver

Cool, it doesn't even build, since it doesn't know any ServiceResolver classes. So let's change that by adding a ServiceResolver class to the ServiceInfrastructure project, like this one:

using System.ServiceModel;
 
namespace Stormwind.EasyWcf.ServiceInfrastructure
{
    public class ServiceResolver
    {
        public static T Resolve<T>()
        {
            return ChannelFactory<T>.CreateChannel(
                new BasicHttpBinding(),
                new EndpointAddress(UrlHelper.GetUrlFor<T>()));
        }
    }
}

What? That's it? I'm sure you must be mistaken!

Not at all my dear friend, and you can thank the WCF Team for that. They made it very easy to create a client-side proxy using code, instead of a Service Reference.

Now, let's run our test again, and who could foresee it! GREEN!

Conclusion

That's all there is to it, though. Even though I would like to keep rambling on and on about how hard it is, it wasn't even hard. I just had a hard time finding resources that explained the whole process. That's why I decided on writing a comprehensive post on it.

I really hope that these three posts have helped you grasp how to use WCF in a more developer-friendly way. I'll post next on how to do secure bindings and stuff like that using this approach, which is quite good to ENFORCE security, since the client and host cannot mess with the security settings. Once again, it has drawbacks on flexibility.

The full code for Stormwind.EasyWcf is available through our Subversion server at: http://svn.stormwindproject.org/svn/Personal/Heynemann/Stormwind.EasyWcf.

Cheers.

Posted by heynemann | 9 Comments
Filed under ,

WCF Service Extreme Make-over - Part 2

IMPORTANT: It's really important that you read this post before reading Part 2. I'll assume you did, so a lot might not make any sense unless you've read it.

Introduction

Ok, in last post we decided on the following desired features for our WCF Services:

  1. No more Service References and we'll share contract assemblies between client and host.
  2. No more funky XML Config files that span multiple screens.
  3. Calling services should be simple and straightforward, with as little configuration as possible.
  4. Hosting the services should be easy and require as little configuration as possible.

We also decided on some assumptions that need to be true in order for our scenario to work in the way we intended:

  1. Well-defined policies on communication between services.
  2. Well-defined boundaries between services.
  3. Contracts shared between client and host (and versioned).

We have some degree of variability for those constraints in our solution, but for the rest of this post I'll assume them to be true.

Service Infrastructure

First thing we'll do is create a service infrastructure project in our solution to hold all our infrastructure code (the actual purpose for this post).

I'll call it Stormwind.EasyWcf.ServiceInfrastructure, and add it as a Class Library.

So, without further ado let's create our WCF Service.

MathService Host

We'll start with the contract for our service. This will be a very simple contract with just one operation that goes like this:

   1:  namespace Stormwind.EasyWcf.Contracts
   2:  {
   3:      [ServiceContract]
   4:      public interface IMathService
   5:      {
   6:          [OperationContract]
   7:          int Add(int value1, int value2);
   8:      }
   9:  }

That is fairly common WCF stuff, right? We'll add that class to our Stormwind.EasyWcf.Contracts assembly. This is a new project which we'll add to our solution, and is responsible for holding all our contracts. This is the "currency" we'll use when having clients talking to hosts.

Now we just need to implement that contract. There are two approaches here. One is creating the implementation for it in the Web App that hosts the service. The other approach is to have an assembly of service implementation that you can version independently of the web site. I like the later one better than the former.

So we'll just create another class library called Stormwind.EasyWcf.Implementation, and add to it our MathService class, like this:

   1:  using WCFFacilityTest.Contracts;
   2:   
   3:  namespace Stormwind.EasyWcf.Implementation
   4:  {
   5:      public class MathService : IMathService
   6:      {
   7:          public int Add(int value1, int value2)
   8:          {
   9:              return value1 + value2;
  10:          }
  11:      }
  12:  }

Ok, again pretty straightforward. Now we have both our contract and our implementation. All that's left is hosting the service. That's should be easy, right? Well, after we get the infrastructure out of our way it actually is.

WCF can automatically host the services in IIS for you provided you supply it with some configurations (config files) and a service file (*.svc) so that it can serve as a hook for IIS to call on the ASPNET_Isapi.dll and then delegate the call to WCF.

That's all fine and dandy IF you want to use config files, which is not our case. Fortunately for us, there is another way. WCF allows us to specify our own ServiceHost factory. What that means is that we can specify how our service is to be hosted, instead of relying in the default one.

In order to host our service using that approach we'll rely on our own implementation of the ServiceHostFactory class. This class is responsible for building our own ServiceHost via the CreateServiceHost method. This method gets a service and a bunch of urls. We're only interested in the service. We'll add a class called DefaultServiceHostFactory to our ServiceInfrastructure project like this one:

namespace Stormwind.EasyWcf.ServiceInfrastructure
{
    public class DefaultServiceHostFactory : ServiceHostFactory
    {
        public override ServiceHostBase CreateServiceHost(string service, Uri[] baseAddresses)
        {
            var implementationType = Type.GetType(service);
            var contractType = GetContractFrom(implementationType);
            var serviceHost = new ServiceHost(implementationType);
 
            serviceHost.AddServiceEndpoint(contractType,
                                           new BasicHttpBinding(), UrlHelper.GetUrlFor(contractType)); 
            return serviceHost;
        }
 
        private static Type GetContractFrom(Type serviceType)
        {
            var contract = serviceType.GetInterfaces().Where(type => type.IsServiceContract()).SingleOrDefault();
            if (contract != null)
                return contract;
 
            throw new InvalidOperationException(string.Format("The type {0} is not a service. Try using the ServiceContractAttribute.", 
                serviceType));
        }       
    }
}

Let's take it slow. Before I start commenting on the code above, let me show you how we would use that in a MathService.svc file:

<%@ ServiceHost Language="C#" Service="Stormwind.EasyWcf.Implementation.MathService, Stormwind.EasyWcf.Implementation"
Factory="Stormwind.EasyWcf.ServiceInfrastructure.DefaultServiceHostFactory, Stormwind.EasyWcf.ServiceInfrastructure" %>

So in the service declaration you can see that we're specifying the ServiceHost factory to use for this service. This is how WCF knows how to build the service host for this service.

If we go back to the DefaultServiceHostFactory class, you'll see that the CreateServiceHost method gets the service that is trying to use this factory as a parameter. That string is the one supplied in the .svc file in the "Service" attribute, in our case "Stormwind.EasyWcf.Implementation.MathService, Stormwind.EasyWcf.Implementation".

From this string which is the full name for our service implementation we can get to a type, represented by the implementationType variable. From that we can get to the contract easily by inspecting the interfaces implemented by the "implementationType" type. The GetContractFrom method uses an extension method defined in the class below:

using System;
using System.ServiceModel;
 
namespace Stormwind.EasyWcf.ServiceInfrastructure
{
    public static class TypeExtensions
    {
        public static bool IsServiceContract(this Type type)
        {
            var attributes = type.GetCustomAttributes(typeof(ServiceContractAttribute), true);
            return attributes != null && attributes.Length != 0;
        }
    }
}

This class just defined a method that returns if some type contains the ServiceContractAttribute, which is the one that defines a WCF Service Contract.

After retrieving the ContractType, we can just define our ServiceHost. In this sample we're just using a BasicHttpBinding, but you can use any binding you want. We also applied a Convention-Over-Configurations concept that defines that we need an AppSettings value that defines the URL for the endpoint using "Contract Name" + Url, in our case "IMathServiceUrl". So after defining that key in our web.config file for the Web App, we are set. We have a ServiceHost that can handle requests properly.

If you want to try it out just attach your VS to ASP.Net and access the SVC file's url from your browser. Remember to put a breakpoint in the ServiceHostFactory to see how it is hit.

Conclusion

Oh no, not again!

Sorry, this is bigger than most people would read already. If you still feel like reading and would like to know how to USE this hosted service just go to Part 3.

Cheers.

Posted by heynemann | 2 Comments
Filed under ,
More Posts Next page »