tag:blogger.com,1999:blog-6828435964621398292024-03-13T03:51:02.378-07:00Elliott the AgileElliott's "Geeky" BlogElliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.comBlogger43125tag:blogger.com,1999:blog-682843596462139829.post-73101578118394168432011-10-17T14:29:00.000-07:002011-10-17T14:29:00.154-07:00Lets use EventSourcing on the Cloud, another Amazon project with Whale in the nameSo, I'm playing with Event Sourcing, and I cooked up my own, mostly because I want to be able to explain this well to those who ask.<br />
<br />
Let's explain in code with a really simple domain that everyone will understand -- Bank Accounts, you better have one!<br />
<br />
How does one get a Bank Account? They open it, right? So I made this event.<br />
<br />
<script src="https://gist.github.com/1265324.js?file=AccountOpened.cs">
</script>
<br />
Now lets make a quick domain POCO domain object for an account. The ctor will take this event, and do some cool stuff with state. Looks like this.
<br />
<script src="https://gist.github.com/1265324.js?file=account_ctor.cs">
</script>
<br />
Two interesting things to note here.
First, that I'm mutating my state in the Apply(AccountOpened) method, not just in the ctor. This is because that method will be called later when we get the Account from a repository. Yeah, it's a bit different, but honestly, to me the benefits WAY outweigh this single "drawback" to me. What benefits, you ask? Glad you did... We'll get there in a minute.
<br />
Second difference is that UncommittedEvents object... We'll also get to that in a minute. For now, lets write a quick integration test and watch WhaleEs in action. First, we bootsrap WhaleEs, like so.
<br />
<script src="https://gist.github.com/1265324.js?file=bunch_of_tests_bootstrap.cs">
</script>
<br />
I'm just parsing some text file that has my Amazon info in it here, then calling WhaleEs fluent config API so that I can get an Instance of Repository<account>. </account><br />
Next I'll create an account and save it. <br />
Like so.<br />
<script src="https://gist.github.com/1265324.js?file=create_account.cs">
</script>
<br />
Kinda boring, huh? Let's go look at the guts of Repository<t>, cause I know that's what you wanna see.
</t><br />
<script src="https://gist.github.com/1265324.js?file=repository_put.cs">
</script>
<br />
Just a little reflection magic here. I really don't wanna make people reference my library and ruin the POCO on their AR objects, so I just let them set the method name that contains the list of Uncommitted objects. That's pretty much the convention everyone is using for ES so I'm sure we'll be ok with that. Anyway, I'm calling that getter, then persisting the events it returns (calling the object that I blogged about last).
<br />
The get method is uses similar logic. It simply pulls the event stream for the AR with that Id, then uses reflection to call Apply(event,true) for every event, rebuilding state. It sends the extra true parameter for isReplaying, that way the AR won't add the event to it's UncommittedEvents property. Here it is.<br />
<script src="https://gist.github.com/1265324.js?file=repository_get.cs">
</script>
<br />
Not to horribly complicated actually. Lets see if it works.
<br />
<script src="https://gist.github.com/1265324.js?file=get_account.cs">
</script>
<br />
Sweet! Console outputs 100, just like I opened the account with. Let's play with this a little more. Make a couple deposits and withdraws.<br />
<br />
<br />
<script src="https://gist.github.com/1265324.js?file=do_stuff.cs">
</script>
<br />
This works great, test passes, and that proves that we're actually rebuilding state. "So what Elliott! I could do that by just saving state, you twit!" you say.
<br />
Yes, tis true, my dear friend, you certainly could. However requirements change. Let's say they did, and know we want a list of all activity that ever occurred on the account. In good 'ole state land, we'd just cross our fingers that when we originally designed the system we had the good sense to actually save Deposit and Withdraw objects to some persistence, maybe we would, but maybe we wouldn't. Point is, working this way, even though we didn't it is very easy to add an "ActivityList" property to our Account object. Just modify the Apply methods for the appropriate events.... Like so.<br />
<script src="https://gist.github.com/1265324.js?file=accout_apply_with_ActivityList.cs">
</script>
<br />
Add a quick test like this.
<br />
<script src="https://gist.github.com/1265324.js?file=show_activity_list.cs">
</script>
<br />
And we see this....
<br /><br />
<br />
<div class="p1">
Account Opened On 10/5/2011 2:35:43 PM with $1000</div>
<div class="p1">
Deposit made on 10/5/2011 2:35:43 PM $45</div>
<div class="p1">
Withdraw on 10/5/2011 2:35:43 PM $17</div>
<div class="p1">
<br /></div>
<div class="p1">
Anyway, I know there's a ton of writings on this stuff, but I hope this helps someone somewhere.<br />Feel free to yank the code down from github at https://github.com/elliottohara/WhaleES.<br /><br />Have fun!</div>
Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com6tag:blogger.com,1999:blog-682843596462139829.post-67751696763779130742011-10-10T05:22:00.000-07:002011-10-10T05:22:00.209-07:00Quick and Dirty Event Source using Amazon S3 and JSONhttps://github.com/elliottohara/WhaleES<br />
<br />
So yeah, I'm doing it too. Writing an event store. Not really because I think that <a href="https://github.com/joliver/EventStore/downloads">Jonathan Oliver's</a> isn't good, just because I wanna wrap my head around it.
Lemme break down Event Sourcing into as small as a nutshell as possible.<br />
<br />
<blockquote>
Event Sourcing is the storing of a "stream" of events that represent the history of all that has happened in a system, and using that to create state instead of simply storing the state.</blockquote>
<br />
If that doesn't make sense to you, well, that's not the purpose of this blog. Go google it a bit, and read some more, then come back. This particular post is about the quick and dirty ES implementation I'm writing. If you're too lazy for google, here's a few great links.<br />
<br />
<a href="http://cqrsinfo.com/documents/events-as-storage-mechanism/">http://cqrsinfo.com/documents/events-as-storage-mechanism/</a><br />
<a href="http://martinfowler.com/eaaDev/EventSourcing.html">http://martinfowler.com/eaaDev/EventSourcing.html</a><br />
<a href="http://codebetter.com/gregyoung/2010/02/20/why-use-event-sourcing/">http://codebetter.com/gregyoung/2010/02/20/why-use-event-sourcing/</a><br />
<br />
<br />
Here's the idea. Events are raised by Aggregate Roots, and I'll create one stream for each AR. I'll serialize the stream of events (for now using JSON) to a Amazon S3 bucket with a key of the type name of the AR. Seems pretty simple to me... Let's give it a whirl.<br />
<br />
First, I'll create some test dummies for my AR and an Event. Like so<br />
<br />
<script src="https://gist.github.com/1264281.js?file=test_ar_and_event.cs">
</script>
I think I'll make an EventSource<t> object where T is the AR type... Here's a quick integration test - starting with actual integration because, well, because I don't wanna get bit by Amazon quirks that I'll miss in a unit test. <br />
<script src="https://gist.github.com/1264281.js?file=store_an_event.cs">
</script>
</t><br />
Not really a test, but I'll just look in S3 for the file for now... Like I said, this is quick and dirty! Let's go make this happen. So first, we're gunna need a way to tell the serializer what types the actual events are, so we can't just dump them all in a file and persist it. I created a quick little EventEnvelope class that wraps that. Like so.<br />
<script src="https://gist.github.com/1264281.js?file=EventEnvelope.cs">
</script>
We'll just serialize a list of these to S3. Ok, lets do this.
<br />
<script src="https://gist.github.com/1264281.js?file=EventSource_take1.cs">
</script>
Looks pretty simple... Run the test. No exceptions and yep, I see a file there.
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
The contents look like so...
<br />
<script src="https://gist.github.com/1264281.js?file=eventstream.json">
</script>
<br />
Cool. Looks ok so far. Now lets make that GetEventStream method work. I'll add a test that'll just write out the TestEvent.What value that I just put up there.
<br />
<script src="https://gist.github.com/1264281.js?file=print_event_to_console.cs">
</script>
<br />
Pretty simple... Now lets make it pass.<br />
<script src="https://gist.github.com/1264281.js?file=EventSource_take2.cs">
</script>
<br />
<br />
Pretty simple huh? We're just calling that ExistingEvents for method that yanks the file from s3 for that Id..<br />
Run the test, and yep, I see "Blah" in my console.<br />
<br />
Ok, so I'm not sure if this code will actually get used, because Jonathan Oliver did some really great ES stuf and he's working on S3/Simple DB implementations now, however, I really wanted to create a quick implementation for some POC stuff. I was surprised at the simplicity of this. Next up, lets see if I can write something to get some AR state working of a stream.Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-30761855986877756912011-10-03T06:05:00.000-07:002011-10-03T06:05:34.223-07:00Givin' Amazon cloud services some luv... Why you should consider using Amazon for SOA needsSo the <a href="http://lostechies.com/joeocampo/2011/10/02/pablos-fiesta-thank-you/">Los Techies Open Spaces Event</a> was amazing. I have never in an environment where I was surrounded by so many people that just "got it". It was just awesome.<br />
<br />
There was a re-occurring theme (besides FUBU and javascript) that I found myself gravitating to, it was EventSourcing and messaging patterns (surprise huh). The last session was on messaging systems, and I was surprised to kinda be the lone voice in the room mentioning Amazon services. I figured I'd make a quick blog post where I show the actual pub/sub guts of WhaleBus, and how simple it is to get messaging working on the Amazon cloud.<br />
<br />
First of all, go sign up for <a href="http://aws.amazon.com/">AWS</a>, it's free to sign up, and the free tier allows 100K SNS and SQS requests and up to 1K email notifications. <br />
<br />
Done? Ok cool, let's get to coding.<br />
<br />
Let's publish a message to the cloud real quick like. Spin yourself up a project and get AWSSDK.dll (it's on nuget- just search for Amazon, it's the first result).<br />
<br />
So, first, let's create a "Topic" to publish our messages to. Amazon gives us a web interface to do this, but who wants that? Let's do it programmatically...<br />
<script src="https://gist.github.com/1258937.js?file=create_a_topic.cs">
</script>
<br />
Simple enough, I see the topic arn on my console when I run it. And I can see the new topic in my aws console.
<br />
Next up, let's publish a messages to that topic. First, let's just set up an email end point through the aws console (we can do it programatically just as well, but I wanna publish real quick like).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF3FHvsmu7gr9Kxyi60dpITWJx-htylCvemeh_sy9Ot7piDTonEiKHvDQBvH1ld3to_BnwHnHBwVLc54O35TDRM_1pqDQk4iCnEsTwB1wydeZ27t9JI5JaCHtQzUxy5JfoV-9y1aCYl_U/s1600/Screen+Shot+2011-10-03+at+6.47.28+AM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF3FHvsmu7gr9Kxyi60dpITWJx-htylCvemeh_sy9Ot7piDTonEiKHvDQBvH1ld3to_BnwHnHBwVLc54O35TDRM_1pqDQk4iCnEsTwB1wydeZ27t9JI5JaCHtQzUxy5JfoV-9y1aCYl_U/s400/Screen+Shot+2011-10-03+at+6.47.28+AM.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Create an Email Endpoint UI in AWS console<br />
<br />
<div style="text-align: left;">
<br /></div>
</td></tr>
</tbody></table>
Now, amazon sends me an email to the address I specified and makes me opt in (otherwise this would be a really cool way to piss people off, huh?). I follow the link in the email. Subscriber is done.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrcrOB-_uNTOUS5-ISYQMHj4i1waqrR1UTLBsSmgzQgrgi389gbvDE9nvj2_1ANgnPi-FyXy-XlSLeqOis-45SuXp1FutPdrrmesw13ohO-X4DWpcIhadfyErXYkHFf-wkvFJJp88o6Pk/s1600/Screen+Shot+2011-10-03+at+7.00.41+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrcrOB-_uNTOUS5-ISYQMHj4i1waqrR1UTLBsSmgzQgrgi389gbvDE9nvj2_1ANgnPi-FyXy-XlSLeqOis-45SuXp1FutPdrrmesw13ohO-X4DWpcIhadfyErXYkHFf-wkvFJJp88o6Pk/s400/Screen+Shot+2011-10-03+at+7.00.41+AM.png" width="400" /></a></div>
<br />
<br />
Simple enough, right?<br />
Now, when I publish a message to this topic, I SHOULD get a email with the message json serialized. Let's try it out.<br />
<br />
<script src="https://gist.github.com/1258937.js?file=publish_a_message.cs">
</script>
<br />
Run the test, and check my email, and booya!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo6qEU-g4iGqE9OaiWp0IOOfhrbq_DD8Jhy81EAAxj5NCjfy5m9VIFS4FmSb5hWe7gepNoyQjve2TfNCKi6E5kDeJG9Kc_hFO-UlSJyh-hwz5YA07K8IKThhdJs0wtMYz30y_JvqnJOhU/s1600/Screen+Shot+2011-10-03+at+7.03.50+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo6qEU-g4iGqE9OaiWp0IOOfhrbq_DD8Jhy81EAAxj5NCjfy5m9VIFS4FmSb5hWe7gepNoyQjve2TfNCKi6E5kDeJG9Kc_hFO-UlSJyh-hwz5YA07K8IKThhdJs0wtMYz30y_JvqnJOhU/s400/Screen+Shot+2011-10-03+at+7.03.50+AM.png" width="400" /></a></div>
<br />
So, there we go, I just created a topic, then published a message to that topic. However, I'm having a hard time seeing how useful getting emails of published messages is, I mean, I could just send myself an email with a subject of "Elliott has an awesome blog" and a body of "Hello from the cloud" and get the same result, right?<br />
<br />
So, lets, now go set up a subscriber that has a little more value. SNS supports HTTP posts, but let's not do that, lets use an SQS queue. Yeah, we can create one through their ui, but lets do it with code.
<script src="https://gist.github.com/1258937.js?file=create_and_subscribe.cs">
</script>
<br />
Ok, ok, lots of code there, but it's almost all security stuff that you'll only do once per sqs queue.<br />Everything from line 21 - 30 is simply setting the security to allow SNS to publish messages to the newly created SQS queue.<br />
<br />Now, let's create a go publish that message again and write a quick little test that'll pull messages from the sqs queue. So, I run the publish_a_message test, and I see the email arrive. So I know the message made it to SNS, let's write the code to pull from the sqs queue.<br />
<br />
<br />
<script src="https://gist.github.com/1258937.js?file=get_messages_from_sqs"></script>
All this code is doing is looping for 5 seconds and calling RecieveMessage, then writing the contents of the message to the console. Here's what I see.<br />
<script src="https://gist.github.com/1258937.js?file=result.js"></script>
So, yeah, it works, and it's simple. So I like it.
Whatta ya think?
Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com6tag:blogger.com,1999:blog-682843596462139829.post-51113767734913249342011-09-29T08:57:00.000-07:002011-10-01T18:24:38.038-07:00Looking for good software engineers in Austin, Tx<div class="separator" style="clear: both; text-align: center;">
</div>
Ok, so they put a link to my blog on the corporate site, and while I've been blowing up twitter about the fact that we're hiring, I figured I'd make one little blog post about the fact that we're hiring, what we're looking for and how AWESOME this job is. Email me at <a href="mailto:eohara@whalesharkmedia.com">eohara@whalesharkmedia.com</a> if you're interested, or hit <a href="http://www.whalesharkmedia.com/careers_culture.html">http://www.whalesharkmedia.com/careers_culture.html</a>.<br />
<br />
<div style="text-align: center;">
<span class="Apple-style-span" style="font-size: large;">Why is WhaleShark awesome?</span> </div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXyGoVOqzyLDNKx5XMyw9Hk9YP16SbVO3Uhcd3VshWdYvnEfnfBsEhUlQQyACr70ZcqJCxH5Hw76C40vNrEP1396fzhJg2xDYTSpFLx8adOI9nkZc82kGkeC3wz9IjVDwTFZAQw6syyiY/s1600/IMAG0276.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXyGoVOqzyLDNKx5XMyw9Hk9YP16SbVO3Uhcd3VshWdYvnEfnfBsEhUlQQyACr70ZcqJCxH5Hw76C40vNrEP1396fzhJg2xDYTSpFLx8adOI9nkZc82kGkeC3wz9IjVDwTFZAQw6syyiY/s200/IMAG0276.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Yeah, we hire interns too!</td></tr>
</tbody></table>
<br />
The deal industry is going nuts. It ain't no secret that the economy has seen better days, and consumers are trying how to buy more stuff with less money. So, yeah, we're killing it economically. How do I know? Well, every Tuesday, we have an all hands meeting, and they tell us actual numbers. It's really cool to work for a company where people start wondering what's wrong, when we only exceed forecast on revenue by 4 or 5%. <br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiV-U01QQW0IsOwH23ftK6R3GHJHUqfpLs6_9mZ7YODZPxsckRsENKygR2LGsSmXZUXRa33tN0tHOw0wwl4WE0KC2n5EsQqSL4rW59J-LYfryv-2d6P_tpOdkg52M0hOAvev3kQBvlCV0/s1600/IMAG0279.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiV-U01QQW0IsOwH23ftK6R3GHJHUqfpLs6_9mZ7YODZPxsckRsENKygR2LGsSmXZUXRa33tN0tHOw0wwl4WE0KC2n5EsQqSL4rW59J-LYfryv-2d6P_tpOdkg52M0hOAvev3kQBvlCV0/s200/IMAG0279.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Our kitchen</td></tr>
</tbody></table>
The technology stack is pretty neat here. By "pretty neat", I mean AWESOME! We use Voldemort for persistence. It's a distributed key/value store that's pretty nifty. We serialize everything using Google Protocol buffers (the value part of key/value). All queries (since you can't query a key/value store easily) are Solr. We also use the heck outta Amazon servies. We use Amazon S3 to store all sorts of data, SNS and SQS for pub/sub (see my <a href="http://blog.elliottohara.com/2011/09/using-messaging-in-real-world-legacy.html">WhaleBus</a> post). We have massive amounts of data that we use Hadoop/Pig to process to get lots of cool analytics stuff. We've got sites in classic asp, asp.net 1.1, asp.net mvc, wordpress, LAMP - pretty much everything you can think of, and we make all work together. In short, it's pretty freekin' cool. What's even cooler, is we'll keep getting more sites, the executive team is all about acquisition, so engineering <i><u>has to</u></i> stay agile. That's a good thing to me.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0wIZi3ntlZH_M9JxLV183RxNmXuSFyI1IWvZ6ICwPBDHpbpnUpPG-fDg45HV-CvlIcfT0zsPxqGbk78pkXnYyaM11QNI-Pfzzl9N3fc3gZQiXPGx5oFBJHLfYoITzosNNkyXaB20Uolo/s1600/IMAG0277.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0wIZi3ntlZH_M9JxLV183RxNmXuSFyI1IWvZ6ICwPBDHpbpnUpPG-fDg45HV-CvlIcfT0zsPxqGbk78pkXnYyaM11QNI-Pfzzl9N3fc3gZQiXPGx5oFBJHLfYoITzosNNkyXaB20Uolo/s200/IMAG0277.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Yeah, that's Dublin baby!</td></tr>
</tbody></table>
Lunch is catered (at least) every Tuesday. The fridge is always stocked with frozen lunches, Blue Bell ice cream, bagels and waffles (for us morning guys), and lots of other goodies. And YES, to the right, that is a picture of Dublin Dr. Pepper on tap that you see. The office is, AMAZING. Glass conference rooms, desks that promote pair programming and impromptu conversations, but still large enough that you can take a personal call and not let everyone know that your kid just got sent home for flicking a bugger on the teacher. There's small couches all over the place so you can sit and talk about how awesome CQRS or why the Cowboys aren't gunna suck this year without the need to reserve some conference room. Oh yeah, there's usually beer in the fridge on Friday, and no one gives you crap if you drink it.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUKLwH3frArdBaVwNgNv7_EzgE-baKAZ20Q7j91LZDVD5aA5Txoe-qmeJ7n7DhsbnJBo3WXEdvo084GXDGCxWOGcNZJW_rnujJAspD5FSeEizMpN_t_0zri6_1OsKzMWIfl4nKGqGfbpY/s1600/IMAG0230.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUKLwH3frArdBaVwNgNv7_EzgE-baKAZ20Q7j91LZDVD5aA5Txoe-qmeJ7n7DhsbnJBo3WXEdvo084GXDGCxWOGcNZJW_rnujJAspD5FSeEizMpN_t_0zri6_1OsKzMWIfl4nKGqGfbpY/s200/IMAG0230.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">My Desk (and Pratik being awesome)</td></tr>
</tbody></table>
The culture at WhalesShark is another thing worth mentioning. I've been around the block a couple times. Too many times I've seen a great company loose good engineers because of some silly political issues between product, engineering and executive staff. Cotter (our CEO) is amazing about NOT allowing that kinda thing, we have an open culture like I've never seen. Most (I think all) of our full time employees have options. So, we've all got a vested interest in the success, and we all work together at it. We work hard, but have fun too. I'm the resident nerf dart expert, and as the Team Lead for the Internal systems team, it's not rare for Angie (one of the operations managers) to come shoot me with her nerf gun because one of the feeds imported a coupon wrong. We have a LOT of nerf gun wars actually.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzSWnDLa0qfHkkZASHJsKnUK98tKmSoeYtjiEiK-s8TBjdBlybeT3uAfTf3k0zxa6D4Vi0sgPBZNw9BrLKkaMtCmvGZCtNoxxtDzMg3eyeAD9yLYObr6AS4Hl87dVLFZunRhHUbvihnLk/s1600/IMAG0237.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzSWnDLa0qfHkkZASHJsKnUK98tKmSoeYtjiEiK-s8TBjdBlybeT3uAfTf3k0zxa6D4Vi0sgPBZNw9BrLKkaMtCmvGZCtNoxxtDzMg3eyeAD9yLYObr6AS4Hl87dVLFZunRhHUbvihnLk/s200/IMAG0237.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Board Room</td></tr>
</tbody></table>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFkp6yiGGz2v9FVBJc7J_9u65u6aXOXMUgpMB3PRoOZesViDzcK6Ehh6LNZyvVdIcIyyengIB-MsOPDoO6lN32h2zWfWaGgpicBNOm93OsXeomVC48JKpshowgE03UJ9zU9Mfvry-ry-c/s1600/IMAG0239.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFkp6yiGGz2v9FVBJc7J_9u65u6aXOXMUgpMB3PRoOZesViDzcK6Ehh6LNZyvVdIcIyyengIB-MsOPDoO6lN32h2zWfWaGgpicBNOm93OsXeomVC48JKpshowgE03UJ9zU9Mfvry-ry-c/s200/IMAG0239.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Conference room (and Jamie)</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcNX5tnEL645_cd1awL4PnpHnSu0hJbbRvwKC3Wa5bxHgs9VKbmFeEEvagTBF-C7PSE7wThjagkqDIxBjSBwZGHiT9VvxD4SDkV1KmLDZLRhVHVsdPDfILfzLpN7ouwdNTv-jGT0HQcD4/s1600/IMAG0235.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcNX5tnEL645_cd1awL4PnpHnSu0hJbbRvwKC3Wa5bxHgs9VKbmFeEEvagTBF-C7PSE7wThjagkqDIxBjSBwZGHiT9VvxD4SDkV1KmLDZLRhVHVsdPDfILfzLpN7ouwdNTv-jGT0HQcD4/s200/IMAG0235.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Elevator Lobby</td></tr>
</tbody></table>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: center;">
<span class="Apple-style-span" style="font-size: large;">So what types of Engineers are we looking for?</span></div>
<div style="text-align: left;">
</div>
<ul>
<li>Passionate about their craft, someone who codes because they love it, not because it's their carrier.</li>
<li>Is willing to step out of the comfort zone, and does. Someone who learns things because they want to.</li>
<li>Someone who understands the importance of testing (I'm a TDD guy, and that's what I want, but it's not a must)</li>
<li>Someone strong in OOP and Design (I'm a big DDD guy)</li>
<li>Someone who is interested or has experience in distributed systems (CQRS is a big plus in my book)</li>
<li>An awesome engineer - I WILL go through coding exercises with you, and if you think giant case statements are ok, then, move on :)</li>
<li>A github account with some examples of stuff you've done is a big plus.</li>
</ul>
<div>
Ok... sorry for the spam! Back to coding for me! </div>
<div>
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7cn_Ea_ClSF-bXadWmYv2oH0PRNXnvUDmZlUH46sR8JA45tJkPQXA3jRaIvR-Ar19QVFnjaM74xEk1VrLt985fB0kgIcDZT_d5ZO2CAYLXdw22rDQdAuoLXh7a1-FMQZkTGFbG2V9OpM/s1600/IMAG0247.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7cn_Ea_ClSF-bXadWmYv2oH0PRNXnvUDmZlUH46sR8JA45tJkPQXA3jRaIvR-Ar19QVFnjaM74xEk1VrLt985fB0kgIcDZT_d5ZO2CAYLXdw22rDQdAuoLXh7a1-FMQZkTGFbG2V9OpM/s320/IMAG0247.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="mailto:eohara@whalesharkmedia.com">eohara@whalesharkmedia.com</a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.whalesharkmedia.com/careers_culture.html">http://www.whalesharkmedia.com/careers_culture.html</a>.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />
<br />
<br />
<br />Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com27tag:blogger.com,1999:blog-682843596462139829.post-47746290643621793242011-09-26T06:16:00.000-07:002011-09-28T03:58:34.393-07:00WhaleBus Publishing - An example of Amazon SNS and Amazon SQS working togetherSo, lets talk a little bit about the Guts of WhaleBus...<br />
<div>
<br /></div>
<div>
The problem I'm solving at <a href="http://www.whalesharkmedia.com/">WhaleShark</a> is to communicate things across different applications, as in when something happens on www.retailmenot.com, we may want to create some content on www.deals.com. This fits naturally with Amazon Simple Notification Services. </div>
<div>
<br /></div>
<div>
Amazon SNS is simply a push mechanism. You have "Topics" that endpoints subscribe to. The endpoints can be of various types (HTTP post, HTTPS post, email with json payload, and Amazon Simple Queue Serviecs). We'll be creating a topic per type of event.<br />
<br />
To publish an event, consumers just call EventPublisher.Publish<t>(T @event). The event publisher will create a SNS topic if one doesn't exist, serialize the event, then publish it to Amazon. It's actually pretty simple.<br />
<br />
I really like this approach, because at WhaleShark, we have a quite a few non .Net websites. We can still push up events pretty much any way we want (they're just protocol buffer serialized messages) to the proper SNS topic, and all subscribers will be notified - regardless of platform.<br />
<br />
Back to .Net though... </t></div>
<div>
<br /></div>
<div>
For subscription, we use WhaleBusHost. It's simply an executable that pulls from SQS queues. All instances of WhaleBus require a config property to be set for ApplicationName. This application name is used to create an SQS queue. All instances of WhaleBus with the same ApplicationName will share an SQS queue. This is pretty cool, because you can throw more instances easily for scalability. <br />
<br />
After it creates the SQS queue, it then scans the application bin directory for all classes that implement IDomainEventHandler<>. It'll then pull out the type for the event, then have the SQS queue, subscribe to that Event's SNS Topic.<br />
<br />
Let's make a pretty picture...</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUBXdMwRSaQGt3UX-7sk-lNNhj2xn-Io1lDqHMV_E2-erce_xxa_-D6uRyAGZt9G09uVOoizSGTqRbfO0eqtscsIGvNedMcaXOe3O3sxM_XRp_2H-M6eEL4XifH28DhnZxAiZD3XuUVkw/s1600/Untitleddrawing%25283%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUBXdMwRSaQGt3UX-7sk-lNNhj2xn-Io1lDqHMV_E2-erce_xxa_-D6uRyAGZt9G09uVOoizSGTqRbfO0eqtscsIGvNedMcaXOe3O3sxM_XRp_2H-M6eEL4XifH28DhnZxAiZD3XuUVkw/s320/Untitleddrawing%25283%2529.jpg" width="320" /></a></div>
<div>
<br />
The green clouds are SNS topics, Orange ones are SQS queues for specific applications, the blue boxes are instances of WhaleBusHost.exe pulling from those queues.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Note that those green clouds can also send messages via HTTP posts, I just didn't show that in this diagram. I really think this an awesome approach to a very real problem. At WhaleShark we want to be able to quickly acquire new websites, and allow our current operations staff to use the tools they're currently using to moderate content and activity on the new sites. Instead of ripping the guts out of each of the new sites, this way we can simply write a publishing and subscribing code that publishes events our administration website will care about. The subscription code for those events will already be written. Picture anyone?<br />
<br />
Lets say WhaleShark buys the (I think) fictional website www.baconcoupons.com. Let's say it's a php site with a mysql database. Well, when one of our awesome operation folks or one of or super secret automated tools finds a killer deal on bacon....<br />
<br />
<a href="http://dealseekingmom.com/wp-content/uploads/2010/03/Oscar-Mayer-Bacon-Coupon.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="147" src="http://dealseekingmom.com/wp-content/uploads/2010/03/Oscar-Mayer-Bacon-Coupon.jpg" width="200" /></a><br />
We'd definitely want to have that content to show on baconcoupons.com right? <br />
Given a system kinda like this.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGXm6lhy2GqGoUEvgeij6HXoyHKRZNyRZMmBqXiYx_ukjg9Xim9LGnWn6Yh5lg9GihHIKm-jQq0KccbwkGMr3cJ3L7OeG4s4FFjMK7RC3Zs19u8ZSJb6XJni0uk4LrfqsgtxQKTyaYc_Q/s1600/Untitleddrawing%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGXm6lhy2GqGoUEvgeij6HXoyHKRZNyRZMmBqXiYx_ukjg9Xim9LGnWn6Yh5lg9GihHIKm-jQq0KccbwkGMr3cJ3L7OeG4s4FFjMK7RC3Zs19u8ZSJb6XJni0uk4LrfqsgtxQKTyaYc_Q/s320/Untitleddrawing%25281%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br /></div>
We've got a few options on how to make that happen.<br />
<br />
Write some quick mapping code that maps from DealFound event to what is expected as a post to sharebacon.php and add it as a new HTTP endpoint for the DealFound SNS endpoint like so.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiydh2j4SeF0iJNVJp_EI6-ADIcSpJkafzdl5MQhQKx8-tO_g4-ymuUzAyPhJNQBoN0cUyDlDkQdAUJeW0EfxlYkNnsmn2nF8ROJp3m5hla9hQg9RUSnGySnX0p44iQgLmQElOF26OHUo/s1600/Untitleddrawing%25283%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiydh2j4SeF0iJNVJp_EI6-ADIcSpJkafzdl5MQhQKx8-tO_g4-ymuUzAyPhJNQBoN0cUyDlDkQdAUJeW0EfxlYkNnsmn2nF8ROJp3m5hla9hQg9RUSnGySnX0p44iQgLmQElOF26OHUo/s320/Untitleddrawing%25283%2529.png" width="320" /></a></div>
<br />
<br />
OR<br />
Write an implementation of IDomainEventHandler<dealfound> that inserts directly into BaconCoupons.db</dealfound><br />
<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifqofAVK3s7v_8o0gXwbp3fln65PC8k7yOU3nlvdL-oVuC0OkPmB1Vuj5sALB5ad16yjtq26Au1jt8LGT7nrdxjdpAh3CogcRw9kvuWQ6SFxkz5Nd5BiYcOuIzbB6XAqaq7_vde4pqud0/s1600/Untitleddrawing%25285%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifqofAVK3s7v_8o0gXwbp3fln65PC8k7yOU3nlvdL-oVuC0OkPmB1Vuj5sALB5ad16yjtq26Au1jt8LGT7nrdxjdpAh3CogcRw9kvuWQ6SFxkz5Nd5BiYcOuIzbB6XAqaq7_vde4pqud0/s320/Untitleddrawing%25285%2529.png" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
I don't think there is a right or wrong approach here. It totally depends on the logic that exists in sharebaconcoupon.php, and the team structure (did we get a bunch of new php superheros when we acquired baconcoupons.com?). I'm guessing that we'd be more inclined to number 2, since any business logic that exists (checking for duplicates, etc), would be in .Net code, and could use libraries we've already written, but that doesn't mean that we can't (or won't) use the first approach.</div>
<div>
<br /></div>
<div>
Ok, I've gotta quit writing blogs and start writing code. Catch everyone later....</div>
Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com8tag:blogger.com,1999:blog-682843596462139829.post-68070485533131248572011-09-20T15:53:00.000-07:002011-09-28T03:58:51.684-07:00Using Messaging in Real World, Legacy Apps (and intro to WhaleBus) - Amazon SNS and Amazon SQSSo, if you wanna pull down the code before you read my blog post... It's here...<br />
<a href="https://github.com/elliottohara/WhaleBus">https://github.com/elliottohara/WhaleBus</a><br />
<br />
Also, If you're familiar with <a href="http://www.nservicebus.com/">NServiceBus</a>, and this code looks something like it, that's not an accident, I think <a href="http://www.udidahan.com/">Udi Dahan</a> made a awesome product, and I've used it with great success on a few projects. I've just got a few different problems at the new gig, and figured I'd give my own little service bus a shot. To be fair, it's pretty much the Ellemy.CQRS stuff I had been blogging about before moving here, but yeah, I spent the last week making it work a lot better to solve some problems we have here.<br />
<br />
We own coupon websites - a lot of them. The business plan is pretty much, buy them up, make them a little better (hopefully), throw a little better marketing at them, and make LOTS OF money. The state of our current systems looks something like this.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSN_2LyUwsLCwCVC2x6Tnp7qoIm7XWfEKIWUpHNtxsF-UVS822iYaAhWZiVOAAKsx9KXere81dM4Ibu_8YyATpb5U4q9pPXyf3FleEXtKfQaiCyTjNMD4Y7Mvxg5Mgbuliu4TeglC8_-Q/s1600/Untitleddrawing.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSN_2LyUwsLCwCVC2x6Tnp7qoIm7XWfEKIWUpHNtxsF-UVS822iYaAhWZiVOAAKsx9KXere81dM4Ibu_8YyATpb5U4q9pPXyf3FleEXtKfQaiCyTjNMD4Y7Mvxg5Mgbuliu4TeglC8_-Q/s320/Untitleddrawing.png" width="320" /></a></div>
<br />
<br />
Note that Deals2Buy.com, Deals.com, and Howzat where all greenfield applications. They're things our internal staff wrote from the ground up. They wrote them in a traditional N-Tier approach, they all share a Voldemort document store that we've named "Madre".<br />
<br />
<br />
We clearly have a lot of work to do. It's not cost effective to acquire a property, then train ops employees on how to use the admin features of each website. That's how things are being handled now. We've gotta change that..<br />
<br />
<br />
Certainly one approach is to migrate all properties to "Madre". It would "kinda" work. However, to me, that's a LOT of work. At the end of the day, ops needs to be able to use one application to update ANY website that we own. That's it. There's no requirement that all apps share a single database, just that the apps can be updated from a single application and that our execs can see the money we're making.<br />
<br />
Well…. That sounds like a great candidate for messaging huh?<br />
<br />
Think about it. The business logic for most (if not all) use cases already exists on every website we'd ever acquire, right? I mean, when someone shares a coupon on some php site with a mysql data base, there's already code on that site that updates the data base… Otherwise, well, we probably wouldn't be buying it.<br />
<br />
<br />
We already use Amazon Cloud services for lots of stuff… <a href="http://aws.amazon.com/sns/" title="http://aws.amazon.com/sns/">Simple Notification Service</a> is PERFECT for this. It supports a few different types of endpoints, including HTTPS post. So, our admin interface pushes some message (a domain event) out to SNS, and we configure SNS to have an HTTPS endpoint to some page on our site that takes the post. Here's a pretty picture I just drew using google documents.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjawkMpbevA9nS9VGt_thCKg7VOy3JpD0SMYjMT0F_apEMIF-NfGuaxYJ3Xy58BcnU2YaUUOD3CGpWuoH_r_NdEwqJl83L6_xolU3W725jSb5JWgsfLFb4auey122SEJzatScmOGwdtCrk/s1600/Untitleddrawing%25281%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjawkMpbevA9nS9VGt_thCKg7VOy3JpD0SMYjMT0F_apEMIF-NfGuaxYJ3Xy58BcnU2YaUUOD3CGpWuoH_r_NdEwqJl83L6_xolU3W725jSb5JWgsfLFb4auey122SEJzatScmOGwdtCrk/s320/Untitleddrawing%25281%2529.jpg" width="320" /></a></div>
<br />
<br />
This way, the only thing new we need to write on RetailMeNot is some HTTP endpoint that translates the CouponSharedOnSite message to call whatever code ALREADY EXISTS on RetailMeNot. Pretty simple, and makes the acquisition of new properties MUCH much easier (something that makes our CEO happy - and having a happy CEO is a good thing - trust me).<br />
<br />
<br />
Ok, so, that's awesome and all, but we've got a finance department, they like to say things like "Hey, we're 8% up in revenue over projection" and they wanna know if they're actually telling the truth. The picture above is awesome to get customers on RetailMeNot to see some coupon that one of our ops person created on the Howzat application, but what about when finance wants to know that someone actually clicked on one of our coupons?<br />
<br />
<br />
<br />
Well, we're working on a really nice database, that for the purposes of this blog, we're gonna say is totally complete. All we need to do is update that database when someone clicks on a link that goes to a vendor. Note that we want this to happen for ALL of our properties, and also note that our CEO love buying new websites. Also note that this is how pretty much EVERY coupon site makes revenue so they all record these outclicks in some way. All we need to do is add code to whatever exists on those properties that records the outclick to send a message out to SNS, and we'll write an app that subscribes to these messages.<br />
I'll draw you another picture, cause I think google drawings are cool.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTrzpUKlctz388aEBG20n-vc9KIzL5oLL38tRios-n7TxsKO8R8reJ94zoTudKrvr1DUH7mhvWyarXusv7G7pPvlKlJ4uQoxyZD-Dhfv6QmBWuY38gB-zM6kkbVWLhUwOFyeq1TwB8hj4/s1600/Untitleddrawing%25282%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTrzpUKlctz388aEBG20n-vc9KIzL5oLL38tRios-n7TxsKO8R8reJ94zoTudKrvr1DUH7mhvWyarXusv7G7pPvlKlJ4uQoxyZD-Dhfv6QmBWuY38gB-zM6kkbVWLhUwOFyeq1TwB8hj4/s320/Untitleddrawing%25282%2529.jpg" width="320" /></a></div>
<br />
<br />
Ok, cool pictures and everything right? But no one comes to my blog for pictures… Let's talk code.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">WhaleBus</span><br />
<br />
For now, I'm gonna just go over how to use my new little pet project, we'll dive deep into the code in some future blog.<br />
First of all, all the messages we'll be sending are <a href="http://www.udidahan.com/2009/06/14/domain-events-salvation/" title="http://www.udidahan.com/2009/06/14/domain-events-salvation/">DomainEvents</a>, not commands. This is because commands handling is (for the most part) application specific. If you don't understand this, well, read that link for DomainEvents. In a nutshell, DomainEvents communicate something that has ALREADY HAPPENED that other systems may care about, like "Customer Clicked On A Deal" or "Some deal just was just expired".<br />
<br />
<br />
When an event happens, applications want to do things based on that event. Let's express that in code.<br />
<br />
<pre class="csharpcode"><span class="kwrd">namespace</span> WhaleBus
{
<span class="kwrd">public</span> <span class="kwrd">interface</span> IDomainEvent{}
<span class="kwrd">public</span> <span class="kwrd">interface</span> IDomainEventHandler<<span class="kwrd">in</span> TDomainEvent> <span class="kwrd">where</span> TDomainEvent : IDomainEvent
{
<span class="kwrd">void</span> Handle(TDomainEvent @<span class="kwrd">event</span>);
}
}
</pre>
So, if we wanted to add a row to some db table when a customer clicked on a deal, we'd simply write the code in a class implementing IDomainEventHandler<CusomterClickedOnDeal>. I'll show some of this in a future blog, but for now, I'm gonna assume that you can figure that out.<br />
<br />
I had two real use cases in this blog, one where we where publishing an event, and one to subscribe to it.<br />
<span class="Apple-style-span" style="font-size: large;"><br />
</span><br />
<span class="Apple-style-span" style="font-size: large;">Publishing</span><br />
Here's the code you'd use to publish events….<br />
<br />
<pre class="csharpcode">Configure.With()
.ProtoBufSerializer()
.StructureMapBuilder()
.EventsInAssemblyContaining<StoreAddedToSite>();
var publisher = EventPublisherFactory.CreateEventPublisher();
</pre>
<pre class="csharpcode"></pre>
You'd probably do this during app startup and make your publisher a singleton since you only need one instance of him for the app. We can talk more about that later. This code tells WhaleBus to use GoogleProtocolBuffers for it's serialization mechanism (you can also use Json), and what assembly contains the events. If you've got events spread over multiple assemblies, just keep calling EventsAreInAssemblyContainingType<t>... It'll add them for you. To publish code you simply call Publish<TEvent>(TEvent @event) on publisher. WhaleBus will do all sorts of cool stuff for you that will discuss in a future blog. </t><br />
<br />
<br />
Here's the code that's in the Example on github for a publisher.<br />
<br />
<pre class="csharpcode"><span class="kwrd">using</span> System;
<span class="kwrd">using</span> Events.Stores;
<span class="kwrd">using</span> WhaleBus.Implementations;
<span class="kwrd">using</span> WhaleBus.Infrastructure;
<span class="kwrd">using</span> WhaleBus.Infrastructure.Publisher;
<span class="kwrd">namespace</span> WhaleBus.PublisherConsole
{
<span class="kwrd">class</span> Program
{
<span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)
{
Configure.With()
.ProtoBufSerializer()
.StructureMapBuilder(<span class="kwrd">new</span> PubSubSettingsFromFile(<span class="str">@"c:\specialsupersecret\elliott.ohara@gmail.com.txt"</span>))
.EventsInAssemblyContaining<StoreAddedToSite>();
var publisher = EventPublisherFactory.CreateEventPublisher();
<span class="kwrd">while</span>(<span class="kwrd">true</span>)
{
Console.WriteLine(<span class="str">"Enter Store Name"</span>);
var name = Console.ReadLine();
Console.WriteLine(<span class="str">"Enter Domain"</span>);
var domain = Console.ReadLine();
publisher.Publish(<span class="kwrd">new</span> StoreAddedToSite{Domain = domain, Name= name});
Console.WriteLine(<span class="str">"Published Event."</span>);
}
}
}
}
</pre>
<br />
<span class="Apple-style-span" style="font-size: large;">Subscribing</span> <br />
Now lets show a subscriber. We handle subscribers a bit differently. For now, I've written a console app called WhaleBusHost. To use it, you simply reference it, and write a class that implements the IConfigureThisSubscriber class. Here's the one that's in the example on github.<br />
<br />
<br />
<br />
<pre class="csharpcode"><span class="kwrd">using</span> Events.Stores;
<span class="kwrd">using</span> WhaleBus;
<span class="kwrd">using</span> WhaleBus.Implementations;
<span class="kwrd">using</span> WhaleBus.Infrastructure;
<span class="kwrd">using</span> WhaleBusHost;
<span class="kwrd">namespace</span> AnEndpoint
{
<span class="kwrd">public</span> <span class="kwrd">class</span> ConfigureMe : IConfigureThisSubscriber
{
<span class="kwrd">public</span> ConfigureMe()
{
Configure.With()
.ProtoBufSerializer()
.StructureMapBuilder(<span class="kwrd">new</span> PubSubSettingsFromFile(<span class="str">@"C:\SpecialSuperSecret\elliott.ohara@gmail.com.txt"</span>))
.EventsInAssemblyContaining<StoreAddedToSite>();
}
}
}
</pre>
<br />
That's really it. WhaleBus wires up all your subscriptions for any event that has a handler in every assembly in the applications bin folder. So as you add new functionality (handlers), you don't have to manually do much of anything. Just run WhaleBusHost.exe.<br />
<br />
We'll discuss how everything works on the next post.Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com4tag:blogger.com,1999:blog-682843596462139829.post-64478378186091561282011-04-16T13:14:00.000-07:002011-04-16T13:15:18.708-07:00Inactive for a bitSorry to everyone for not blogging for a while. Started a new gig and haven't had as much personal time.<br />
<br />
I do plan on continuing work with Ellemy.CQRS and have some pretty cool that I will be blogging about in the near future.<br />
<br />
Also, I DO have a email subscription to my my Amazon account and I checked in some code that let people use it. Well, some of the messages being pushed out to the cloud looked suspiciously like someone thought they where a really cool hacker. They weren't and whoever you are, since I'm not using SQL, attempted SQL injection attacks sent to an event bus that doesn't use SQL probably won't do anything except make me laugh when I read the email that I get for every message published.<br />
<br />
I went ahead and changed my Access Key so. So sorry folks, if you wanna run the tests, simply sign up for an amazon account at https://aws-portal.amazon.com/gp/aws/developer/registration/index.html and use use your account.<br />
<br />
I'll catch up with everyone soon!<br />
<br />
Happy Coding!<br />
EElliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com1tag:blogger.com,1999:blog-682843596462139829.post-9896545128675405812011-02-23T11:57:00.001-08:002011-02-23T11:57:56.888-08:00Size does matter, Crunching data for the cloud with a Google Protocol Buffer Serializer<p>So, most cloud services charge by some unit of size (Amazon charges by the Gig). If you’re working for a company that uses these services, then, well, duhh….send around the smallest bits of data you can, right?</p> <p>The way I’ve got Ellemy stuff for serialization now is that I have one little interface.</p> <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">interface</span> ISerializer</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">object</span> Deserialize(<span class="kwrd">string</span> input, Type desiredType);</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">object</span> DeserializeObject(<span class="kwrd">string</span> input);</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">string</span> Serialize(<span class="kwrd">object</span> input);</pre><pre><span class="lnum"> 6: </span> }</pre></div><br /><p>I hate the DeserializeObject thing, but, well, that’s for another time. </p><br /><p>I have <a href="https://github.com/elliottohara/Ellemy.CQRS/blob/master/Source/Ellemy.CQRS/Serializers/EllemyJsonSerializer.cs">one concrete implementation</a> of it currently, it uses JSON. I won’t show it here, because that’s not the point of the post. </p><br /><p>So, I’ve been looking at <a href="http://code.google.com/apis/protocolbuffers/">Google Protocol Buffers</a> and the <a href="http://code.google.com/p/protobuf-net/">.Net project</a> for it, so I figured I’d write a ISerializer that uses protocol buffers. It looks pretty nice, apparently about half the size of Json serialization and a bit quicker too. The not so nice part is that the data is not self descriptive, and you MUST know the type of the object being requested. Honestly though, that’s not a problem for most apps I write, Just send the type in the message, and we’re good right? </p><br /><p>So taking a look at the <a href="http://code.google.com/p/protobuf-net/wiki/GettingStarted">Protobuf-net wiki</a>, I see that we’ll need data contracts for our messages. </p><br /><p>Given a class like so.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> SomeThingToSerialize</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> SomeStringProperty { get; set; }</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> Guid SomeGuidProperty { get; set; }</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">int</span> SomeIntProperty { get; set; }</pre><pre><span class="lnum"> 6: </span> }</pre></div><br /><p>We’d need a class like this so that Protocol Buffer can do its thing.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[ProtoContract]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> SomeThingToSerialize</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> [ProtoMember(1)]</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> SomeStringProperty { get; set; }</pre><pre><span class="lnum"> 6: </span> [ProtoMember(2)]</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> Guid SomeGuidProperty { get; set; }</pre><pre><span class="lnum"> 8: </span> [ProtoMember(3)]</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">int</span> SomeIntProperty { get; set; }</pre><pre><span class="lnum"> 10: </span> }</pre></div><br /><p>I don’t want to muddy my problem domains with sterilization specific attributes, so I think what I’ll do is actually generate the classes for the messages on the fly – let’s play in CodeDom a bit!</p><br /><p>Test</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [TestFixture]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> ProtocolBufferGenerator_tests</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> ProtocolBufferDataContractGenerator _generator;</pre><pre class="alt"><span class="lnum"> 5: </span> [SetUp]</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> arrange()</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _generator = <span class="kwrd">new</span> ProtocolBufferDataContractGenerator();</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> [Test]</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> message_is_decorated_with_ProtoContract_attribute()</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> var random = <span class="kwrd">new</span> Random();</pre><pre><span class="lnum"> 14: </span> var testThing = <span class="kwrd">new</span> SomeThingToSerialize</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> SomeGuidProperty = Guid.NewGuid(),</pre><pre class="alt"><span class="lnum"> 17: </span> SomeIntProperty = random.Next(),</pre><pre><span class="lnum"> 18: </span> SomeStringProperty = <span class="str">"Blah"</span></pre><pre class="alt"><span class="lnum"> 19: </span> };</pre><pre><span class="lnum"> 20: </span> var protoClass = _generator.GenerateProtoFor(testThing);</pre><pre class="alt"><span class="lnum"> 21: </span> var foundProtoContractAttribute = protoClass.GetType()</pre><pre><span class="lnum"> 22: </span> .GetCustomAttributes(<span class="kwrd">false</span>)</pre><pre class="alt"><span class="lnum"> 23: </span> .Any(attribute => attribute.GetType().Name == <span class="str">"ProtoContractAttribute"</span>);</pre><pre><span class="lnum"> 24: </span> </pre><pre class="alt"><span class="lnum"> 25: </span> Assert.IsTrue(foundProtoContractAttribute);</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre></div><br /><p>Yeah, just what the test says, it just checks that the object this ProtocolBufferDataContractGenerator is decorated with the correct attribute. Lets make it so.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> ProtocolBufferDataContractGenerator</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> _codeContractsNamespace = <span class="str">"Ellemy.CQRS.Serializers.GoogleProtocolBuffers.Contracts"</span>;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">object</span> GenerateProtoFor<T>(T thing)</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> var nameSpace = <span class="kwrd">new</span> CodeNamespace(_codeContractsNamespace);</pre><pre class="alt"><span class="lnum"> 9: </span> nameSpace.Imports.Add(<span class="kwrd">new</span> CodeNamespaceImport(<span class="str">"ProtoBuf"</span>));</pre><pre><span class="lnum"> 10: </span> nameSpace.Imports.Add(<span class="kwrd">new</span> CodeNamespaceImport(thing.GetType().Namespace));</pre><pre class="alt"><span class="lnum"> 11: </span> var @<span class="kwrd">class</span> = <span class="kwrd">new</span> CodeTypeDeclaration(thing.GetType().Name)</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> IsClass = <span class="kwrd">true</span>,</pre><pre><span class="lnum"> 14: </span> Attributes = MemberAttributes.Public</pre><pre class="alt"><span class="lnum"> 15: </span> };</pre><pre><span class="lnum"> 16: </span> var protoContractAttribute = <span class="kwrd">new</span> CodeAttributeDeclaration(<span class="str">"ProtoContract"</span>);</pre><pre class="alt"><span class="lnum"> 17: </span> @<span class="kwrd">class</span>.CustomAttributes.Add(protoContractAttribute);</pre><pre><span class="lnum"> 18: </span> nameSpace.Types.Add(@<span class="kwrd">class</span>);</pre><pre class="alt"><span class="lnum"> 19: </span> var compileUnit = <span class="kwrd">new</span> CodeCompileUnit();</pre><pre><span class="lnum"> 20: </span> compileUnit.Namespaces.Add(nameSpace);</pre><pre class="alt"><span class="lnum"> 21: </span> compileUnit.ReferencedAssemblies.Add(<span class="str">"protobuf-net.dll"</span>);</pre><pre><span class="lnum"> 22: </span> var thingAssembly = thing.GetType().Assembly;</pre><pre class="alt"><span class="lnum"> 23: </span> var assemblyToAdd = thingAssembly.GetName().Name + <span class="str">".dll"</span>;</pre><pre><span class="lnum"> 24: </span> compileUnit.ReferencedAssemblies.Add(assemblyToAdd);</pre><pre class="alt"><span class="lnum"> 25: </span> var parameters = <span class="kwrd">new</span> CompilerParameters {GenerateInMemory = <span class="kwrd">true</span>};</pre><pre><span class="lnum"> 26: </span> </pre><pre class="alt"><span class="lnum"> 27: </span> var provider = <span class="kwrd">new</span> CSharpCodeProvider();</pre><pre><span class="lnum"> 28: </span> var results = provider.CompileAssemblyFromDom(parameters,compileUnit);</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">if</span>(results.Errors.Count != 0)</pre><pre><span class="lnum"> 30: </span> {</pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(results.Errors[0].ErrorText);</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span> </pre><pre><span class="lnum"> 34: </span> <span class="kwrd">return</span></pre><pre class="alt"><span class="lnum"> 35: </span> results.CompiledAssembly.CreateInstance(<span class="kwrd">string</span>.Format(<span class="str">"{0}.{1}"</span>, _codeContractsNamespace,thing.GetType().Name));</pre><pre><span class="lnum"> 36: </span> </pre><pre class="alt"><span class="lnum"> 37: </span> </pre><pre><span class="lnum"> 38: </span> }</pre><pre class="alt"><span class="lnum"> 39: </span> </pre><pre><span class="lnum"> 40: </span> </pre><pre class="alt"><span class="lnum"> 41: </span> }</pre></div><br /><p>Lots of code there, but it does work! If you’re not familiar with code dom, it’s actually not very complicated. We’re just using C# to gen c#, and the code is fairly self documenting. </p><br /><p>Pretty neat that we’re leaving the assembly in memory, but I’m thinking that in the future, we might wanna save that assembly, and not pay the cost of genning that class multiple times, but we’ll get to that soon enough.</p><br /><p>So far, we’re creating an empty class decorated with the ProtoContract attribute, which is perfectly worthless since we don’t have the properties. Let’s fix that. </p><br /><p>First, make sure we’re adding properties on the message.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> properties_are_added()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var expectedNumberOfProperties = <span class="kwrd">typeof</span>(SomeThingToSerialize).GetProperties().Count();</pre><pre class="alt"><span class="lnum"> 5: </span> var actualNumberOfProperties = _protoClass.GetType().GetProperties().Count();</pre><pre><span class="lnum"> 6: </span> Assert.AreEqual(expectedNumberOfProperties,actualNumberOfProperties);</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br /><p>To make it pass I wrote this little method and called it on line 19 (actually doesn’t matter where) from the GenerateProto for method.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> AddProperties<T>(T thing, CodeTypeDeclaration @<span class="kwrd">class</span>)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">foreach</span> (var propertyInfo <span class="kwrd">in</span> thing.GetType().GetProperties().OrderBy(p => p.Name))</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> var field = <span class="kwrd">new</span> CodeMemberField</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> Type = <span class="kwrd">new</span> CodeTypeReference(propertyInfo.PropertyType.FullName),</pre><pre><span class="lnum"> 8: </span> Attributes = MemberAttributes.Private,</pre><pre class="alt"><span class="lnum"> 9: </span> Name = <span class="str">"_"</span> + propertyInfo.Name</pre><pre><span class="lnum"> 10: </span> };</pre><pre class="alt"><span class="lnum"> 11: </span> @<span class="kwrd">class</span>.Members.Add(field);</pre><pre><span class="lnum"> 12: </span> var @property = <span class="kwrd">new</span> CodeMemberProperty</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> Name = propertyInfo.Name,</pre><pre class="alt"><span class="lnum"> 15: </span> HasGet = <span class="kwrd">true</span>,</pre><pre><span class="lnum"> 16: </span> HasSet = <span class="kwrd">true</span>,</pre><pre class="alt"><span class="lnum"> 17: </span> Type = <span class="kwrd">new</span> CodeTypeReference(propertyInfo.PropertyType.FullName),</pre><pre><span class="lnum"> 18: </span> Attributes = MemberAttributes.Public,</pre><pre class="alt"><span class="lnum"> 19: </span> };</pre><pre><span class="lnum"> 20: </span> var getter = <span class="kwrd">new</span> CodeSnippetStatement(String.Format(<span class="str">"return _{0};"</span>,propertyInfo.Name));</pre><pre class="alt"><span class="lnum"> 21: </span> @property.GetStatements.Add(getter);</pre><pre><span class="lnum"> 22: </span> var setter = <span class="kwrd">new</span> CodeSnippetStatement(String.Format(<span class="str">"_{0} = value;"</span>, propertyInfo.Name));</pre><pre class="alt"><span class="lnum"> 23: </span> @property.SetStatements.Add(setter);</pre><pre><span class="lnum"> 24: </span> @<span class="kwrd">class</span>.Members.Add(@property);</pre><pre class="alt"><span class="lnum"> 25: </span> }</pre><pre><span class="lnum"> 26: </span> }</pre></div><br /><br /><br /><br /><br /><br /><br /><p>Ok, so now we need to decorate each property with a ProtoMember attribute. Here’s the test.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> every_property_on_the_message_is_decorated_with_a_ProtoMember_attribute()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">foreach</span>(var propertyInfo <span class="kwrd">in</span> _protoClass.GetType().GetProperties())</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> var foundProtoMemberAttribute = _protoClass.GetType().GetProperty(propertyInfo.Name)</pre><pre><span class="lnum"> 8: </span> .GetCustomAttributes(<span class="kwrd">false</span>)</pre><pre class="alt"><span class="lnum"> 9: </span> .Any(attribute => attribute.GetType().Name == <span class="str">"ProtoMemberAttribute"</span>);</pre><pre><span class="lnum"> 10: </span> Assert.IsTrue(foundProtoMemberAttribute);</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> }</pre><pre class="alt"><span class="lnum"> 13: </span> }</pre></div><br /><br /><p>And now lets make it pass. I wrote this little method and added it as in that loop in AddProperties.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> AddProtoMemberAttribute(CodeMemberProperty property, <span class="kwrd">int</span> memberNumber)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var protoBuffAttribute = <span class="kwrd">new</span> CodeAttributeDeclaration(<span class="str">"ProtoMember"</span>);</pre><pre><span class="lnum"> 4: </span> var attributeArgument = <span class="kwrd">new</span> CodeAttributeArgument(<span class="kwrd">new</span> CodePrimitiveExpression(memberNumber));</pre><pre class="alt"><span class="lnum"> 5: </span> protoBuffAttribute.Arguments.Add(attributeArgument);</pre><pre><span class="lnum"> 6: </span> @property.CustomAttributes.Add(protoBuffAttribute);</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br /><p>Sweet, now we’re generating the DataContracts! We’re still not actually serializing objects though. We’re just making stuff that Google Protocol Buffers can work with. </p><br /><p>Lets add a new test.</p><br /><p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [TestFixture]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> using_the_GoogleProtocolBuffer_serializer</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> Serializer _serializer;</pre><pre class="alt"><span class="lnum"> 5: </span> [SetUp]</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Arrange()</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _serializer = <span class="kwrd">new</span> Serializer();</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> [Test]</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> serialize_an_non_DataContract_class()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> var testThing = <span class="kwrd">new</span> TestThing { Guid = Guid.NewGuid(), Int = 1, String = <span class="str">"Some String"</span>};</pre><pre class="alt"><span class="lnum"> 15: </span> var output = _serializer.Serialize(testThing);</pre><pre><span class="lnum"> 16: </span> Assert.IsNotNullOrEmpty(output);</pre><pre class="alt"><span class="lnum"> 17: </span> Console.WriteLine(output);</pre><pre><span class="lnum"> 18: </span> }</pre></div></p><br /><p>Ok, so not much going on here, we’re just testing that the output is actually not null, and (by extension), that the Serializer class doesn’t throw an exception. </p><br /><p>Here’s what I did to make it work.</p><br /><div class="csharpcode"> </div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> Serializer : ISerializer </pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> ProtocolBufferDataContractGenerator _protocolBufferDataContractGenerator;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> Serializer()</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _protocolBufferDataContractGenerator = <span class="kwrd">new</span> ProtocolBufferDataContractGenerator();</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">object</span> Deserialize(<span class="kwrd">string</span> input, Type desiredType)</pre><pre><span class="lnum"> 10: </span> {<span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException(<span class="str">"patience is a virtue"</span>); }</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">object</span> DeserializeObject(<span class="kwrd">string</span> input)</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> NotSupportedException(<span class="str">"nope, dis dont werk "</span>);</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Serialize(<span class="kwrd">object</span> input)</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> var t = _protocolBufferDataContractGenerator.GenerateProtoFor(input);</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">foreach</span> (var property <span class="kwrd">in</span> input.GetType().GetProperties())</pre><pre><span class="lnum"> 20: </span> {</pre><pre class="alt"><span class="lnum"> 21: </span> var setterForT = t.GetType().GetProperty(property.Name);</pre><pre><span class="lnum"> 22: </span> var <span class="kwrd">value</span> = property.GetValue(input, <span class="kwrd">null</span>);</pre><pre class="alt"><span class="lnum"> 23: </span> setterForT.SetValue(t, <span class="kwrd">value</span>,<span class="kwrd">null</span>);</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">string</span> data;</pre><pre><span class="lnum"> 26: </span> <span class="kwrd">using</span> (var writer = <span class="kwrd">new</span> MemoryStream())</pre><pre class="alt"><span class="lnum"> 27: </span> {</pre><pre><span class="lnum"> 28: </span> ProtoBuf.Serializer.NonGeneric.Serialize(writer, t);</pre><pre class="alt"><span class="lnum"> 29: </span> writer.Position = 0;</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">using</span> (var reader = <span class="kwrd">new</span> StreamReader(writer,Encoding.ASCII))</pre><pre class="alt"><span class="lnum"> 31: </span> {</pre><pre><span class="lnum"> 32: </span> data = reader.ReadToEnd();</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre><pre><span class="lnum"> 34: </span> }</pre><pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">return</span> data;</pre><pre><span class="lnum"> 36: </span> }</pre><pre class="alt"><span class="lnum"> 37: </span> }</pre><pre><span class="lnum"> 38: </span>}</pre></div><br /><p>on line 18, I simply get an instance of the DataContract class (we just saw what’s in there). I then loop through all the types on the DataContract via reflection and set all the values appropriately. </p><br /><p>On line 26-34, we’re just using Protobuff-net to serialize the object, and return the results.</p><br /><p>Not horribly complicated, but we’re still not done, because we can’t yet deserialize. </p><br /><p>New test.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> serialize_then_deserialize()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var testThing = <span class="kwrd">new</span> TestThing { Guid = Guid.NewGuid(), Int = 1, String = <span class="str">"Some String"</span> };</pre><pre class="alt"><span class="lnum"> 5: </span> var output = _serializer.Serialize(testThing);</pre><pre><span class="lnum"> 6: </span> var result = (TestThing)_serializer.Deserialize(output, <span class="kwrd">typeof</span>(TestThing));</pre><pre class="alt"><span class="lnum"> 7: </span> Assert.AreEqual(testThing.Guid, result.Guid);</pre><pre><span class="lnum"> 8: </span> Assert.AreEqual(testThing.String, result.String);</pre><pre class="alt"><span class="lnum"> 9: </span> Assert.AreEqual(testThing.Int, result.Int);</pre><pre><span class="lnum"> 10: </span> Assert.AreEqual(testThing.Enum1, result.Enum1);</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre></div><br /><br /><p>Yeah, now we’re testing that it actually works. Lets make this pass.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">object</span> Deserialize(<span class="kwrd">string</span> input, Type desiredType)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var bytes = ASCIIEncoding.ASCII.GetBytes(input);</pre><pre><span class="lnum"> 4: </span> var @<span class="kwrd">event</span> = Activator.CreateInstance(desiredType);</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">using</span> (var stream = <span class="kwrd">new</span> MemoryStream(bytes))</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> var thisSucksINeedToFixIt = Activator.CreateInstance(desiredType);</pre><pre><span class="lnum"> 8: </span> var protobufferType = _protocolBufferDataContractGenerator.GenerateProtoFor(thisSucksINeedToFixIt).GetType();</pre><pre class="alt"><span class="lnum"> 9: </span> var protobuffer = ProtoBuf.Serializer.NonGeneric.Deserialize(protobufferType, stream);</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">foreach</span> (var fieldInfo <span class="kwrd">in</span> protobuffer.GetType().GetProperties())</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> var setter = desiredType.GetProperty(fieldInfo.Name);</pre><pre><span class="lnum"> 14: </span> var <span class="kwrd">value</span> = fieldInfo.GetValue(protobuffer,<span class="kwrd">null</span>);</pre><pre class="alt"><span class="lnum"> 15: </span> setter.SetValue(@<span class="kwrd">event</span>, <span class="kwrd">value</span>, <span class="kwrd">null</span>);</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">return</span> @<span class="kwrd">event</span>;</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre></div><br /><p>Not too bad, huh?</p><br /><p>Except for that thisSucksINeedToFixIt variable. I should defer to some IOC container or something there, but honestly, I don’t think events should have dependencies – yeah, argument for a different day. </p><br /><p>Ok, so the test passes except for the Guid assertion. Gunna go take a look at that now. </p><br /><p>Ok, after a lot of googling I realized that the issue was my ASCII encoding. I doubt anyone reading this blog cares too much, but what I did to fix it was to use BitConverter.ToString() method when Serializing and <a href="http://stackoverflow.com/questions/1230303/bitconverter-tostring-in-reverse">some code I ripped off off stack overflow</a> to deserialize. If you’re interested, yeah go git the code!</p><br /><p>So there we go, I like it, but there’s still a lot of work to go before it’s ready for prime time. </p><br /><p>Some things I have to get done.</p><br /><ul><br /><li>Handle versioning well (the order is very important, if you added a new property with the current implementation in the middle of a class, it would break old stuff)</li><br /><li>Maybe even output .proto files, and have the GoogleProtocolBufferGenerator use them if they exist, that way it’ll work by default with conventions, but allow customization, and it’ll save on the overhead of generating the contract.</li></ul><br /><p>Have fun with it! Thoughts?</p> Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com7tag:blogger.com,1999:blog-682843596462139829.post-8335151969784419562011-02-16T15:30:00.000-08:002011-02-16T13:39:40.893-08:00What goes up must come down, Events from the cloud with the Amazon SQS Ellemy.CQRS subscriber<p>Ok, so I’ve got a nice little <a href="http://blog.elliottohara.com/2011/02/ellemycqrs-goes-to-cloud-amazon-sns.html">Amazon SNS publisher</a> working for Ellemy.CQRS. So I figured I wanted to work some more with Amazon services and write a subscriber. Amazon makes a <a href="http://aws.amazon.com/sqs/">Simple Queue Service</a> that was build to work seamlessly with their <a href="http://aws.amazon.com/sns/">SNS</a>, so I figured, why fight it? </p><p>Lets get to work. In keeping with the spirit of the other examples I’ve written for Ellemy.CQRS, I’m going to make this really simple. In fact I’m gunna write it to behave the same way as the <a href="http://blog.elliottohara.com/2011/02/subscribing-you-know-because-you-care.html">NServiceBus Subscriber</a> does. </p><p>Here’s the user story… <blockquote><p>As a weird person who looks at console windows, I want to see a display on my screen that shows the message text and message id every time a message is submitted on the website so that I can clap my hands and do the funky chicken.</p></blockquote><p>For now, I’m just gunna put the subscription stuff in the Amazon Publishing assembly (just like I did for NService bus). I don’t really see a reason not to, and since I’m the boss of this product, what I say goes. <p>Since I’ve done a lotta leg work in <a href="http://blog.elliottohara.com/2011/02/playing-around-in-cloud-some.html">my last post</a>, I’m familiar with what we’re gunna need a subscriber to do. <ol><li>Create a Queue if it doesn’t exist</li>
<li>Set an Attribute on the new Queue that gives the Topic permission to send messages to it</li>
<li>Subscribe to topics from the Topic ARN</li>
</ol><p>So, lets get started. I wrote this little test.</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [SetUp]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Arrange()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> _config = Configure.With().AmazonPublisher();</pre><pre class="alt"><span class="lnum"> 5: </span> _config</pre><pre><span class="lnum"> 6: </span> .AwsAccessKeyId(awsKey)</pre><pre class="alt"><span class="lnum"> 7: </span> .AwsSecretKey(secret)</pre><pre><span class="lnum"> 8: </span> .TopicArn(topicArn)</pre><pre class="alt"><span class="lnum"> 9: </span> .QueueName(tempQueueName);</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> _amazonClient = AWSClientFactory.CreateAmazonSQSClient(awsKey, secret);</pre><pre><span class="lnum"> 12: </span> }</pre><pre class="alt"><span class="lnum"> 13: </span> [TearDown]</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> CleanUp()</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> var listQueuesRequest = <span class="kwrd">new</span> ListQueuesRequest().WithQueueNamePrefix(<span class="str">"TESTING"</span>);</pre><pre class="alt"><span class="lnum"> 17: </span> var result = _amazonClient.ListQueues(listQueuesRequest);</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">foreach</span> (var queueUrl <span class="kwrd">in</span> result.ListQueuesResult.QueueUrl)</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> _amazonClient.DeleteQueue(<span class="kwrd">new</span> DeleteQueueRequest().WithQueueUrl(queueUrl));</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> </pre><pre class="alt"><span class="lnum"> 23: </span> </pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> [Test]</pre><pre><span class="lnum"> 26: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> it_will_create_the_queue()</pre><pre class="alt"><span class="lnum"> 27: </span> {</pre><pre><span class="lnum"> 28: </span> <span class="rem">//lets just leave the logic in the ctor, it'll be singleton for consumers</span></pre><pre class="alt"><span class="lnum"> 29: </span> var subscriber = <span class="kwrd">new</span> AmazonSqsSubscriber(_config);</pre><pre><span class="lnum"> 30: </span> </pre><pre class="alt"><span class="lnum"> 31: </span> var listQueuesResponse = _amazonClient.ListQueues(<span class="kwrd">new</span> ListQueuesRequest());</pre><pre><span class="lnum"> 32: </span> var foundExpectedQueue = <span class="kwrd">false</span>;</pre><pre class="alt"><span class="lnum"> 33: </span> <span class="kwrd">foreach</span> (var queueUrl <span class="kwrd">in</span> listQueuesResponse.ListQueuesResult.QueueUrl)</pre><pre><span class="lnum"> 34: </span> {</pre><pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">if</span> (queueUrl.Contains(tempQueueName))</pre><pre><span class="lnum"> 36: </span> foundExpectedQueue = <span class="kwrd">true</span>;</pre><pre class="alt"><span class="lnum"> 37: </span> }</pre><pre><span class="lnum"> 38: </span> Assert.IsTrue(foundExpectedQueue,<span class="str">"Queue was not found"</span>);</pre><pre class="alt"><span class="lnum"> 39: </span> </pre><pre><span class="lnum"> 40: </span> </pre><pre class="alt"><span class="lnum"> 41: </span> </pre><pre><span class="lnum"> 42: </span> }</pre></div><br />
<p>Basically, we just spin up the Subscriber and use Amazon’s SDK to check that it did create the queue, then we kill the queue in a teardown. Note that Amazon gets all pissy if you try and create a queue with the same name as one you just deleted within 60 seconds, I got around that by (you guessed it!) waiting 60 seconds before I ran the test again. I guess we can make a unique name for the queue and using that, maybe I’ll do that….</p><br />
<p>Lets make this puppy pass. Here’s what I did.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> AmazonSqsSubscriber</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> AmazonConfig _config;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> AmazonSQS _client;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> AmazonSqsSubscriber(AmazonConfig config)</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _config = config;</pre><pre class="alt"><span class="lnum"> 9: </span> SetupQueue();</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> SetupQueue()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(_config.SqsQueueUrl)) <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 15: </span> _client = Amazon.AWSClientFactory.CreateAmazonSQSClient(_config.AccessKeyId, _config.SecretKey);</pre><pre><span class="lnum"> 16: </span> var listQueuesResponse = _client.ListQueues(<span class="kwrd">new</span> ListQueuesRequest {QueueNamePrefix = _config.SqsQueueName});</pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">string</span> queueUrl;</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">if</span>(listQueuesResponse.ListQueuesResult.QueueUrl.Count == 0)</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> var response = _client.CreateQueue(<span class="kwrd">new</span> CreateQueueRequest{QueueName = _config.SqsQueueName});</pre><pre class="alt"><span class="lnum"> 21: </span> queueUrl = response.CreateQueueResult.QueueUrl;</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">else</span></pre><pre><span class="lnum"> 24: </span> {</pre><pre class="alt"><span class="lnum"> 25: </span> queueUrl = listQueuesResponse.ListQueuesResult.QueueUrl[0];</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> _config.SqsQueueUrl = queueUrl;</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span>}</pre></div><br />
<p> </p><br />
<p>Kinda ugly, but yeah, that just made the test pass. I can’t see much I can do to clean it up except a little resharper extract method stuff, I won’t bore you with what I did.</p><br />
<p>Next up, lets make that subscriber actually receive messages. Amazon gives you a “RecieveMessage” call you can make, so we’ll just set up a little loop that’ll just continually call that, and dump out the results. Actually, lets just write the “test”, it’s not really a test, since there’s no assertion, I just wanna see stuff get written to my console. </p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> it_will_parse_messages()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var subscriber = <span class="kwrd">new</span> AmazonSqsSubscriber(_config);</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> var messageRequest = <span class="kwrd">new</span> SendMessageRequest()</pre><pre><span class="lnum"> 8: </span> .WithMessageBody(<span class="str">"This is a test"</span>)</pre><pre class="alt"><span class="lnum"> 9: </span> .WithQueueUrl(_config.SqsQueueUrl);</pre><pre><span class="lnum"> 10: </span> _amazonClient.SendMessage(messageRequest);</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> subscriber.Start();</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> subscriber.Stop();</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> }</pre></div><br />
<p>When I run this, I just wanna see that “This is a test” dumped out in my console, I guess I could make it so we inject some interface for Console, but I’m too lazy to do that right now. Let’s go make this puppy pass.</p><br />
<p>Here’s what I added.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [ThreadStatic]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">private</span> <span class="kwrd">bool</span> _stopped;</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Stop()</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> Console.WriteLine(<span class="str">"Stopping...."</span>);</pre><pre><span class="lnum"> 6: </span> _stopped = <span class="kwrd">true</span>;</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Start()</pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> ThreadPool.QueueUserWorkItem(<span class="kwrd">delegate</span> { BeginWork(); });</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> BeginWork()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">while</span>(!_stopped)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> ThreadPool.QueueUserWorkItem(<span class="kwrd">delegate</span> { DoWork(); });</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> DoWork()</pre><pre><span class="lnum"> 20: </span> {</pre><pre class="alt"><span class="lnum"> 21: </span> var response = _client.ReceiveMessage(<span class="kwrd">new</span> ReceiveMessageRequest { QueueUrl = _config.SqsQueueUrl });</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">if</span> (!response.IsSetReceiveMessageResult())</pre><pre class="alt"><span class="lnum"> 23: </span> {</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 25: </span> }</pre><pre><span class="lnum"> 26: </span> var messageResult = response.ReceiveMessageResult;</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">foreach</span> (var message <span class="kwrd">in</span> messageResult.Message)</pre><pre><span class="lnum"> 28: </span> {</pre><pre class="alt"><span class="lnum"> 29: </span> Console.WriteLine(message.Body);</pre><pre><span class="lnum"> 30: </span> }</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span> }</pre></div><br />
<br />
<br />
<br />
<br />
<p>Run the test, and here’s what I see. </p><br />
<p><a href="http://lh3.ggpht.com/_hl5c3g00lJk/TVwNhOVm0pI/AAAAAAAAAPU/x74ePbQaomg/s1600-h/subscribertest%5B3%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="subscribertest" border="0" alt="subscribertest" src="http://lh6.ggpht.com/_hl5c3g00lJk/TVwNhSP8AII/AAAAAAAAAPY/MmD9siZgbJY/subscribertest_thumb%5B1%5D.png?imgmax=800" width="412" height="288"></a></p><br />
<p>Looks good, although, I KNOW there’s a bug, because Amazon leaves the message up there. I’ll write another test that demonstrates the bug, but since I was too lazy to inject some “Console writer” thing that I could do assertions on, I’ll just have to use my eyes to see that it’s “failing”.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_message_is_only_processed_once()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var subscriber = <span class="kwrd">new</span> AmazonSqsSubscriber(_config);</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> var messageRequest = <span class="kwrd">new</span> SendMessageRequest()</pre><pre><span class="lnum"> 8: </span> .WithMessageBody(<span class="str">"This is a test"</span>)</pre><pre class="alt"><span class="lnum"> 9: </span> .WithQueueUrl(_config.SqsQueueUrl);</pre><pre><span class="lnum"> 10: </span> _amazonClient.SendMessage(messageRequest);</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> subscriber.Start();</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> Thread.Sleep(15000);</pre><pre class="alt"><span class="lnum"> 15: </span> subscriber.Stop();</pre><pre><span class="lnum"> 16: </span> }</pre></div><br />
<br />
<p>Ok… so when I run that… For some reason, it does exactly what I expect. I think it’s got something to do with Amazon putting a lock on the message or something. Regardless I know (from <a href="http://aws.amazon.com/sqs/faqs/#Why_are_there_separate_ReceiveMessage_and_DeleteMessage_operations">Amazon Documentation</a>) that I need to manually delete the message after I’ve processed it. So, whatever, I’m gunna go do that. Here’s what I did.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">private</span> <span class="kwrd">void</span> DoWork()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var response = _client.ReceiveMessage(<span class="kwrd">new</span> ReceiveMessageRequest { QueueUrl = _config.SqsQueueUrl });</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (!response.IsSetReceiveMessageResult())</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> var messageResult = response.ReceiveMessageResult;</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">foreach</span> (var message <span class="kwrd">in</span> messageResult.Message)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> Console.WriteLine(message.Body);</pre><pre><span class="lnum"> 12: </span> Delete(message);</pre><pre class="alt"><span class="lnum"> 13: </span> }</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> Delete(Message message)</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> var deleteRequest = <span class="kwrd">new</span> DeleteMessageRequest()</pre><pre class="alt"><span class="lnum"> 19: </span> .WithReceiptHandle(message.ReceiptHandle)</pre><pre><span class="lnum"> 20: </span> .WithQueueUrl(_config.SqsQueueUrl);</pre><pre class="alt"><span class="lnum"> 21: </span> _client.DeleteMessage(deleteRequest);</pre><pre><span class="lnum"> 22: </span> }</pre></div><br />
<p> </p><br />
<p>Everything looks like it’s working. </p><br />
<p>Ok, technically, we’ve met the requirements of the AC, but we don’t wanna couple up that Console.WriteLine stuff, we wanna make this thing work so we can make Domain Event handlers handle the events that are sent in messages, I mean that was the whole point of this, right?</p><br />
<p>First of all, we’re not yet subscribing to a topic, lets rip off the code from my last post to do that.</p><br />
<p>Wow…. This made it ugly! But it’s working for now, we’ll clean up in a bit.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> SetupQueue()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">if</span> (TheQueueIsAlreadySet()) <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 4: </span> var queueUrl = GetOrCreateQueueUrl();</pre><pre class="alt"><span class="lnum"> 5: </span> _config.SqsQueueUrl = queueUrl;</pre><pre><span class="lnum"> 6: </span> SubscribeToTopic();</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> SubscribeToTopic()</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> SetPermissions();</pre><pre><span class="lnum"> 12: </span> var getArnRequest = <span class="kwrd">new</span> GetQueueAttributesRequest().WithQueueUrl(_config.SqsQueueUrl).WithAttributeName(<span class="str">"QueueArn"</span>);</pre><pre class="alt"><span class="lnum"> 13: </span> var clientArn = _client.GetQueueAttributes(getArnRequest).GetQueueAttributesResult.Attribute[0].Value;</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> var sns = Amazon.AWSClientFactory.CreateAmazonSNSClient(_config.AccessKeyId,</pre><pre><span class="lnum"> 16: </span> _config.SecretKey);</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> var subscriptionRequest = <span class="kwrd">new</span> SubscribeRequest()</pre><pre class="alt"><span class="lnum"> 19: </span> .WithEndpoint(clientArn)</pre><pre><span class="lnum"> 20: </span> .WithProtocol(<span class="str">"sqs"</span>)</pre><pre class="alt"><span class="lnum"> 21: </span> .WithTopicArn(_config.TopicAccessResourceName);</pre><pre><span class="lnum"> 22: </span> </pre><pre class="alt"><span class="lnum"> 23: </span> sns.Subscribe(subscriptionRequest);</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> </pre><pre><span class="lnum"> 26: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> SetPermissions()</pre><pre class="alt"><span class="lnum"> 27: </span> {</pre><pre><span class="lnum"> 28: </span> var setQueueAttributeRequest = <span class="kwrd">new</span> SetQueueAttributesRequest()</pre><pre class="alt"><span class="lnum"> 29: </span> .WithQueueUrl(_config.SqsQueueUrl)</pre><pre><span class="lnum"> 30: </span> .WithAttribute(<span class="kwrd">new</span> Attribute { Name = <span class="str">"Policy"</span>, Value = AllowSnsAttribute() });</pre><pre class="alt"><span class="lnum"> 31: </span> _client.SetQueueAttributes(setQueueAttributeRequest);</pre><pre><span class="lnum"> 32: </span> }</pre></div><br />
<p>You’ll recognize a lot of this code from my last blog, so it’s not new. I really need to move out the concept of subscribing in it’s own little class, but I’ll get to that soon enough.</p><br />
<p>Changed my test to look like this.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_message_is_only_processed_once()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var subscriber = <span class="kwrd">new</span> AmazonSqsSubscriber(_config);</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> var @<span class="kwrd">event</span> = <span class="kwrd">new</span> TestEvent {SomeGuid = Guid.NewGuid(), SomeInt = 1, SomeString = <span class="str">"Some String"</span>};</pre><pre class="alt"><span class="lnum"> 7: </span> _publisher.Publish(@<span class="kwrd">event</span>);</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> subscriber.Start();</pre><pre><span class="lnum"> 10: </span> <span class="rem">//Ugly, but I wanna make sure the message arrives</span></pre><pre class="alt"><span class="lnum"> 11: </span> Thread.Sleep(3000);</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> subscriber.Stop();</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> </pre></div><br />
<p> </p><br />
<p>here’s what I see.</p><br />
<p><a href="http://lh5.ggpht.com/_hl5c3g00lJk/TVxDbZkvQwI/AAAAAAAAAPc/-RT0xUXtvI4/s1600-h/publishedevent%5B5%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="publishedevent" border="0" alt="publishedevent" src="http://lh6.ggpht.com/_hl5c3g00lJk/TVxDbxumHII/AAAAAAAAAPg/9LZ60onDkZk/publishedevent_thumb%5B3%5D.png?imgmax=800" width="574" height="339"></a></p><br />
<br />
<br />
<br />
<br />
<p>Sweet! Now events published (line 7 in the above code) actually do make it to my subscriber. Next, we’re gunna actually deserialize that Message property to get the actual Domain Event instead of just doing a Console.Write(message.Body).</p><br />
<p>Ok, first things first, we’ve gotta be able to load the type. I added a cute little method to allow consumers to specify what assembly (or assemblies) the Events they’ll be publishing live in to my AmazonConfig class like so.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> AmazonConfig EventsAreInAssemblyContainingType<TEvent>()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">if</span> (EventAssemblies == <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 4: </span> EventAssemblies = <span class="kwrd">new</span> List<Assembly>();</pre><pre class="alt"><span class="lnum"> 5: </span> EventAssemblies.Add(<span class="kwrd">typeof</span>(TEvent).Assembly);</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
<p> </p><br />
<p>Just call that method in my test, and changed my AmazonSubscriber to look like this. Bear with the Ugly for now, I’ll be cleaning it, the point of this isn’t to show how clean I can make code, it’s to show that I can get it to work. Pull down the code from git, you’ll see that I clean my stuff up darnit! Anyway, here’s the (very ugly) code.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">private</span> <span class="kwrd">void</span> DoWork()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var response = _client.ReceiveMessage(<span class="kwrd">new</span> ReceiveMessageRequest { QueueUrl = _config.SqsQueueUrl });</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (!response.IsSetReceiveMessageResult())</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> var messageResult = response.ReceiveMessageResult;</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">foreach</span> (var message <span class="kwrd">in</span> messageResult.Message)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> var serializer = <span class="kwrd">new</span> JavaScriptSerializer();</pre><pre><span class="lnum"> 12: </span> var thing = (Dictionary<String,Object>)serializer.DeserializeObject(message.Body);</pre><pre class="alt"><span class="lnum"> 13: </span> Type eventType = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">foreach</span> (var eventAssembly <span class="kwrd">in</span> _config.EventAssemblies)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> var t = eventAssembly.GetType((<span class="kwrd">string</span>)thing[<span class="str">"Subject"</span>]);</pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">if</span>(t!=<span class="kwrd">null</span>)</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> eventType = t;</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">break</span>;</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">if</span> (eventType == <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> ConfigurationException(</pre><pre class="alt"><span class="lnum"> 25: </span> String.Format(</pre><pre><span class="lnum"> 26: </span> <span class="str">"Could not load type {0}, please make sure you call AmazonConfig.EventsAreInAssemblyContainingType<TEvent>() during bootstrap."</span>,</pre><pre class="alt"><span class="lnum"> 27: </span> thing[<span class="str">"Subject"</span>]));</pre><pre><span class="lnum"> 28: </span> </pre><pre class="alt"><span class="lnum"> 29: </span> var @<span class="kwrd">event</span> = serializer.Deserialize((<span class="kwrd">string</span>)thing[<span class="str">"Message"</span>], eventType);</pre><pre><span class="lnum"> 30: </span> Console.WriteLine(@<span class="kwrd">event</span>);</pre><pre class="alt"><span class="lnum"> 31: </span> Delete(message);</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre></div><br />
<p> </p><br />
<p>Still doing that Console.WriteLine, of course, but yeah, now when I run the test, I’m getting “AmazonTests.TestEvent” instead of that Json string. Now, lets just change that Console.WriteLine to instead locate all the event handlers for the event and execute them.</p><br />
<p>Once again, shut up about ugly, this is RED/GREEN/CLEAN land, clean is last, I wanna make it work before the code looks pretty. </p><br />
<p>I changed that line 30 from above to this.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> var @<span class="kwrd">event</span> = serializer.Deserialize((<span class="kwrd">string</span>)thing[<span class="str">"Message"</span>], eventType);</pre><pre><span class="lnum"> 2: </span> var handlerInterface = <span class="kwrd">typeof</span>(IDomainEventHandler<>).MakeGenericType(eventType);</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">foreach</span> (var handler <span class="kwrd">in</span> _config.EllemyConfiguration.ObjectBuilder.BuildAll(handlerInterface))</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> var handlerMethod = handler.GetType().GetMethod(<span class="str">"Handle"</span>);</pre><pre><span class="lnum"> 6: </span> handlerMethod.Invoke(handler, <span class="kwrd">new</span> [] { @<span class="kwrd">event</span> });</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> Delete(message);</pre></div><br />
<p> </p><br />
<p>I’ll then go write a quick hander write in the test assembly that’ll do the Console.WriteLine, just so I can still see all that pretty stuff. Like this.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> TestEvent : IDomainEvent</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> Guid SomeGuid { get; set; }</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> String SomeString { get; set; }</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">int</span> SomeInt { get; set; }</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> ConsoleWriter : IDomainEventHandler<TestEvent>{</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(TestEvent @<span class="kwrd">event</span>)</pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> Console.WriteLine(<span class="str">"SomeGuid: \t{0}"</span>,@<span class="kwrd">event</span>.SomeGuid);</pre><pre class="alt"><span class="lnum"> 11: </span> Console.WriteLine(<span class="str">"SomeInt: \t {0}"</span>,@<span class="kwrd">event</span>.SomeInt);</pre><pre><span class="lnum"> 12: </span> Console.WriteLine(<span class="str">"SomeString: \t{0}"</span>,@<span class="kwrd">event</span>.SomeString);</pre><pre class="alt"><span class="lnum"> 13: </span> }</pre><pre><span class="lnum"> 14: </span> }</pre></div><br />
<br />
<br />
<br />
<p> </p><br />
<p>To make it all work, I changed the setup method in my test to look like this.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [SetUp]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Arrange()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> tempQueueName = <span class="str">"Test_QUEUE"</span>;</pre><pre><span class="lnum"> 6: </span> _config = Configure.With()</pre><pre class="alt"><span class="lnum"> 7: </span> .StructureMapBuilder()</pre><pre><span class="lnum"> 8: </span> .HandlersAreInAssemblyContainingType<TestEvent>()</pre><pre class="alt"><span class="lnum"> 9: </span> .AmazonPublisher();</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> _config</pre><pre><span class="lnum"> 12: </span> .AwsAccessKeyId(awsKey)</pre><pre class="alt"><span class="lnum"> 13: </span> .AwsSecretKey(secret)</pre><pre><span class="lnum"> 14: </span> .TopicArn(topicArn)</pre><pre class="alt"><span class="lnum"> 15: </span> .QueueName(tempQueueName)</pre><pre><span class="lnum"> 16: </span> .EventsAreInAssemblyContainingType<TestEvent>();</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> _publisher = <span class="kwrd">new</span> AmazonPublisher(_config);</pre><pre class="alt"><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> _amazonClient = AWSClientFactory.CreateAmazonSQSClient(awsKey, secret);</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> }</pre></div><br />
<br />
<p>So, note that we’re (in line 6) telling it to use my StructureMapBuilder, and telling it where the DomainEventHandlers are (line 8).</p><br />
<p>Lets give the test a run.</p><br />
<p><a href="http://lh6.ggpht.com/_hl5c3g00lJk/TVxDcD9lsEI/AAAAAAAAAPk/nPrs7BElETY/s1600-h/finished%5B3%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="finished" border="0" alt="finished" src="http://lh3.ggpht.com/_hl5c3g00lJk/TVxDcuUEO_I/AAAAAAAAAPo/EcoJ6WthcjQ/finished_thumb%5B1%5D.png?imgmax=800" width="405" height="303"></a></p><br />
<p>NICE!</p><br />
<p>Lots of stuff going on there, and I’ve got a lot of cleaning to do, but it’s working! Freel free to pull down the code from <a href="https://github.com/elliottohara/Ellemy.CQRS">github</a>. If you’d like to contribute, I’m game too!</p><br />
<p>Have fun with it all, and I hope this is helpful.</p>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-74421089486584104552011-02-15T16:07:00.000-08:002011-02-15T16:07:44.667-08:00Playing around in the Cloud, Some exploratory testing before I can write a Amazon SQS subscriber<p>So, in my quest to send everything to the cloud, I wanna build a subscriber that uses <a href="http://aws.amazon.com/sqs/">Amazon SQS</a> to get messages. What is Amazon SQS you ask? Here’s what they say….</p><blockquote><p>Amazon Simple Queue Service (Amazon SQS) offers a reliable, highly scalable, hosted queue for storing messages as they travel between computers. By using Amazon SQS, developers can simply move data between distributed components of their applications that perform different tasks, without losing messages or requiring each component to be always available. Amazon SQS makes it easy to build an automated workflow, working in close conjunction with the Amazon Elastic Compute Cloud (Amazon EC2) and the other AWS infrastructure web services.<br />
Amazon SQS works by exposing Amazon’s web-scale messaging infrastructure as a web service. Any computer on the Internet can add or read messages without any installed software or special firewall configurations. Components of applications using Amazon SQS can run independently, and do not need to be on the same network, developed with the same technologies, or running at the same time.</p></blockquote><p>Yeah, it’s just a Queuing service. I’m gunna link my Amazon <a href="http://blog.elliottohara.com/2011/02/ellemycqrs-goes-to-cloud-amazon-sns.html">SNS Event publisher</a> up with SQS and be all cool Amazonish. Unfortunately, SQS doesn’t have a cute little console like SNS did, so I’m gunna spend a little time doing some exploratory testing before I go wire all this up to Ellemy.CQRS. I really want my Amazon stuff to be easy and friendly to use with Ellemy.CQRS. I figure that if I get all my hacking out of the way with this testing, I’ll have my head wrapped around everything by the time I’m ready to write my subscriber. <p>Ok, lets get to it. <p>First of all, we’ve gotta create a Queue. The <a href="http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSGettingStartedGuide/">Amazon Documentation</a> says it’s pretty simple. Let’s give that a spin, but I’ll use NUnit as my runner (instead of a Console app). <p>I wrote this. <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[TestFixture]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> check_message_tests</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> AmazonSQSClient _client;</pre><pre class="alt"><span class="lnum"> 5: </span> [SetUp]</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> create_client()</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _client = <span class="kwrd">new</span> AmazonSQSClient(<span class="str">"MyAccountId"</span>, <span class="str">"MySecret"</span>);</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> [Test]</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> create_queue()</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> var request = <span class="kwrd">new</span> CreateQueueRequest().WithQueueName(<span class="str">"Test_for_blog"</span>);</pre><pre><span class="lnum"> 14: </span> var response = _client.CreateQueue(request);</pre><pre class="alt"><span class="lnum"> 15: </span> Console.WriteLine(response.CreateQueueResult.QueueUrl);</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span>}</pre></div><br />
<br />
<p>Here’s the results….</p><br />
<p><a href="http://lh4.ggpht.com/_hl5c3g00lJk/TVrphrNTYAI/AAAAAAAAAO8/73jDDBz__8c/s1600-h/Capture%5B5%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Capture" border="0" alt="Capture" src="http://lh5.ggpht.com/_hl5c3g00lJk/TVrph6abceI/AAAAAAAAAPA/7mI7f9sBLGo/Capture_thumb%5B3%5D.png?imgmax=800" width="484" height="310"></a></p><br />
<p>Sweet… I guess that means I created a Queue!</p><br />
<p>Let’s go check from Amazon what Queues I have… That’s simple enough. Here’s what I did.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> list_queues()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var response = _client.ListQueues(<span class="kwrd">new</span> ListQueuesRequest());</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">foreach</span> (var result <span class="kwrd">in</span> response.ListQueuesResult.QueueUrl)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> Console.WriteLine(result);</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre></div><br />
<p> </p><br />
<p>I won’t do another screen capture… Just take my word for it, the test passed and I saw that Queue show up in the console. This is nice, because I can write a little utility that’ll check if the requested Queue exists for my subscriber, if not, we’ll use the code from that first test to create one. </p><br />
<p>Lets go send and receive a message it, just to get familiar with this stuff. I wrote this.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> send_and_receive_messages()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var url = <span class="str">"https://queue.amazonaws.com/xxxxxxxxx/Test_for_blog"</span>;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> Console.WriteLine(<span class="str">"Sending Message"</span>);</pre><pre class="alt"><span class="lnum"> 7: </span> var sendMessageRequest = <span class="kwrd">new</span> SendMessageRequest()</pre><pre><span class="lnum"> 8: </span> .WithQueueUrl(url)</pre><pre class="alt"><span class="lnum"> 9: </span> .WithMessageBody(<span class="str">"Hello from the cloud"</span>);</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> var sendResult = _client.SendMessage(sendMessageRequest);</pre><pre><span class="lnum"> 12: </span> Console.WriteLine(sendResult.ToXML());</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> Console.WriteLine(<span class="str">"Receiving Message"</span>);</pre><pre class="alt"><span class="lnum"> 15: </span> var request =</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">new</span> ReceiveMessageRequest().</pre><pre class="alt"><span class="lnum"> 17: </span> WithQueueUrl(url);</pre><pre><span class="lnum"> 18: </span> </pre><pre class="alt"><span class="lnum"> 19: </span> var result = _client.ReceiveMessage(request);</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">foreach</span> (var message <span class="kwrd">in</span> result.ReceiveMessageResult.Message)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> Console.WriteLine(message.Body);</pre><pre class="alt"><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> }</pre></div><br />
<br />
<br />
<p>Ok, so after screwing around for a while, I had some help from a buddy, on what I needed to do. Basically, if we’re gunna allow SNS to Send messages to SQS, we have to send a Queue Attribute up to SQS. I did it like this (this is a “trust me here” kinda thing).</p><br />
<p> </p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> set_permissions()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var setQueueAttributeRequest = <span class="kwrd">new</span> SetQueueAttributesRequest()</pre><pre class="alt"><span class="lnum"> 5: </span> .WithQueueUrl(_queueUrl)</pre><pre><span class="lnum"> 6: </span> .WithAttribute(<span class="kwrd">new</span> Attribute { Name = <span class="str">"Policy"</span>, Value = AllowSnsAttribute() });</pre><pre class="alt"><span class="lnum"> 7: </span> var result = _client.SetQueueAttributes(setQueueAttributeRequest);</pre><pre><span class="lnum"> 8: </span> Console.WriteLine(result.ToXML());</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> var getQueueAttributesResponse = _client.GetQueueAttributes(</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">new</span> GetQueueAttributesRequest().WithAttributeName(<span class="str">"Policy"</span>).WithQueueUrl(_queueUrl));</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">foreach</span> (var attribute <span class="kwrd">in</span> getQueueAttributesResponse.GetQueueAttributesResult.Attribute)</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> Console.WriteLine(<span class="str">"{0} : {1}"</span>,attribute.Name,attribute.Value);</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">private</span> <span class="kwrd">string</span> AllowSnsAttribute()</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">return</span> <span class="str">@"{"</span><span class="str">"Version"</span><span class="str">": "</span><span class="str">"2008-10-17"</span><span class="str">","</span><span class="str">"Statement"</span><span class="str">": [{"</span><span class="str">"Resource"</span><span class="str">": "</span><span class="str">"arn:aws:sqs:us-east-1:XXXXX:Test_for_blog"</span><span class="str">", "</span><span class="str">"Effect"</span><span class="str">": "</span><span class="str">"Allow"</span><span class="str">", "</span><span class="str">"Sid"</span><span class="str">": "</span><span class="str">"1"</span><span class="str">", "</span><span class="str">"Action"</span><span class="str">": "</span><span class="str">"sqs:*"</span><span class="str">", "</span><span class="str">"Condition"</span><span class="str">": {"</span><span class="str">"StringEquals"</span><span class="str">": {"</span><span class="str">"aws:SourceArn"</span><span class="str">": "</span><span class="str">"arn:aws:sns:us-east-1:XXXXX:EventMessage"</span><span class="str">"}}, "</span><span class="str">"Principal"</span><span class="str">": {"</span><span class="str">"AWS"</span><span class="str">": "</span><span class="str">"*"</span><span class="str">"}}]}"</span>;</pre><pre><span class="lnum"> 20: </span> }</pre></div><br />
<p>Yeah, so it’s pretty nifty actually, but for now, just trust me that we HAVE TO do that to make stuff work. I’ll save this code, cause I’ll be using it later when I start automagically wiring stuff up.</p><br />
<p>Next up I think I wanna publish from Amazon SNS and receive the resulting message. To do that, I’m gunna need to subscribe to a Topic ARN. Here’s what I did.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> subscribe_to_topic()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var getArnRequest = <span class="kwrd">new</span> GetQueueAttributesRequest().WithQueueUrl(_queueUrl).WithAttributeName(<span class="str">"QueueArn"</span>);</pre><pre class="alt"><span class="lnum"> 5: </span> var clientArn = _client.GetQueueAttributes(getArnRequest).GetQueueAttributesResult.Attribute[0].Value;</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> var sns = Amazon.AWSClientFactory.CreateAmazonSNSClient(<font color="#006080">awsKeyId</font>,</pre><pre><span class="lnum"> 8: </span> secret);</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> var subscriptionRequest = <span class="kwrd">new</span> SubscribeRequest()</pre><pre class="alt"><span class="lnum"> 11: </span> .WithEndpoint(clientArn)</pre><pre><span class="lnum"> 12: </span> .WithProtocol(<span class="str">"sqs"</span>)</pre><pre class="alt"><span class="lnum"> 13: </span> .WithTopicArn(<span class="str">"arn:aws:sns:us-east-1:xxxxx:EventMessage"</span>);</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> var response = sns.Subscribe(subscriptionRequest);</pre><pre><span class="lnum"> 16: </span> Console.WriteLine(response.SubscribeResult.SubscriptionArn);</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre></div><br />
<br />
<p>Works like a charm, cause when I go to my AWS console, I see the new subscription. Cool!</p><br />
<p>Next up, we’re gunna manually publish a message via the AWS console, and rip out the code from that send and receive test (just the receive part) and make sure that the message we published shows. First I fire up the AWS Console and hit the little publish to topic button. It looks like this….</p><br />
<p><a href="http://lh6.ggpht.com/_hl5c3g00lJk/TVrpiQhem7I/AAAAAAAAAPE/6KxbMksF1dA/s1600-h/publishtotopic%5B3%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="publishtotopic" border="0" alt="publishtotopic" src="http://lh6.ggpht.com/_hl5c3g00lJk/TVrpi4sUfNI/AAAAAAAAAPI/LqMgZNg9_zg/publishtotopic_thumb%5B1%5D.png?imgmax=800" width="390" height="302"></a></p><br />
<p>I’ll type in some mumbo jumbo and run a test that looks like this.</p><br />
<p> </p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> receive_message()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var request =</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">new</span> ReceiveMessageRequest().</pre><pre><span class="lnum"> 6: </span> WithQueueUrl(_queueUrl);</pre><pre class="alt"><span class="lnum"> 7: </span> var result = _client.ReceiveMessage(request);</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">foreach</span> (var message <span class="kwrd">in</span> result.ReceiveMessageResult.Message)</pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> Console.WriteLine(message.Body);</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> }</pre></div><br />
<br />
<p> </p><br />
<p>And the results?</p><br />
<p><a href="http://lh5.ggpht.com/_hl5c3g00lJk/TVrpjdx9nZI/AAAAAAAAAPM/xxzt_kBbI-0/s1600-h/successfulpublish%5B5%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="successfulpublish" border="0" alt="successfulpublish" src="http://lh5.ggpht.com/_hl5c3g00lJk/TVrpjzDnOCI/AAAAAAAAAPQ/_oLQnhlwRYg/successfulpublish_thumb%5B3%5D.png?imgmax=800" width="626" height="362"></a></p><br />
<p>Wow! Done… So, now I’ve proven that I can indeed get all this technology to work together… Next up, is doing it!</p><br />
<p>Stay tuned!</p>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com1tag:blogger.com,1999:blog-682843596462139829.post-7692126150066665092011-02-10T20:17:00.001-08:002011-02-10T20:24:36.647-08:00Ellemy.CQRS goes to the cloud, The Amazon SNS Event Publisher<p>So, long story short, I left my old job. Starting a new one with a very exciting company. The architect there apparently LOVES Amazon services. So, I figured I’d go write a quick Event publisher for Ellemy.CQRS using <a href="http://aws.amazon.com/sns/">Amazon Simple Notification Service</a>. Won’t hurt to be familiar with it, and sounds kinda cool.</p> <p>First of all, I signed up for the service, it’s essentially free. Yes, they want a CC card, but they charge nothing as long as you don’t exceed a gig a month, and it’s $0.19 per gig after that. I’m not gunna even approach a gig, so I’m fine.</p> <p>First of all, I went and configured a Topic, If you’re an <a href="http://www.nservicebus.com/">NServiceBus</a> guy (like me), a Topic is essentially a Queue – someone feel free to correct me there, but that’s how I think of it.</p> <p>You can make API calls all day to administer topics, but that’s not the point of this blog, the point of this blog is to send a message out to the cloud. So I’m gunna get a topic created as quick as possible. Amazon made a nice little web interface to do that.</p> <p>I logged in with my Amazon Account and added a topic, really simple. </p> <p><a href="http://lh4.ggpht.com/_hl5c3g00lJk/TVSsPOW7CBI/AAAAAAAAAOc/S4izt0Ya4o8/s1600-h/createtopic%5B3%5D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="createtopic" border="0" alt="createtopic" src="http://lh3.ggpht.com/_hl5c3g00lJk/TVSsPXCNpDI/AAAAAAAAAOg/LfzSHD0qJog/createtopic_thumb%5B1%5D.png?imgmax=800" width="482" height="319"></a></p> <p>I named my topic, and got redirected to a nice little screen that gave me the Topic ARN. We’ll need that along with the Key Id, and Secret. You can add subscribers from the next page, and one of the options is Email (JSON). That’s pretty cool. I just select that. Easy way to check if the message is publishing ok.</p> <p>Let’s get in some code now….. We need to implement a new version of IEventPublisher. Here’s what it looks like.</p> <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Event</pre><pre><span class="lnum"> 2: </span>{</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IEventPublisher</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">void</span> Publish<TDomainEvent>(TDomainEvent @<span class="kwrd">event</span>) <span class="kwrd">where</span> TDomainEvent : IDomainEvent;</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span>}</pre></div><br /><p>We’re just gunna send the event off to the cloud there.</p><br /><p>I can hand roll GET/POST/PUT all day, but I really don’t feel like doing all the plumbing so I downloaded the <a href="http://aws.amazon.com/sdkfornet/">Amazon AWS .NET SDK</a>. It’s got a nice wrapper around all that stuff. Spun up the object browser and looked around and saw this puppy.</p><br /><p><a href="http://lh3.ggpht.com/_hl5c3g00lJk/TVSsPqbwa5I/AAAAAAAAAOk/SnPrGoysSDk/s1600-h/objectbrowser%5B3%5D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="objectbrowser" border="0" alt="objectbrowser" src="http://lh5.ggpht.com/_hl5c3g00lJk/TVSsPzp9gpI/AAAAAAAAAOo/v342U9nBDn4/objectbrowser_thumb%5B1%5D.png?imgmax=800" width="528" height="382"></a></p><br /><p>I think that’s exactly what we need. Let’s just do the simpliest thing first and new one of those up in the publisher. I’ll clean up in a second.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> AmazonPublisher : IEventPublisher</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> AmazonPublisher()</pre><pre><span class="lnum"> 4: </span> {}</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Publish<TDomainEvent>(TDomainEvent @<span class="kwrd">event</span>) <span class="kwrd">where</span> TDomainEvent : IDomainEvent</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> var serializer = <span class="kwrd">new</span> JavaScriptSerializer();</pre><pre class="alt"><span class="lnum"> 9: </span> var payload = serializer.Serialize(@<span class="kwrd">event</span>);</pre><pre><span class="lnum"> 10: </span> var client = <span class="kwrd">new</span> AmazonSimpleNotificationServiceClient(<span class="str">"myAccessId"</span>,</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="str">"mysecretKey"</span>);</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> var request = <span class="kwrd">new</span> PublishRequest</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> Message = payload,</pre><pre><span class="lnum"> 16: </span> Subject = @<span class="kwrd">event</span>.GetType().AssemblyQualifiedName,</pre><pre class="alt"><span class="lnum"> 17: </span> TopicArn = <span class="str">"arn:aws:sns:us-east-1:451419498740:EventMessage"</span></pre><pre><span class="lnum"> 18: </span> };</pre><pre class="alt"><span class="lnum"> 19: </span> client.Publish(request);</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> </pre><pre class="alt"><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> </pre><pre class="alt"><span class="lnum"> 25: </span> </pre><pre><span class="lnum"> 26: </span> }</pre></div><br /><p>Lets go tell Ellemy.CQRS to use it. We’ll make a pretty Extension method in a bit, for now, lets do it ugly.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>Configure.With()</pre><pre><span class="lnum"> 2: </span> .StructureMapBuilder(ObjectFactory.Container)</pre><pre class="alt"><span class="lnum"> 3: </span> .CommandExecutorsAreInAssemblyContainingType<CreateMessage>()</pre><pre><span class="lnum"> 4: </span> .HandlersAreInAssemblyContainingType<MessageReadModel>()</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="rem">//.NServiceBusPublisher(ObjectFactory.Container.GetInstance<IBus>());</span></pre><pre><span class="lnum"> 6: </span> .PublishEventsWith(<span class="kwrd">new</span> AmazonPublisher());</pre></div><br /><p>Let’s go fire it ok, here goes nuthin!</p><br /><p>BAM!</p><br /><p><a href="http://lh5.ggpht.com/_hl5c3g00lJk/TVSsQFKM9aI/AAAAAAAAAOs/bTFbXV3bK_o/s1600-h/message%5B4%5D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="message" border="0" alt="message" src="http://lh5.ggpht.com/_hl5c3g00lJk/TVSsQyZBjrI/AAAAAAAAAOw/3vXQEPGve4M/message_thumb%5B2%5D.png?imgmax=800" width="497" height="221"></a></p><br /><p>How freekin awesome is that? </p><br /><p>A little cleanup now…. First lets make a extensions class to make it so people don’t have to new up that AmazonPublisher object, and let them send in the config stuff. I’ve kinda blogged on how I like doing this before, but what the heck? I’ll do it again…</p><br /><p>First of all, I’ll create a little fluent config to allow users to configure their amazon KeyId, Secret and TopicArn. In a future release I’m gunna create a Factory to resolve TopicArn by domain event type, but for now, let’s stick to a single TopicArn.</p><br /><p>Here’s what I cooked up.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> AmazonPublisherConfig</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Configuration _configuration;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> AmazonPublisherConfig(Configuration configuration)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _configuration = configuration;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">internal</span> <span class="kwrd">string</span> AccessKeyId { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">internal</span> <span class="kwrd">string</span> SecretKey { get; <span class="kwrd">private</span> set; }</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">internal</span> <span class="kwrd">string</span> TopicAccessResourceName { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">public</span> AmazonPublisherConfig AwsAccessKeyId(<span class="kwrd">string</span> accessKeyId)</pre><pre><span class="lnum"> 16: </span> {</pre><pre class="alt"><span class="lnum"> 17: </span> AccessKeyId = accessKeyId;</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">public</span> AmazonPublisherConfig AwsSecretKey(<span class="kwrd">string</span> awsSecretKey)</pre><pre><span class="lnum"> 22: </span> {</pre><pre class="alt"><span class="lnum"> 23: </span> SecretKey = awsSecretKey;</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre class="alt"><span class="lnum"> 25: </span> }</pre><pre><span class="lnum"> 26: </span> </pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">public</span> AmazonPublisherConfig TopicArn(<span class="kwrd">string</span> <span class="kwrd">value</span>)</pre><pre><span class="lnum"> 28: </span> {</pre><pre class="alt"><span class="lnum"> 29: </span> TopicAccessResourceName = <span class="kwrd">value</span>;</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span> <span class="kwrd">public</span> Configuration CreatePublisher()</pre><pre class="alt"><span class="lnum"> 33: </span> {</pre><pre><span class="lnum"> 34: </span> _configuration.PublishEventsWith(<span class="kwrd">new</span> AmazonPublisher(<span class="kwrd">this</span>));</pre><pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">return</span> _configuration;</pre><pre><span class="lnum"> 36: </span> }</pre><pre class="alt"><span class="lnum"> 37: </span> }</pre></div><br /><p>At some point I’ll let config files do this, but for now, lets keep it simple. Basically, we just build up the config values fluently, and call CreatePublisher and that’ll set the IDomainEventPublisher to the AmazonPublisher. Now lets just clean up the AmazonPublisher. </p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> AmazonPublisher : IEventPublisher</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> AmazonSimpleNotificationServiceClient _client;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> _topicArn;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">internal</span> AmazonPublisher(AmazonPublisherConfig config)</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _client = <span class="kwrd">new</span> AmazonSimpleNotificationServiceClient(config.AccessKeyId, config.SecretKey);</pre><pre class="alt"><span class="lnum"> 9: </span> _topicArn = config.TopicAccessResourceName;</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Publish<TDomainEvent>(TDomainEvent @<span class="kwrd">event</span>) <span class="kwrd">where</span> TDomainEvent : IDomainEvent</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> var serializer = <span class="kwrd">new</span> JavaScriptSerializer();</pre><pre><span class="lnum"> 14: </span> var payload = serializer.Serialize(@<span class="kwrd">event</span>);</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> var request = <span class="kwrd">new</span> PublishRequest</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> Message = payload,</pre><pre class="alt"><span class="lnum"> 19: </span> Subject = @<span class="kwrd">event</span>.GetType().Name,</pre><pre><span class="lnum"> 20: </span> TopicArn = _topicArn</pre><pre class="alt"><span class="lnum"> 21: </span> };</pre><pre><span class="lnum"> 22: </span> _client.Publish(request);</pre><pre class="alt"><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> }</pre></div><br /><p>Nice and simple, huh? I’m setting the Subject to the event type name right now, and we’ll create a subscriber soon that’ll use that to go find the IDomainEventHandlers for type with the name that matches the subject. Not sure if that’ll work or not, but we’ll see soon enough huh? I’m just JSON serializing the Event. I think that’ll work great.</p><br /><p>Lets fire her up again, and see. Hit the UI, fire off a message, and here’s what I get (well, after I took an eraser to the signature so you guys don’t see how many terabytes of messages you can send up to the cloud and charge up my credit card). </p><br /><p><a href="http://lh3.ggpht.com/_hl5c3g00lJk/TVS4XqoIYYI/AAAAAAAAAO0/id4-Mue6aVA/s1600-h/hellofromthecloud%5B10%5D.jpg"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="hellofromthecloud" border="0" alt="hellofromthecloud" src="http://lh4.ggpht.com/_hl5c3g00lJk/TVS4YIavJMI/AAAAAAAAAO4/do8rJuPCSo0/hellofromthecloud_thumb%5B6%5D.jpg?imgmax=800" width="643" height="294"></a></p><br /><p>I’m having a blast working on this stuff, and I see that I’m getting a few watches on <a href="https://github.com/elliottohara/Ellemy.CQRS">github</a>, Folks, please leave comments with what you’d like to see added to it, or any other blog requests. </p><br /><p>Have fun with it!</p> Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com2tag:blogger.com,1999:blog-682843596462139829.post-49320747628879839432011-02-09T06:50:00.000-08:002011-02-11T10:03:11.747-08:00I’m bored, so I code. Stupid extension methods that might be useful. (Probably not)<p>Chad Myers keeps tweeting things about dates, and it reminded me of something I read about some cool extension methods to make using dates easier. I can’t find the thing I read (mostly because I didn’t look), but I figured I’d go blog it really quick.</p><p>I wanna add some extension methods for int to make getting Dates easier. Something like this</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> public void days()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var timespan = 1.Days();</pre><pre class="alt"><span class="lnum"> 5: </span> Assert.AreEqual(1,timespan.Days);</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
<p>Oh, cool…. Easy to make it work too. Just like this.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>public static TimeSpan Days(this int days)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> return new TimeSpan(days,0,0,0,0);</pre><pre><span class="lnum"> 4: </span> }</pre></div><br />
<br />
<p>Seriously though if I need to get a timespan for one day, really… Look at line 3, that’s some ugly stuff, aint it? I like the extension method.</p><br />
<p>Lets make it a little more valuable.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> public void ago()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var expected = DateTime.Today.AddDays(-2);</pre><pre class="alt"><span class="lnum"> 5: </span> var actual = 2.Days().Ago();</pre><pre><span class="lnum"> 6: </span> Assert.AreEqual(expected,actual);</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
<p>Simple enough to make it work, right? </p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>public static DateTime Ago(this TimeSpan timeSpan)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> return DateTime.Now.Subtract(timeSpan);</pre><pre><span class="lnum"> 4: </span> }</pre></div><br />
<br />
<p>Ok, so now lets get really cute.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> public void honest_abe()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var expected = DateTime.Now.AddYears(-87);</pre><pre class="alt"><span class="lnum"> 5: </span> var actual = 4.Score().And(7).Years().Ago();</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> Assert.AreEqual(expected.Year,actual.Year);</pre><pre><span class="lnum"> 8: </span> }</pre></div><br />
<br />
<p>Wow, I’m stupid, huh? Hehe…</p><br />
<p>Here’s what I did.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> public static class DateExtensions</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> public static TimeSpan Days(this int days)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> return new TimeSpan(days,0,0,0,0);</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> public static TimeSpan Hours(this int hours)</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> return new TimeSpan(hours,0,0);</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> public static TimeSpan Minutes(this int minutes)</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> return new TimeSpan(0,minutes,0);</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> public static TimeSpan Years(this int years)</pre><pre><span class="lnum"> 16: </span> {</pre><pre class="alt"><span class="lnum"> 17: </span> var leapYears = years/4;</pre><pre><span class="lnum"> 18: </span> return new TimeSpan(365*years + leapYears, 0, 0, 0);</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> public static DateTime Ago(this TimeSpan timeSpan)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> return DateTime.Now.Subtract(timeSpan);</pre><pre class="alt"><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> public static int Score(this int val)</pre><pre class="alt"><span class="lnum"> 25: </span> {</pre><pre><span class="lnum"> 26: </span> return val*20;</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre><pre><span class="lnum"> 28: </span> public static int And(this int left,int right)</pre><pre class="alt"><span class="lnum"> 29: </span> {</pre><pre><span class="lnum"> 30: </span> return left + right;</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span> }</pre></div><br />
<p>Wow, what a stupid blog huh? I think my next blog post is gunna be cool though… think Amazon, think cloud, think Ellemy.CQRS and publishing… </p>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com1tag:blogger.com,1999:blog-682843596462139829.post-91986304709835192592011-02-08T06:00:00.000-08:002011-02-08T06:00:12.376-08:00Subscribing, you know, because you care. Ellemy.CQRS and NServiceBus subscriptions<p>See, I keep my promises!<br><br>Ok in my <a href="http://blog.elliottohara.com/2011/02/publishing-events-cause-sometimes.html">last blog</a> I was able to write an <a href="https://github.com/elliottohara/Ellemy.CQRS/blob/master/Extensions/NServiceBusPublisher/NServiceBusPublisher.cs">NServiceBusPublisher</a> that pushes out message using <a href="http://www.nservicebus.com/">NServiceBus</a>. I quit the writing after I showed the MSMQ admin screen and that the message actually did make it to the queue. </p> <p>As a side note, I once pushed a product all the way to production spelling queue, que. It really pissed off our DBA since we where using NHibernate Automapping and the table was mispelled, he really got his panties in a wad over it. It was pretty dramatic.Wow, how did I get on that?</p> <p>Where were we? Oh yeah, so, I wanna write a quick little NServiceBusHost.exe app that’ll just print out the message to the console. I really don’t care what happens with it, just wanna see that we can subscribe and react to the message. Spin up my App.Config MsmqTransport config to grab from the queue the client in sending messages to.</p> <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd"><</span><span class="html">MsmqTransportConfig</span></pre><pre><span class="lnum"> 2: </span> <span class="attr">InputQueue</span><span class="kwrd">="MyServerInputQueue"</span></pre><pre class="alt"><span class="lnum"> 3: </span> <span class="attr">ErrorQueue</span><span class="kwrd">="error"</span></pre><pre><span class="lnum"> 4: </span> <span class="attr">NumberOfWorkerThreads</span><span class="kwrd">="1"</span></pre><pre class="alt"><span class="lnum"> 5: </span> <span class="attr">MaxRetries</span><span class="kwrd">="5"</span></pre><pre><span class="lnum"> 6: </span> <span class="kwrd">/></span></pre></div><br /><p>I’m a <a href="http://structuremap.net/structuremap/">StructureMap</a> guy, so I wire up my container in my Endpoint config like so.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> EndpointConfig : IConfigureThisEndpoint, AsA_Server,IWantCustomInitialization</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Init()</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> ObjectFactory.Initialize(r => r.Scan(scan =></pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> scan.AssemblyContainingType<EndpointConfig>();</pre><pre><span class="lnum"> 8: </span> scan.AddAllTypesOf(<span class="kwrd">typeof</span> (IDomainEventHandler<>));</pre><pre class="alt"><span class="lnum"> 9: </span> }));</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> Configure.With().StructureMapBuilder().BinarySerializer().UnicastBus().LoadMessageHandlers();</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><br /><div class="csharpcode"> </div><br /><div class="csharpcode"><font face="Arial">This is kinda outside the scope of this post, but the AddAllTypesOf is something that trips up everyone, they use ConnectImplementationsToTypeClosing. That’ll only give you one Handler, that’s what you DO NOT want for handling events – that’s fine for commandhandlers, but events – no, we might have multiple so yeah, just use AddAllTypesOf. </font></div><br /><div class="csharpcode"><font face="Arial"></font> </div><br /><div class="csharpcode"><font face="Arial">Anyway, yeah, I wire up SM to locate all Domain Event handers and tell NServiceBus to use the StructureMapBuilder, and to Load the message handlers.</font></div><br /><div class="csharpcode"><font face="Arial"></font> </div><br /><div class="csharpcode"><font face="Arial">Note that in my last blog, I made a message that wraps up domain events, and marked that with IMessage, so while we may have multiple handlers for domain events, we only have one messages that will get sent to the bus. Just a refresher – here it is.</font></div><br /><div class="csharpcode"> </div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Serializable]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> EventMessage<T> : IMessage</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">where</span> T : IDomainEvent</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> EventMessage(T @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> Payload = @<span class="kwrd">event</span>;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> EventMessage(){}</pre><pre><span class="lnum"> 10: </span> <span class="rem">/// <summary></span></pre><pre class="alt"><span class="lnum"> 11: </span> <span class="rem">/// Gets or sets transported event.</span></pre><pre><span class="lnum"> 12: </span> <span class="rem">/// </summary></span></pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> T Payload { get; set; }</pre><pre><span class="lnum"> 14: </span> }</pre><pre> </pre><pre><font face="Arial">So the idea is to have a Handler for EventMessage<IDomainEvent> and have it locate all IDomainEventHandlers for the event in the message. Not horribly complicated actually. But since the compilers not smart enough to figure out the type, we’ve gotta do a little reflection magic…Let’s take a look.</font></pre><pre> </pre><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MessageHandler: IHandleMessages<EventMessage<IDomainEvent>></pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IContainer _container;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> MessageHandler(IContainer container)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _container = container;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(EventMessage<IDomainEvent> message)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> var handlerInterface = <span class="kwrd">typeof</span>(IDomainEventHandler<>).MakeGenericType(message.Payload.GetType());</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">foreach</span> (var handler <span class="kwrd">in</span> _container.BuildAll(handlerInterface))</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> var handlerMethod = handler.GetType().GetMethod(<span class="str">"Handle"</span>);</pre><pre class="alt"><span class="lnum"> 15: </span> handlerMethod.Invoke(handler, <span class="kwrd">new</span> <span class="kwrd">object</span>[] { message.Payload });</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> }</pre></div></div><br /><p>So, we build the proper type by interrogating the message payload for type, and build the appropriate generic. Once we’ve got that we use the container to get all the handlers. Oh, a little side note, that IContainer is the NServiceBus IOC container, NOT a StructureMap container. That way people can use the IOC container of their choice. <br><br>Once I’ve got all the handlers, we just use reflection to invoke the Handle method on each of them. Simple enough right?</p><br /><p>Next up, I’ll go build a quick little handler for my MessageCreated event (that’s the one that is raised from the website)that dumps stuff out in the console window. It looks like this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> PrintStuffToConsole : IDomainEventHandler<MessageCreated></pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(MessageCreated @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> Console.WriteLine(<span class="str">"Recieved Message Created"</span>);</pre><pre><span class="lnum"> 6: </span> Console.WriteLine(<span class="str">"Text:{0}"</span>,@<span class="kwrd">event</span>.Text);</pre><pre class="alt"><span class="lnum"> 7: </span> Console.WriteLine(<span class="str">"Id:{0}"</span>,@<span class="kwrd">event</span>.MessageId);</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre></div><br /><p>There we have it. Now lets just go run NServiceBusHost.exe and see what we get.</p><br /><p><(null)> - MessageHandler Failed handling message.<br>StructureMap.StructureMapException: StructureMap Exception Code: 202<br>No Default Instance defined for PluginFamily NServiceBus.ObjectBuilder.Common.IC<br>ontainer, NServiceBus.Core, Version=2.5.0.1446, Culture=neutral, PublicKeyToken=<br>9fc386479f8a226c</p><br /><p>OOPS! That’s not what I wanted. This surprised me, I kinda figured that StructureMapObjectBuilder would tell NServiceBuses IOC container to use itself when asked for an IContainer, guess I was wrong, I’ll bug Andreas about that, see if we can get that fixed. Until then, lets just go tell StructureMap that when we ask for an NSB IContainer, use the StructureMapObjectBuilder.</p><br /><p>That’s pretty easy… We just change or Endpoint config a tiny bit so we end up with this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> Ellemy.CQRS.Event;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> NServiceBus;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> NServiceBus.ObjectBuilder.StructureMap262;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> StructureMap;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> IContainer = NServiceBus.ObjectBuilder.Common.IContainer;</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">namespace</span> Ellemy.CQRS.NServiceBusSubscriber</pre><pre><span class="lnum"> 8: </span>{</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Init()</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> ObjectFactory.Initialize(r =></pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> r.Scan(scan =></pre><pre><span class="lnum"> 16: </span> {</pre><pre class="alt"><span class="lnum"> 17: </span> scan.AssemblyContainingType<EndpointConfig>();</pre><pre><span class="lnum"> 18: </span> scan.AddAllTypesOf(<span class="kwrd">typeof</span> (IDomainEventHandler<>));</pre><pre class="alt"><span class="lnum"> 19: </span> });</pre><pre><span class="lnum"> 20: </span> r.For<IContainer>().Use</pre><pre class="alt"><span class="lnum"> 21: </span> <StructureMapObjectBuilder>();</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span> );</pre><pre><span class="lnum"> 24: </span> </pre><pre class="alt"><span class="lnum"> 25: </span> </pre><pre><span class="lnum"> 26: </span> Configure.With().StructureMapBuilder().BinarySerializer().UnicastBus().LoadMessageHandlers();</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span>}</pre></div><br /><p>Now, give ‘er a spin and here’s what we get.</p><br /><p><a href="http://lh5.ggpht.com/_hl5c3g00lJk/TU3e8WNihMI/AAAAAAAAAOE/YXHhk2HLAJ8/s1600-h/NserviceBusHostWindow%5B3%5D.jpg"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="NserviceBusHostWindow" border="0" alt="NserviceBusHostWindow" src="http://lh5.ggpht.com/_hl5c3g00lJk/TU3e8y9kDOI/AAAAAAAAAOI/pwYHIDdmWe4/NserviceBusHostWindow_thumb%5B1%5D.jpg?imgmax=800" width="479" height="291"></a></p><br /><p>It’s a happy day in Ellemy.CQRS land! </p><br /><p>Hope this helped, and have fun. Let me know if you’ve got any questions!</p><br /><p>Elliott</p> Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-8741077696341116072011-02-07T05:22:00.000-08:002011-02-07T05:22:29.898-08:00No, no, no, don’t put read concerns there!<p>So Scott brought up a good point in my <a href="http://blog.elliottohara.com/2011/02/its-not-about-data-its-about-what-we-do.html">"it's not about the data" post</a>. He pointed out that the rules for what we display are mixed up in the domain. </p> <p>He’s right, take a look at this code.</p> <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Visit(DateTime? when = <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> when = when ?? DateTime.Now;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (_lastVisit.Date != DateTime.Today)</pre><pre class="alt"><span class="lnum"> 5: </span> DomainEvents.Raise(<span class="kwrd">new</span> VisitorForFirstTimeInDay(WebIdentifier,when.Value));</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> _lastVisit = when.Value;</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> }</pre></div><br /><br /><p>If you think about it, the ONLY reason we’re raising that event is because someone said, “Hey, I wanna count the number of visitors per day”. What if they said, “Hey, I wanna count the number of visitors per hour” or minute, or month? That code would get really ugly, really quick. The reason is that it’s making a decision that is ONLY a read model concern. There is NO point in doing that. It’s just as bad, and arguably worse, than just putting the read concerns as properties on the domain model. It defeats the whole purpose of CQRS.</p><br /><p>Let’s fix it.</p><br /><p>Really, what the customer cares about is how many visitors he gets. He wants (right now) one report based on some times stuff. </p><br /><p>Instead, lets just raise and event that lets other things know that the site was visited, and we’ll make Reporters understand the report they need to write.</p><br /><p>New Unit test.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> a_visit_raises_the_SiteVistedEvent()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="rem">//Arrange</span></pre><pre class="alt"><span class="lnum"> 5: </span> var when = DateTime.Now.AddDays(-1);</pre><pre><span class="lnum"> 6: </span> var sessionId = String.Empty;</pre><pre class="alt"><span class="lnum"> 7: </span> var eventTime = DateTime.MinValue;</pre><pre><span class="lnum"> 8: </span> DomainEvents.Register<SiteVisted>(@<span class="kwrd">event</span>=></pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> sessionId = @<span class="kwrd">event</span>.SessionId;</pre><pre class="alt"><span class="lnum"> 11: </span> eventTime = @<span class="kwrd">event</span>.When;</pre><pre><span class="lnum"> 12: </span> });</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> <span class="rem">//act</span></pre><pre class="alt"><span class="lnum"> 15: </span> _visitor.Visit(when);</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> <span class="rem">//assert</span></pre><pre><span class="lnum"> 18: </span> Assert.AreEqual(sessionId,_visitor.WebIdentifier);</pre><pre class="alt"><span class="lnum"> 19: </span> Assert.AreEqual(when,eventTime);</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> }</pre></div><br /><p>Ok, so now we end up ripping out some code from the Visit method, and instead just raise the appropriate event.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">void</span> Visit(DateTime? when = <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var date = when ?? DateTime.Now;</pre><pre><span class="lnum"> 4: </span> DomainEvents.Raise(<span class="kwrd">new</span> SiteVisted(WebIdentifier, date));</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> _lastVisit = date;</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> }</pre></div><br /><p>And…<br></p><br /><p><a href="http://lh5.ggpht.com/_hl5c3g00lJk/TU9u-hxHKiI/AAAAAAAAAOM/YvMByVqVR_U/s1600-h/capture%5B2%5D.jpg"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="capture" border="0" alt="capture" src="http://lh5.ggpht.com/_hl5c3g00lJk/TU9u-38mkdI/AAAAAAAAAOQ/-MP4OpGfpVM/capture_thumb.jpg?imgmax=800" width="244" height="142"></a></p><br /><p>Ok… Now lets go fix that reporter to handle the new event.</p><br /><p>Test…<br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_reportwriter_records_unique_visitor_for_his_first_visit()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var sessionId = <span class="str">"THESESSION"</span>;</pre><pre class="alt"><span class="lnum"> 5: </span> var reallyEarly = DateTime.Today.AddHours(1);</pre><pre><span class="lnum"> 6: </span> var theEvent = <span class="kwrd">new</span> SiteVisted(sessionId, reallyEarly);</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> _reporter.Handle(theEvent);</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> var visitorsToday = _visitorsByDateRepository.GetReportFor(DateTime.Today).UniqueVisitors;</pre><pre class="alt"><span class="lnum"> 11: </span> Assert.AreEqual(1,visitorsToday);</pre><pre><span class="lnum"> 12: </span> }</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> [SetUp]</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Arrange()</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> _visitor = <span class="kwrd">new</span> Visitor(<span class="str">"SomeSessionId"</span>);</pre><pre class="alt"><span class="lnum"> 19: </span> _visitorsByDateRepository = <span class="kwrd">new</span> VisitorsPerDayRepository();</pre><pre><span class="lnum"> 20: </span> _siteVisitRepository = <span class="kwrd">new</span> SiteVisitRepository();</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> _reporter = <span class="kwrd">new</span> VisitorsPerDayReportWriter(_siteVisitRepository,_visitorsByDateRepository);</pre><pre class="alt"><span class="lnum"> 23: </span> }</pre></div></p><br /><div class="csharpcode"></div><br /><p>Here’s how I made it pass.<br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> VisitorsPerDayReportWriter : IDomainEventHandler<SiteVisted></pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> SiteVisitRepository _repository;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> VisitorsPerDayRepository _reportRepository;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> VisitorsPerDayReportWriter(SiteVisitRepository repository, VisitorsPerDayRepository reportRepository)</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _repository = repository;</pre><pre class="alt"><span class="lnum"> 9: </span> _reportRepository = reportRepository;</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(SiteVisted @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">if</span> (_repository</pre><pre><span class="lnum"> 16: </span> .Get()</pre><pre class="alt"><span class="lnum"> 17: </span> .Any(s => s.When.Date == @<span class="kwrd">event</span>.When.Date && s.SessionId == @<span class="kwrd">event</span>.SessionId)) <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 18: </span> </pre><pre class="alt"><span class="lnum"> 19: </span> _reportRepository.GetReportFor(@<span class="kwrd">event</span>.When).UniqueVisitors++;</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> }</pre></div></p><br /><div class="csharpcode"></div><br /><p>I won’t bother showing you the two repos, just know that GetReportFor will always return an instance of whatever that entity is (it’ll just create one if it’s not there yet).</p><br /><p>Now, I’m pretty sure this isn’t done, and lets just prove that with another test.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_reportwriter_does_not_double_count()</pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 3: </span> {</pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 4: </span> var sessionId = <span class="str">"THESESSION"</span>;</pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 5: </span> var reallyEarly = DateTime.Today.AddHours(1);</pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 6: </span> var theEvent = <span class="kwrd">new</span> SiteVisted(sessionId, reallyEarly);</pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 7: </span> var atNoon = reallyEarly.AddHours(11);</pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 8: </span> </pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 9: </span> _reporter.Handle(<span class="kwrd">new</span> SiteVisted(sessionId,reallyEarly));</pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 10: </span> _reporter.Handle(<span class="kwrd">new</span> SiteVisted(sessionId,atNoon));</pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 11: </span> </pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 12: </span> var visitorsToday = _visitorsByDateRepository.GetReportFor(DateTime.Today).UniqueVisitors;</pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 13: </span> Assert.AreEqual(1, visitorsToday);</pre></div><br /><div class="csharpcode"><pre><span class="lnum"> 14: </span> }</pre></div><br /><div class="csharpcode"><pre> </pre></div><br /><div class="csharpcode"><pre>And NOPE… just like I thought “Expected 1, Actual 2”. Let’s go fix that really quick.</pre></div><br /><div class="csharpcode"><pre> </pre></div><br /><div class="csharpcode"><pre>What we’re gunna do is add another handler to record the site visit. It looks like this.</pre></div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> VisitRecorderService : IDomainEventHandler<SiteVisted></pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> SiteVisitRepository _siteVisitRepository;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> VisitRecorderService(SiteVisitRepository siteVisitRepository)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _siteVisitRepository = siteVisitRepository;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(SiteVisted @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> _siteVisitRepository.Add(<span class="kwrd">new</span> SiteVisit(@<span class="kwrd">event</span>.SessionId,@<span class="kwrd">event</span>.When));</pre><pre class="alt"><span class="lnum"> 13: </span> }</pre><pre><span class="lnum"> 14: </span> }</pre></div><br /><p><font color="#000000" size="2" face="Consolas">Next, since we’re not sure what order events are handled, I added a convenience method to my SiteVisit repo that’ll exclude the SiteVisit that would be generated for a SiteVisit event. It looks like this.</font></p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> IQueryable<SiteVisit> GetWithoutIncluding(SiteVisted @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> _siteVisits.Where(sv => sv.SessionId != @<span class="kwrd">event</span>.SessionId || sv.When != @<span class="kwrd">event</span>.When).AsQueryable();</pre><pre><span class="lnum"> 4: </span> }</pre></div><br /><p>Now, I need to go fix the test to execute the the VisitRecorderService, here’s what it looks like when done.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_reportwriter_does_not_double_count()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var sessionId = <span class="str">"THESESSION"</span>;</pre><pre class="alt"><span class="lnum"> 5: </span> var reallyEarly = DateTime.Today.AddHours(1);</pre><pre><span class="lnum"> 6: </span> var theEvent = <span class="kwrd">new</span> SiteVisted(sessionId, reallyEarly);</pre><pre class="alt"><span class="lnum"> 7: </span> var atNoon = reallyEarly.AddHours(11);</pre><pre><span class="lnum"> 8: </span> <span class="rem">// An IOC container will locate all handlers, but we know that they'll all </span></pre><pre class="alt"><span class="lnum"> 9: </span> <span class="rem">// get located so lets manually execute them</span></pre><pre><span class="lnum"> 10: </span> var event1 = <span class="kwrd">new</span> SiteVisted(sessionId, reallyEarly);</pre><pre class="alt"><span class="lnum"> 11: </span> _reporter.Handle(event1);</pre><pre><span class="lnum"> 12: </span> _visitRecorder.Handle(event1);</pre><pre class="alt"><span class="lnum"> 13: </span> var event2 = <span class="kwrd">new</span> SiteVisted(sessionId, atNoon);</pre><pre><span class="lnum"> 14: </span> _reporter.Handle(event2);</pre><pre class="alt"><span class="lnum"> 15: </span> _visitRecorder.Handle(event2);</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> var visitorsToday = _visitorsByDateRepository.GetReportFor(DateTime.Today).UniqueVisitors;</pre><pre><span class="lnum"> 18: </span> Assert.AreEqual(1, visitorsToday);</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre></div><br /><p>Note that I don’t like writing unit tests like this, I’d typically mock out the behavior of the SiteVisit repository, but for the purpose of this blog post, this is good enough.</p><br /><p>Now, I just change my report writer to this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> VisitorsPerDayReportWriter : IDomainEventHandler<SiteVisted></pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> SiteVisitRepository _repository;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> VisitorsPerDayRepository _reportRepository;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> VisitorsPerDayReportWriter(SiteVisitRepository repository, VisitorsPerDayRepository reportRepository)</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _repository = repository;</pre><pre class="alt"><span class="lnum"> 9: </span> _reportRepository = reportRepository;</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(SiteVisted @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">if</span> (_repository</pre><pre><span class="lnum"> 16: </span> .GetWithoutIncluding(@<span class="kwrd">event</span>)</pre><pre class="alt"><span class="lnum"> 17: </span> .Any(s => s.When.Date == @<span class="kwrd">event</span>.When.Date && s.SessionId == @<span class="kwrd">event</span>.SessionId))</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> _reportRepository.GetReportFor(@<span class="kwrd">event</span>.When).UniqueVisitors++;</pre><pre class="alt"><span class="lnum"> 23: </span> </pre><pre><span class="lnum"> 24: </span> }</pre></div><br /><div class="csharpcode"><pre> </pre><pre>And BAM… </pre><pre><a href="http://lh4.ggpht.com/_hl5c3g00lJk/TU9u_EDEW4I/AAAAAAAAAOU/AIJTkpI3BWI/s1600-h/capture%5B5%5D.jpg"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="capture" border="0" alt="capture" src="http://lh6.ggpht.com/_hl5c3g00lJk/TU9u_SJNrzI/AAAAAAAAAOY/tjaRf_PfJyM/capture_thumb%5B1%5D.jpg?imgmax=800" width="244" height="156"></a></pre></div><br /><p>Hope this helps someone. Let me know if it does (or doesn’t).</p> Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-36474107825279281762011-02-06T15:18:00.001-08:002011-02-06T20:24:43.506-08:00It’s not about the data, it’s about what we do with it<p>Yeah, I promise I’ll get the NServiceBus subscriber thing published. I actually already wrote it, but I don’t wanna spoil my massive (yeah right) reader base, so I set that thing to be published on Monday.<br />
</p><p>I just saw an interesting question on the google DDD/CQRS user group. Here it is.<br />
</p><p>Hi,<br />
Suppose that I need to get count of unique web site visitors according<br />
to some criteria and I want that criteria to be supplied by my domain<br />
model not through a RDBMS or another infrastructure service. My goal<br />
is the persistency ignorance. My unique visitor business rule<br />
currently is: It is a visitor who visited once in a day.<br />
<br />
I figured I’d right a little blog about how I’d solve a problem like this in a behavior (instead of data) centric way. <p>Well, we’ve got a visitor right? Let’s write him. <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> Visitor</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> Visitor(<span class="kwrd">string</span> identifier)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> WebIdentifier = identifier;</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> WebIdentifier { get; <span class="kwrd">private</span> set; }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Visit()</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="rem">//Yeah, what goes here?? </span></pre><pre><span class="lnum"> 12: </span> }</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> Equals(<span class="kwrd">object</span> obj)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">return</span> <span class="kwrd">base</span>.Equals(obj);</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> <span class="kwrd">public</span> <span class="kwrd">bool</span> Equals(Visitor other)</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">if</span> (ReferenceEquals(<span class="kwrd">null</span>, other)) <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">if</span> (ReferenceEquals(<span class="kwrd">this</span>, other)) <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">return</span> Equals(other.WebIdentifier, WebIdentifier);</pre><pre class="alt"><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> </pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">int</span> GetHashCode()</pre><pre><span class="lnum"> 26: </span> {</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">return</span> (WebIdentifier != <span class="kwrd">null</span> ? WebIdentifier.GetHashCode() : 0);</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span> }</pre></div><br />
<p>Basically, the rules say, that we want to record a visitor when he visits for the first time in a day. So I write this test.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_last_visit_is_recorded()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var when = DateTime.Now;</pre><pre class="alt"><span class="lnum"> 5: </span> _visitor.Visit(when);</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> Assert.AreEqual(when, _visitor._lastVisit);</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">private</span> Visitor _visitor;</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> [SetUp]</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Arrange()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> _visitor = <span class="kwrd">new</span> Visitor(<span class="str">"SomeSessionId"</span>);</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><br />
<p>Making it pass is trivial, so trivial that I’m not gunna bother showing you the code. Lets move on. We’re supposed to have some screen that shows the count of visitors per day. We’re gunna use raise a domain event if the visitors last visit was yesterday or before.</p><br />
<p>Test?</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> a_new_visit_for_the_day_raises_a_UniqueVisitorAdded()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="rem">//Arrange</span></pre><pre class="alt"><span class="lnum"> 5: </span> var yesterday = DateTime.Now.AddDays(-1);</pre><pre><span class="lnum"> 6: </span> _visitor.Visit(yesterday);</pre><pre class="alt"><span class="lnum"> 7: </span> var eventWasRaised = <span class="kwrd">false</span>;</pre><pre><span class="lnum"> 8: </span> DomainEvents.Register<VisitorForFirstTimeInDay>(@<span class="kwrd">event</span>=> eventWasRaised = <span class="kwrd">true</span>);</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> <span class="rem">//act</span></pre><pre class="alt"><span class="lnum"> 11: </span> _visitor.Visit();</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="rem">//assert</span></pre><pre><span class="lnum"> 14: </span> Assert.IsTrue(eventWasRaised);</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><br />
<p>Ok, lets go make this work….</p><br />
<p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">void</span> Visit(DateTime? when = <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> when = when ?? DateTime.Now;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (_lastVisit.Date != DateTime.Today)</pre><pre class="alt"><span class="lnum"> 5: </span> DomainEvents.Raise(<span class="kwrd">new</span> VisitorForFirstTimeInDay(WebIdentifier,when.Value));</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> _lastVisit = when.Value;</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> }</pre></div><br />
<br />
<p> </p><br />
<p>Now, we need some readmodel that counts the visitors per day.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> VisitorsByTimeFrameReadModel</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> VisitorsByTimeFrameReadModel(DateTime date)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> Date = date;</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> DateTime Date { get; <span class="kwrd">private</span> set; }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">int</span> UniqueVisitorsCount { get; set; }</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> }</pre></div><br />
<div class="csharpcode"> </div><br />
<div class="csharpcode"> </div><br />
<div class="csharpcode">Now lets write a test to make sure we count the visitors per day with that read model.</div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> the_report_writer_adds_unique_visitor_count()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> var when = DateTime.Now;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> var theEvent = <span class="kwrd">new</span> VisitorForFirstTimeInDay(<span class="str">"SomeSessionId"</span>, when);</pre><pre class="alt"><span class="lnum"> 7: </span> _reporter.Handle(theEvent);</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span> var count = _repository.GetByDate(when).UniqueVisitorsCount;</pre><pre><span class="lnum"> 10: </span> Assert.AreEqual(1,count);</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> Visitor _visitor;</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">private</span> VisitorsByDateRepository _repository;</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">private</span> ReportWriter _reporter;</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> [SetUp]</pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Arrange()</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> _visitor = <span class="kwrd">new</span> Visitor(<span class="str">"SomeSessionId"</span>);</pre><pre><span class="lnum"> 20: </span> _repository = <span class="kwrd">new</span> VisitorsByDateRepository();</pre><pre class="alt"><span class="lnum"> 21: </span> _reporter = <span class="kwrd">new</span> ReportWriter(_repository);</pre><pre><span class="lnum"> 22: </span> }</pre></div><br />
<div class="csharpcode"> </div><br />
<div class="csharpcode">Let’s make it pass. I don’t care about persistence here really, so I’ll just throw them in a List, of course we could use an ORM instead.</div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> VisitorsByDateRepository</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> List<VisitorsByTimeFrameReadModel> _allVisitors;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> VisitorsByDateRepository()</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _allVisitors = <span class="kwrd">new</span> List<VisitorsByTimeFrameReadModel>();</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> VisitorsByTimeFrameReadModel GetByDate(DateTime date)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> var dateWeCareAbout = date.Date;</pre><pre><span class="lnum"> 12: </span> var item = _allVisitors.FirstOrDefault(v => v.Date == dateWeCareAbout);</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">if</span>(item == <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> item = <span class="kwrd">new</span> VisitorsByTimeFrameReadModel(dateWeCareAbout);</pre><pre><span class="lnum"> 16: </span> _allVisitors.Add(item);</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">return</span> item;</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> VisitorsByTimeFrameReadModel</pre><pre><span class="lnum"> 22: </span> {</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">public</span> VisitorsByTimeFrameReadModel(DateTime date)</pre><pre><span class="lnum"> 24: </span> {</pre><pre class="alt"><span class="lnum"> 25: </span> Date = date;</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> </pre><pre><span class="lnum"> 28: </span> <span class="kwrd">public</span> DateTime Date { get; <span class="kwrd">private</span> set; }</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">public</span> <span class="kwrd">int</span> UniqueVisitorsCount { get; set; }</pre><pre><span class="lnum"> 30: </span> </pre><pre class="alt"><span class="lnum"> 31: </span> }</pre></div><br />
<p>Just cause dates, get weird (at least they did when I was in high school – ha! Man, I’m funny, huh?). Let’s add some more tests to make sure things are working as expected. That we only raise the event when we need to.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> only_one_visitor_per_day_per_identifier_is_added()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var eventRaisedCount = 0;</pre><pre><span class="lnum"> 4: </span> DomainEvents.Register<VisitorForFirstTimeInDay>(e => eventRaisedCount++);</pre><pre class="alt"><span class="lnum"> 5: </span> var visitor = <span class="kwrd">new</span> Visitor(<span class="str">"SomeId"</span>);</pre><pre><span class="lnum"> 6: </span> var today = DateTime.Today;</pre><pre class="alt"><span class="lnum"> 7: </span> var atNoon = today.AddHours(12);</pre><pre><span class="lnum"> 8: </span> var atDinnerTime = today.AddHours(17);</pre><pre class="alt"><span class="lnum"> 9: </span> visitor.Visit(today);</pre><pre><span class="lnum"> 10: </span> visitor.Visit(atNoon);</pre><pre class="alt"><span class="lnum"> 11: </span> visitor.Visit(atDinnerTime);</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> Assert.AreEqual(1,eventRaisedCount);</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> }</pre></div><br />
<p> </p><br />
<p>And there we have it… We’re now counting unique visitors per day. In a DDD kinda way (that kinda rhymed, didn’t it?). </p><br />
<p>Whacha think?</p><br />
<p> </p><br />
<p>You can git the code at <a href="https://github.com/elliottohara/Blogs/tree/master/Ellemy.Blogs/Ellemy.Blogs/ItsAboutBehavior">https://github.com/elliottohara/Blogs/tree/master/Ellemy.Blogs/Ellemy.Blogs/ItsAboutBehavior</a></p>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com1tag:blogger.com,1999:blog-682843596462139829.post-42050778033087571212011-02-05T10:27:00.001-08:002011-02-05T14:29:34.039-08:00Publishing Events, cause sometimes everyone justs gotta know–Ellemy.CQRS<p> </p> <p>Ok, so my <a href="https://github.com/elliottohara/Ellemy.CQRS">examples</a> working pretty good. Ellemy.CQRS is doing it’s job pretty well. In fact (even though they don’t realize it), a lot of people are using it and liking it – in spite of the arguments about CQRS being “too complex”. I consider this a victory. But hey, whatever!</p> <p>The way things currently work though is that all event and command handling is done in process. When I wrote the first pass, this was a strategic decision – in fact it was one of my stated goals. But, lets move this puppy to real world. Like, in a scenario where we actually want durable messaging. </p> <p>So, I created a simple interface.</p> <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">interface</span> IEventPublisher</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">void</span> Publish<TDomainEvent>(TDomainEvent @<span class="kwrd">event</span>) <span class="kwrd">where</span> TDomainEvent : IDomainEvent;</pre><pre><span class="lnum"> 4: </span> }</pre></div><br /><p>and in my <a href="https://github.com/elliottohara/Ellemy.CQRS/blob/master/Source/Ellemy.CQRS/Event/DomainEvents.cs">DomainEvents</a> class, I made a small change in the Publish method. It now looks like this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Publish()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">if</span> (handlerActions != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> handlerActions.ForEach(a => a());</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> var publisher = Configure.CurrentConfig.EventPublisher;</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">if</span>(unpublishedEvents != <span class="kwrd">null</span>){</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">foreach</span> (var unpublishedEvent <span class="kwrd">in</span> unpublishedEvents)</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> publisher.Publish(unpublishedEvent);</pre><pre><span class="lnum"> 12: </span> }</pre><pre class="alt"><span class="lnum"> 13: </span> unpublishedEvents.Clear();</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><br /><p>I just added lines 7-12. I can clean this up a lot (and I will), but I wanted to leave this method all inline so you guys can see what it looks like. Line 7 grabs the currently configured instance of IEventPublisher. I wrote this one really quick to use if you don’t care to publish to remote systems.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> NoOpPublisher : IEventPublisher</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Publish<TDomainEvent>(TDomainEvent @<span class="kwrd">event</span>) <span class="kwrd">where</span> TDomainEvent:IDomainEvent</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br /><p>And made that the default by just newing one up in my Configuration class. </p><br /><p>Lets get to the good stuff now. I’m a big <a href="http://www.nservicebus.com/">NServiceBus</a> fan, so I wanna make a implementation that pushes out a NSB message for the events. NSB requires us to mark Messages with IMessage. I don’t want to bleed that into my domain, so we’re gunna make a quick class that is an IMessage, but contains the DomainEvent so that subscribers can get to it.</p><br /><p>It looks like this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Serializable]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> EventMessage<T> : IMessage</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">where</span> T : IDomainEvent</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> EventMessage(T @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> Payload = @<span class="kwrd">event</span>;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> EventMessage(){}</pre><pre><span class="lnum"> 10: </span> <span class="rem">/// <summary></span></pre><pre class="alt"><span class="lnum"> 11: </span> <span class="rem">/// Gets or sets transported event.</span></pre><pre><span class="lnum"> 12: </span> <span class="rem">/// </summary></span></pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> T Payload { get; set; }</pre><pre><span class="lnum"> 14: </span> }</pre></div><br /><p>So, next we’re gunna create an implementation of IEventPublisher that sends this puppy off to the bus, it’s really really simple.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> NServiceBusPublisher : IEventPublisher</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IBus _bus;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> NServiceBusPublisher(IBus bus)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _bus = bus;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Publish<TDomainEvent>(TDomainEvent @<span class="kwrd">event</span>) <span class="kwrd">where</span> TDomainEvent:IDomainEvent</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> var message = <span class="kwrd">new</span> EventMessage<TDomainEvent>(@<span class="kwrd">event</span>);</pre><pre class="alt"><span class="lnum"> 13: </span> _bus.Send(message);</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><br /><p>I just did a Send through NSB for now. I’ll move that to a publish once I get commands sent off, so that I won’t be publishing from a website. See <a href="http://www.make-awesome.com/2010/10/why-not-publish-nservicebus-messages-from-a-web-application/">here</a> for why that’s a bad idea.</p><br /><p>Next up, how to we tell Ellemy.CQRS to use this puppy? In my fluent Configure class I simply added this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> Configuration PublishEventsWith(IEventPublisher publisher)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> EventPublisher = publisher;</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">internal</span> IEventPublisher EventPublisher { get; <span class="kwrd">private</span> set; }</pre></div><br /><p>And in the assembly that contains the NSB stuff, I wrote an extension method for the configure class like so.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> ConfigurationExtensions</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> Configuration NServiceBusPublisher(<span class="kwrd">this</span> Configuration configuration,IBus bus)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> configuration.PublishEventsWith(<span class="kwrd">new</span> NServiceBusPublisher(bus));</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">return</span> configuration;</pre><pre class="alt"><span class="lnum"> 7: </span> } </pre><pre><span class="lnum"> 8: </span> }</pre></div><br /><p>Back to my website, I change my bootstrap code to look like this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> Configure.With()</pre><pre><span class="lnum"> 2: </span> .StructureMapBuilder(ObjectFactory.Container)</pre><pre class="alt"><span class="lnum"> 3: </span> .CommandExecutorsAreInAssemblyContainingType<CreateMessage>()</pre><pre><span class="lnum"> 4: </span> .HandlersAreInAssemblyContainingType<MessageReadModel>()</pre><pre class="alt"><span class="lnum"> 5: </span> .NServiceBusPublisher(ObjectFactory.Container.GetInstance<IBus>());</pre></div><br /><p>That’s really all we’ve gotta do to get Ellemy.CQRS to work. </p><br /><p>But we’ve gotta now make NServiceBus play nice. Since we’re only sending one type of message I set my config for the client like this.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd"><</span><span class="html">UnicastBusConfig</span><span class="kwrd">></span></pre><pre><span class="lnum"> 2: </span> <span class="kwrd"><</span><span class="html">MessageEndpointMappings</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd"><</span><span class="html">add</span> <span class="attr">Messages</span><span class="kwrd">="Ellemy.CQRS.Publishing.NServiceBus.EventMessage`1[[Ellemy.CQRS.Event.IDomainEvent, Ellemy.CQRS]],Ellemy.CQRS.Publishing.NServiceBus"</span> <span class="attr">Endpoint</span><span class="kwrd">="myserverinputqueue"</span> <span class="kwrd">/></span></pre><pre><span class="lnum"> 4: </span> <span class="kwrd"></</span><span class="html">MessageEndpointMappings</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd"></</span><span class="html">UnicastBusConfig</span><span class="kwrd">></span></pre></div><br /><p> </p><br /><p>Now, when we add a message through the example website, we see this in MSMQ</p><br /><p><a href="http://lh5.ggpht.com/_hl5c3g00lJk/TU2WhgKFLSI/AAAAAAAAAN8/bUUjqOUdIAQ/s1600-h/eventbus%5B3%5D.jpg"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="eventbus" border="0" alt="eventbus" src="http://lh6.ggpht.com/_hl5c3g00lJk/TU2Wh8WR5NI/AAAAAAAAAOA/GQYfHZsBwnQ/eventbus_thumb%5B1%5D.jpg?imgmax=800" width="377" height="246"></a></p><br /><p>Ok, we know we’re now sending off the message! Next blog, I’ll write a subscriber that’ll print stuff out to the console that it received the message. Or you can just git the code…</p><br /><p>Have fun with it!</p> Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-36048194022605885752011-01-25T05:26:00.000-08:002011-01-25T05:26:17.941-08:00Using Ellemy.CQRS–Check out the example project<p>Ok, lets use my little cqrs project. Note that this whole example is in the source for the <a href="git@github.com:elliottohara/Ellemy.CQRS.git">Ellemy.CQRS on git hub</a></p><p>First download the Ellemy.CQRS binaries (without example) from <a title="https://github.com/downloads/elliottohara/Ellemy.CQRS/binaries.zip" href="https://github.com/downloads/elliottohara/Ellemy.CQRS/binaries.zip">https://github.com/downloads/elliottohara/Ellemy.CQRS/binaries.zip</a>. </p><p>Unzip the payload and ref it from your project. </p><p>In my example project I kept things VERY simple. </p><p>I made a simple domain object.</p><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Ellemy.CQRS.Event;</pre><pre><span class="lnum"> 3: </span><span class="kwrd">using</span> Ellemy.CQRS.Example.Events;</pre><pre><span class="lnum"> 4: </span> </pre><pre><span class="lnum"> 5: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Example.Domain</pre><pre><span class="lnum"> 6: </span>{</pre><pre><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> Message</pre><pre><span class="lnum"> 8: </span> {</pre><pre><span class="lnum"> 9: </span> <span class="rem">/// <summary></span></pre><pre><span class="lnum"> 10: </span> <span class="rem">/// Creates a new instance of Message</span></pre><pre><span class="lnum"> 11: </span> <span class="rem">/// </summary></span></pre><pre><span class="lnum"> 12: </span> <span class="rem">/// <param name="id"></param></span></pre><pre><span class="lnum"> 13: </span> <span class="rem">/// <param name="text"></param></span></pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> Message(Guid id,<span class="kwrd">string</span> text)</pre><pre><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> Id = id;</pre><pre><span class="lnum"> 17: </span> DomainEvents.Raise(<span class="kwrd">new</span> MessageCreated(Id,text));</pre><pre><span class="lnum"> 18: </span> }</pre><pre><span class="lnum"> 19: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> Guid Id { get; set; }</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> ChangeText(<span class="kwrd">string</span> newtext)</pre><pre><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> DomainEvents.Raise(<span class="kwrd">new</span> MessageTextChanged(Id,newtext));</pre><pre><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> </pre><pre><span class="lnum"> 25: </span> }</pre><pre><span class="lnum"> 26: </span>}</pre></div><br />
<p>It doesn’t do much, of course… The whole point here is to show how to use Ellemy.CQRS, not solve a problem domain!</p><br />
<p>Events just have the properties you’d expect.</p><br />
<div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Ellemy.CQRS.Event;</pre><pre><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Example.Events</pre><pre><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MessageCreated : IDomainEvent</pre><pre><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> Guid MessageId { get; set; }</pre><pre><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Text { get; set; }</pre><pre><span class="lnum"> 10: </span> </pre><pre><span class="lnum"> 11: </span> <span class="kwrd">public</span> MessageCreated(Guid id, <span class="kwrd">string</span> text)</pre><pre><span class="lnum"> 12: </span> {</pre><pre><span class="lnum"> 13: </span> MessageId = id;</pre><pre><span class="lnum"> 14: </span> Text = text;</pre><pre><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> }</pre><pre><span class="lnum"> 17: </span>}</pre><pre> </pre><pre>When I made (for now) a single subscriber to MessageCreated. </pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Ellemy.CQRS.Event;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Ellemy.CQRS.Example.Events;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Example.Query</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MessageReadModelWriter : </pre><pre><span class="lnum"> 8: </span> IDomainEventHandler<MessageCreated></pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IRepository<MessageReadModel> _repository;</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> MessageReadModelWriter(IRepository<MessageReadModel> repository)</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> _repository = repository;</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Handle(MessageCreated @<span class="kwrd">event</span>)</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> var newReadModel = <span class="kwrd">new</span> MessageReadModel {Id = @<span class="kwrd">event</span>.MessageId, Text = @<span class="kwrd">event</span>.Text};</pre><pre><span class="lnum"> 20: </span> _repository.Save(newReadModel);</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span>}</pre></div><br />
<p>Now, lets move to the command side. I made a simple Command to create a message</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Ellemy.CQRS.Command;</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Example.Commands</pre><pre class="alt"><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> CreateMessage : ICommand</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> Guid MessageId { get; set; }</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Text { get; set; }</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> CreateMessage(Guid messageId, <span class="kwrd">string</span> text)</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> MessageId = messageId;</pre><pre><span class="lnum"> 14: </span> Text = text;</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span>}</pre></div></div><br />
<p>And then the Handler for it.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Ellemy.CQRS.Command;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Ellemy.CQRS.Example.Query;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Example.Commands</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> CreateMessageHandler : ICommandExecutor<CreateMessage></pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IRepository<MessageReadModel> _repository;</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> CreateMessageHandler(IRepository<MessageReadModel> repository)</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> _repository = repository;</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> Execute(CreateMessage command)</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> var message = <span class="kwrd">new</span> MessageReadModel {Id = command.MessageId, Text = command.Text};</pre><pre class="alt"><span class="lnum"> 19: </span> _repository.Save(message);</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span>}</pre></div><br />
<p>Now, it’s time to plug this into a UI… First, we’ve gotta bootstrap everything up. I did this right in my global.ascx, not the way I’d do it in a production app, but once again, this is for simplicity, not maintainability. </p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">protected</span> <span class="kwrd">void</span> Application_Start()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> AreaRegistration.RegisterAllAreas();</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> RegisterGlobalFilters(GlobalFilters.Filters);</pre><pre><span class="lnum"> 6: </span> RegisterRoutes(RouteTable.Routes);</pre><pre class="alt"><span class="lnum"> 7: </span> ObjectFactory.Configure(c =></pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> c.For(<span class="kwrd">typeof</span> (IRepository<>)).Use(<span class="kwrd">typeof</span>(InMemoryCacheRepository<>));</pre><pre><span class="lnum"> 10: </span> });</pre><pre class="alt"><span class="lnum"> 11: </span> Configure.With()</pre><pre><span class="lnum"> 12: </span> .StructureMapBuilder(ObjectFactory.Container)</pre><pre class="alt"><span class="lnum"> 13: </span> .CommandExecutorsAreInAssemblyContainingType<CreateMessageHandler>()</pre><pre><span class="lnum"> 14: </span> .HandlersAreInAssemblyContainingType<MessageReadModel>();</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> }</pre><pre> </pre><pre>Check out line 11… That’ll configure the CQRS infrastructure so that it can locate CommandExecutors and events. It’ll also wire up the appropriate structure map implementations of the Factories (see my <a href="http://blog.elliottohara.com/2011/01/my-own-little-cqrs-framework-that-doesn_24.html">last post</a> on that).</pre><pre> </pre><pre>Now lets wire up a Controller.</pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Ellemy.CQRS.Command;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Ellemy.CQRS.Example.Commands;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> Ellemy.CQRS.Example.Query;</pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> Ellemy.CQRS.Example.Web.Infrastructure;</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Example.Web.Controllers</pre><pre class="alt"><span class="lnum"> 9: </span>{</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MessageController : Controller</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IRepository<MessageReadModel> _repository;</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> MessageController() : <span class="kwrd">this</span>(<span class="kwrd">new</span> InMemoryCacheRepository<MessageReadModel>())</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> <span class="kwrd">public</span> MessageController(IRepository<MessageReadModel> repository)</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> _repository = repository;</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> </pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">public</span> ActionResult Index()</pre><pre><span class="lnum"> 24: </span> {</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">return</span> View(_repository.GetAll());</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> </pre><pre><span class="lnum"> 28: </span> [HttpGet]</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">public</span> ActionResult Create()</pre><pre><span class="lnum"> 30: </span> {</pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">return</span> View();</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span> </pre><pre><span class="lnum"> 34: </span> [HttpPost]</pre><pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">public</span> ActionResult Create(<span class="kwrd">string</span> text)</pre><pre><span class="lnum"> 36: </span> {</pre><pre class="alt"><span class="lnum"> 37: </span> CommandDispatcher.Dispatch(<span class="kwrd">new</span> CreateMessage(Guid.NewGuid(), text));</pre><pre><span class="lnum"> 38: </span> <span class="kwrd">return</span> RedirectToAction(<span class="str">"Index"</span>);</pre><pre class="alt"><span class="lnum"> 39: </span> }</pre><pre><span class="lnum"> 40: </span> }</pre><pre class="alt"><span class="lnum"> 41: </span>}</pre></div></div><br />
<p>Really, all we care about here is the Create ActionResult. The call to CommandDispatcher.Dispatch will execute the associated hander for the message. </p><br />
<p>Honestly folks… It’s that simple! Questions? Comments?</p><br />
<p>Have fun!</p>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-26380442929976949042011-01-24T16:51:00.000-08:002011-01-24T16:51:38.181-08:00My own little CQRS framework - that doesn't piss off "the other guys"<p> </p><p>Git This: <a title="git://github.com/elliottohara/Ellemy.CQRS.git" href="git://github.com/elliottohara/Ellemy.CQRS.git">git://github.com/elliottohara/Ellemy.CQRS.git</a></p><p>Took a look at <a href="http://ncqrs.org/">NCQRS</a> today. It pretty much rocks. I have one problem though. I having problems selling CQRS at work, mainly because many other engineers just even have a strong grasp of Domain Driven Design, much less CQRS. Trying to sell something like NCQRS, as awesome as it is at doing what it's designed to do just wouldn't fly in my org. </p><p>I figured I'd create a little lightweight solution that is how I typically handle CQRS is my "mainstream" organization where I have to fight the political battles with DBAs and other engineers who think I'm just a alt.net elitist... I'm quite sure I'm not alone. </p><p>Requirements:</p><ul><li>Easy to understand and use- even for developers who have very little understanding of DDD and CQRS</li>
<li>Doesn't even pretend to embrace event stores or alternative persistence mechanisms - I live in a world where that'll just create a backlash against the whole idea of CQRS</li>
<li>Doesn't depart from the stuff that the CQRS crowd espouses - so we can point to his blog when the slight learning curve is encountered, we can point at the large community, and show that this isn't something that we cooked up. At the same time, let's not scare people off by requiring busses and stuff.</li>
<li>Have fun doing it :)</li>
</ul><p>Ok... so lets start. First of all we're ripping off <a href="http://www.udidahan.com/2009/06/14/domain-events-salvation/">Domain Events - Salvation</a>, except (as silly as this sounds) I'm gunna stick to all the traditional naming conventions. Also I'm making a Factory to locate all handlers, that way we don't have to retouch this when the folks that think IOC containers are "redirection tools intended to confuse pragmatic developers" don't wanna use StructureMap and want to go manually wire up every handler so that they don't get confused.</p><p>Here's what my new DomainEvents class looks like.</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Event</pre><pre class="alt"><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> DomainEvents</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> [ThreadStatic] <span class="rem">//so that each thread has its own callbacks</span></pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">private</span> <span class="kwrd">static</span> List<Delegate> actions;</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> IHandlerFactory Container { get; set; } <span class="rem">//as before</span></pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="rem">//Registers a callback for the given domain event</span></pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Register<T>(Action<T> callback) <span class="kwrd">where</span> T : IDomainEvent</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">if</span> (actions == <span class="kwrd">null</span>)</pre><pre class="alt"><span class="lnum"> 17: </span> actions = <span class="kwrd">new</span> List<Delegate>();</pre><pre><span class="lnum"> 18: </span> </pre><pre class="alt"><span class="lnum"> 19: </span> actions.Add(callback);</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> <span class="rem">//Clears callbacks passed to Register on the current thread</span></pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> ClearCallbacks()</pre><pre><span class="lnum"> 24: </span> {</pre><pre class="alt"><span class="lnum"> 25: </span> actions = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> </pre><pre><span class="lnum"> 28: </span> <span class="rem">//Raises the given domain event</span></pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Raise<T>(T args) <span class="kwrd">where</span> T : IDomainEvent</pre><pre><span class="lnum"> 30: </span> {</pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">if</span> (Container != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 32: </span> <span class="kwrd">foreach</span> (var handler <span class="kwrd">in</span> Container.GetHandlersFor<T>())</pre><pre class="alt"><span class="lnum"> 33: </span> handler.Handle(args);</pre><pre><span class="lnum"> 34: </span> </pre><pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">if</span> (actions != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 36: </span> <span class="kwrd">foreach</span> (Delegate action <span class="kwrd">in</span> actions)</pre><pre class="alt"><span class="lnum"> 37: </span> <span class="kwrd">if</span> (action <span class="kwrd">is</span> Action<T>)</pre><pre><span class="lnum"> 38: </span> ((Action<T>) action)(args);</pre><pre class="alt"><span class="lnum"> 39: </span> }</pre><pre><span class="lnum"> 40: </span> }</pre><pre class="alt"><span class="lnum"> 41: </span>}</pre></div><br />
<p>and the IHandlerFactory looks like this </p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Event</pre><pre><span class="lnum"> 4: </span>{</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IHandlerFactory</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> IEnumerable<IDomainEventHandler<TEvent>> GetHandlersFor<TEvent>() <span class="kwrd">where</span> TEvent : IDomainEvent;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span>}</pre></div><br />
<p>I think I also wanna move that handler.Handle(args) into another concept at some point in this. It would be nice to at least async this (or yeah, just publish to a bus, but yeah, let's not scare people!).</p><br />
<p>Simple enough. Gunna do the exact same thing for the command side of the house.</p><br />
<p>Made a maker for Commands named ICommand, and this</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Command</pre><pre><span class="lnum"> 2: </span>{</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> ICommandExecutor<TCommand> <span class="kwrd">where</span> TCommand:ICommand</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">void</span> Execute(TCommand command);</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span>}</pre></div><br />
<p>Once again, lets create a Factory interface so that, given a command, we can locate its single executor - we'll create an implementation that uses my IOC container of choice in a bit.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Command</pre><pre><span class="lnum"> 2: </span>{</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> ICommandExecutorFactory</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> ICommandExecutor<TCommand> GetExecutorFor<TCommand>() <span class="kwrd">where</span> TCommand : ICommand;</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span>}</pre></div><br />
<p> </p><br />
<p>Really, that's all the abstract stuff we're gunna need to make it work. Now we need to wire it all up so that it'll actually work. I'm gunna gunna create some kinda bootstrap thingy that'll set the factories that the command and event side need. I really like the extension model that NServiceBus and NCQRS use, so I'll give something like that a shot.</p><br />
<p>Here's what I came up with.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Ellemy.CQRS.Command;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Ellemy.CQRS.Event;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> Ellemy.CQRS</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> Configure</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">private</span> <span class="kwrd">static</span> Configuration _currentConfig;</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">internal</span> <span class="kwrd">static</span> Configuration CurrentConfig{get { <span class="kwrd">return</span> _currentConfig ?? (_currentConfig = <span class="kwrd">new</span> Configuration()); }</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> Configuration With()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">return</span> _currentConfig;</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> Configuration</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">public</span> Configuration HandlerFactoryOf(IHandlerFactory handlerFactory)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> HandlerFactory = handlerFactory;</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">public</span> Configuration CommandExecutorFactoryOf(ICommandExecutorFactory commandExecutorFactory)</pre><pre><span class="lnum"> 26: </span> {</pre><pre class="alt"><span class="lnum"> 27: </span> CommandExecutorFactory = commandExecutorFactory;</pre><pre><span class="lnum"> 28: </span> <span class="kwrd">return</span> <span class="kwrd">this</span>;</pre><pre class="alt"><span class="lnum"> 29: </span> }</pre><pre><span class="lnum"> 30: </span> </pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">internal</span> ICommandExecutorFactory CommandExecutorFactory { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 32: </span> </pre><pre class="alt"><span class="lnum"> 33: </span> <span class="kwrd">internal</span> IHandlerFactory HandlerFactory { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 34: </span> }</pre><pre class="alt"><span class="lnum"> 35: </span>}</pre></div><br />
<p> </p><br />
<p>The idea is that we'll make a new assembly that'll have the concrete implementations of the two factories, and then create Extension methods on Configuration that'll simply call HandlerFactoryOf and CommandExecutorFactoryOf. </p><br />
<p>But, lets slow down first, and write some concrete factories. I'm gunna use StructureMap. This is really simple... Here we go.</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Ellemy.CQRS.Command;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Ellemy.CQRS.Event;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> StructureMap;</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Implementations.StructureMap</pre><pre><span class="lnum"> 8: </span>{</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> StructureMapBuilder : IHandlerFactory, ICommandExecutorFactory</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IContainer _container;</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> StructureMapBuilder(IContainer container)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> _container = container;</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> <span class="kwrd">public</span> IEnumerable<IDomainEventHandler<TEvent>> GetHandlersFor<TEvent>() <span class="kwrd">where</span> TEvent : IDomainEvent</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">return</span> _container.GetAllInstances<IDomainEventHandler<TEvent>>();</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> </pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">public</span> ICommandExecutor<TCommand> GetExecutorFor<TCommand>() <span class="kwrd">where</span> TCommand : ICommand</pre><pre><span class="lnum"> 24: </span> {</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">return</span> _container.GetInstance<ICommandExecutor<TCommand>>();</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre><pre><span class="lnum"> 28: </span>}</pre><pre>Now, lets go ahead and build some extensions on Configuration that specifies this object as both the Handler and CommandExecutor factories. Kinda like this.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> StructureMap;</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> Ellemy.CQRS.Implementations.StructureMap</pre><pre class="alt"><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> ConfigurationExtensions</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> Configuration StructureMapBuilder(<span class="kwrd">this</span> Configuration config)</pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">return</span> config.StructureMapBuilder(ObjectFactory.Container);</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> Configuration StructureMapBuilder(<span class="kwrd">this</span> Configuration config, IContainer container)</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> var builder = <span class="kwrd">new</span> StructureMapBuilder(container);</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">return</span> config</pre><pre><span class="lnum"> 16: </span> .CommandExecutorFactoryOf(builder)</pre><pre class="alt"><span class="lnum"> 17: </span> .HandlerFactoryOf(builder);</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span>}</pre><pre> </pre><pre> </pre><pre>Ok, now lets use it, but I’ll blog that next. Or you can just git the code… there’s a little example in there of using it.</pre><pre> </pre><pre>Have fun with it!</pre></div></div>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com2tag:blogger.com,1999:blog-682843596462139829.post-80471232735087588772010-11-20T17:45:00.001-08:002010-11-24T07:14:16.442-08:00An extendable MVC2 ModelMetaDataProvider (OCP baby!)So, last blog I mentioned that my ModelMetaDataProvider smelled like an OCP violation to me. While technically it's only doing one thing right now, when I read <a href="http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html">Brad Wilsons blog</a> on what he's doing, I decided I wanted that, but didn't want that big honking class doing all that stuff, so I figured I'd do a little refactoring. I started with this.<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> MetadataProvider : DataAnnotationsModelMetadataProvider</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">protected</span> <span class="kwrd">override</span> ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<<span class="kwrd">object</span>> modelAccessor, Type modelType, <span class="kwrd">string</span> propertyName)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> var metadata = <span class="kwrd">base</span>.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);</pre><pre><span class="lnum"> 6: </span> var linkText = attributes.OfType<LinkText>().FirstOrDefault();</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">if</span>(linkText != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 8: </span> metadata.AdditionalValues.Add(<span class="str">"LinkText"</span>,linkText);</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> <span class="kwrd">return</span> metadata;</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> }</pre></div><p>I just wanna have some interface that builds up a ModelMetaData. For simplicity sake, I'll just give it the same signature as DataAnnotationsModelMetadataProvider, but just allow you to send in the ModelMetaData that's already built.<br />
</p><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IModelMetaBuilder</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> ModelMetadata BuildUp(ModelMetadata metaData, IEnumerable<Attribute> attributes, Type containerType,</pre><pre><span class="lnum"> 4: </span> Func<<span class="kwrd">object</span>> modelAccessor, Type modelType, <span class="kwrd">string</span> propertyName);</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><p><br />
Then I'll make the MetadataProvider just get all instances of those from the container, loop through em all, and build up. That way we can add new behavior without opening it back up (yeah, that's OCP).<br />
</p><p>First, lets make my LinkText stuff from my <a href="http://blog.elliottohara.com/2010/11/mvc-templating-linktextattribute-and.html">last blog</a> and move it into one of these,<br />
</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">class</span> LinkTextModelMetadataBuilder : IModelMetaBuilder</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> ModelMetadata BuildUp(ModelMetadata metadata, IEnumerable<Attribute> attributes, Type containerType, Func<<span class="kwrd">object</span>> modelAccessor, Type modelType, <span class="kwrd">string</span> propertyName)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> var linkText = attributes.OfType<LinkText>().FirstOrDefault();</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">if</span> (linkText != <span class="kwrd">null</span>)</pre><pre class="alt"><span class="lnum"> 7: </span> metadata.AdditionalValues.Add(<span class="str">"LinkText"</span>, linkText);</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">return</span> metadata;</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> }</pre></div><p>Now... Lets go fix my MetaDataProvider to use my IOC container of choice (StructureMap). To be honest, this is so simple, I'm kinda embarrased that I'm making a blog out of it - but hey, I've got no pride (yeah right).<br />
</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MetadataProvider : DataAnnotationsModelMetadataProvider</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IContainer _container;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> MetadataProvider(IContainer container)</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> _container = container;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> <span class="kwrd">protected</span> <span class="kwrd">override</span> ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<<span class="kwrd">object</span>> modelAccessor, Type modelType, <span class="kwrd">string</span> propertyName)</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> var metadata = <span class="kwrd">base</span>.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);</pre><pre class="alt"><span class="lnum"> 13: </span> var allBuilders = _container.GetAllInstances<IModelMetadataBuilder>();</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">foreach</span> (var builder <span class="kwrd">in</span> allBuilders)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> builder.BuildUp(metadata, attributes, containerType, modelAccessor, modelType, propertyName);</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">return</span> metadata;</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> }</pre></div><p>Next up, lets write a cute little registry for StructureMap that'll go get em all. Oh yeah, I renamed that interface to IModelMetadataBuilder. Here it is.<br />
</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> ModelMetadataProviderRegistry : Registry</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> ModelMetadataProviderRegistry()</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> Scan(scanner =></pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> scanner.AssemblyContainingType(GetType());</pre><pre><span class="lnum"> 8: </span> scanner.AddAllTypesOf<IModelMetadataBuilder>();</pre><pre class="alt"><span class="lnum"> 9: </span> });</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre></div><p>And there we go... Now we just gotta change the App_Start stuff that wired up the ModelMetadataProvider to inject the Container, or better yet, just go get the instance from the container. Like so (well like the last line here - line 13).<br />
</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">protected</span> <span class="kwrd">void</span> Application_Start()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span> StructureMapInitilizer.Initilize();</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">new</span> Bootstrapper(ObjectFactory.Container).BootstrapApplication();</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="rem">//TODO: make this stuff bootstrap classes</span></pre><pre><span class="lnum"> 8: </span> AreaRegistration.RegisterAllAreas();</pre><pre class="alt"><span class="lnum"> 9: </span> RegisterGlobalFilters(GlobalFilters.Filters);</pre><pre><span class="lnum"> 10: </span> RegisterRoutes(RouteTable.Routes);</pre><pre class="alt"><span class="lnum"> 11: </span> Db = OpenDatabase();</pre><pre><span class="lnum"> 12: </span> ControllerBuilder.Current.SetControllerFactory(<span class="kwrd">new</span> StructureMapControllerFactory(ObjectFactory.Container));</pre><pre class="alt"><span class="lnum"> 13: </span> ModelMetadataProviders.Current = ObjectFactory.Container.GetInstance<MetadataProvider>();</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><p>Cool... and everything is still passing! Next up... I'm gunna go steal all the cool ModelMetaData stuff from everyone, but implement them in their own builders. First up is <a href="http://bradwilson.typepad.com/blog/2010/01/why-you-dont-need-modelmetadataattributes.html">Brad Wilsons</a>, because it's a whole lot of awesome.<br />
</p><p>All I need to do is to instead of inheriting the DataAnnotationsModelMetaDataProvider, I'll implement IModelMetadataBuilder. Since the signatures are almost exactly the same this is really easy. Here's what I ended up with. <br />
</p><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> WilsonModelMetadataBuilder : IModelMetadataBuilder</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> ModelMetadata BuildUp(ModelMetadata metadata,IEnumerable<Attribute> attributes,</pre><pre><span class="lnum"> 4: </span> Type containerType,</pre><pre class="alt"><span class="lnum"> 5: </span> Func<<span class="kwrd">object</span>> modelAccessor,</pre><pre><span class="lnum"> 6: </span> Type modelType,</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">string</span> propertyName)</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="rem">// Prefer [Display(Name="")] to [DisplayName]</span></pre><pre class="alt"><span class="lnum"> 13: </span> DisplayAttribute display = attributes.OfType<DisplayAttribute>().FirstOrDefault();</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">if</span> (display != <span class="kwrd">null</span>)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">string</span> name = display.GetName();</pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">if</span> (name != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> metadata.DisplayName = name;</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> <span class="rem">// There was no 3.5 way to set these values</span></pre><pre class="alt"><span class="lnum"> 23: </span> metadata.Description = display.GetDescription();</pre><pre><span class="lnum"> 24: </span> metadata.ShortDisplayName = display.GetShortName();</pre><pre class="alt"><span class="lnum"> 25: </span> metadata.Watermark = display.GetPrompt();</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> </pre><pre><span class="lnum"> 28: </span> <span class="rem">// Prefer [Editable] to [ReadOnly]</span></pre><pre class="alt"><span class="lnum"> 29: </span> EditableAttribute editable = attributes.OfType<EditableAttribute>().FirstOrDefault();</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">if</span> (editable != <span class="kwrd">null</span>)</pre><pre class="alt"><span class="lnum"> 31: </span> {</pre><pre><span class="lnum"> 32: </span> metadata.IsReadOnly = !editable.AllowEdit;</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre><pre><span class="lnum"> 34: </span> </pre><pre class="alt"><span class="lnum"> 35: </span> <span class="rem">// If [DisplayFormat(HtmlEncode=false)], set a data type name of "Html"</span></pre><pre><span class="lnum"> 36: </span> <span class="rem">// (if they didn't already set a data type)</span></pre><pre class="alt"><span class="lnum"> 37: </span> DisplayFormatAttribute displayFormat = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();</pre><pre><span class="lnum"> 38: </span> <span class="kwrd">if</span> (displayFormat != <span class="kwrd">null</span></pre><pre class="alt"><span class="lnum"> 39: </span> && !displayFormat.HtmlEncode</pre><pre><span class="lnum"> 40: </span> && String.IsNullOrWhiteSpace(metadata.DataTypeName))</pre><pre class="alt"><span class="lnum"> 41: </span> {</pre><pre><span class="lnum"> 42: </span> metadata.DataTypeName = DataType.Html.ToString();</pre><pre class="alt"><span class="lnum"> 43: </span> }</pre><pre><span class="lnum"> 44: </span> </pre><pre class="alt"><span class="lnum"> 45: </span> <span class="kwrd">return</span> metadata;</pre><pre><span class="lnum"> 46: </span> }</pre><pre class="alt"><span class="lnum"> 47: </span> }</pre></div><p>There ya go folks... Have fun with it.<br />
If you <a href="https://github.com/elliottohara/myDojo">pull down my code</a>, just look in myDojo.Infrastructure.Web. It's all there.<br />
</p><p>Peace out!<br />
<br /><br />
E </p>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com2tag:blogger.com,1999:blog-682843596462139829.post-72557417711179647542010-11-20T11:21:00.001-08:002010-11-20T12:04:35.254-08:00MVC templating, LinkTextAttribute and List templates (Razor and MVC3)<p>So, while this isn't bad code.</p><span style="widows: 2; text-transform: none; text-indent: 0px; border-collapse: separate; font: medium 'Times New Roman'; white-space: normal; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" class="Apple-style-span"><span style="font-family: consolas, 'Courier New', courier, monospace; font-size: small" class="Apple-style-span"> <div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd"><</span><span class="html">ul</span><span class="kwrd">></span></pre><pre><span class="lnum"> 2: </span> @foreach (var item in Model)</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="kwrd"><</span><span class="html">li</span><span class="kwrd">><</span><span class="html">a</span> <span class="attr">href</span><span class="kwrd">='Details/@Model.id'</span> <span class="attr">title</span><span class="kwrd">='@Model.Name'</span><span class="kwrd">></span>@Model.Name<span class="kwrd"></</span><span class="html">a</span><span class="kwrd">></</span><span class="html">li</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 5: </span> }</pre><pre><span class="lnum"> 6: </span><span class="kwrd"></</span><span class="html">ul</span><span class="kwrd">></span></pre></div><br /><p> </p><br /><p><font color="#222222" face="Arial">Frankly, I'm sick of writing it. How often do we write stuff like this? Just about one for every controller. This just isn't using the MVC stack to it's full potential - plus, I've been doing a lot of reading on templating on MVC2/3 and figured I'd give it a shot.<br><br>So, first thing I did was write a quick little template that'll work for any IEnumerable. For first pass, I didn't even worry about the links... just get a generic template for a UL. I added a new partial view in ~/Views/Shared/DisplayTemplates and named it UL.cshtml. Sheesh, I love Razor! Anyway it's pretty simple..</font></p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>@model System.Collections.IEnumerable</pre><pre><span class="lnum"> 2: </span><span class="kwrd"><</span><span class="html">ul</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 3: </span> @foreach(var item in Model){</pre><pre><span class="lnum"> 4: </span> <span class="kwrd"><</span><span class="html">li</span><span class="kwrd">></span>@Html.DisplayFor(m =<span class="kwrd">></span> item)<span class="kwrd"></</span><span class="html">li</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 5: </span> }</pre><pre><span class="lnum"> 6: </span><span class="kwrd"></</span><span class="html">ul</span><span class="kwrd">></span></pre></div><br /><p>Note that I'm using Html.DisplayFor... If you're just pumping out values to the client like so @Model.SomePropery, you're really not using the MVC stack like you can be. Display templates give you free hooks to alter the html if the need arises for REALLY cheap, plus it gives you a central place to generate the html for a specific data type, no matter how complex or simple.</p><br /><p>When I sent this model through it.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [DefaultProperty(<span class="str">"Name"</span>)]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> DojoDetails : ObjectWithIdentity </pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">string</span> Name { get; set; }</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> Address Address { get; set; }</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br /><p>It just generated a list of Names for Dojos. So now, what I wanna do is be able to mark the ViewModel with some Attribute that says "Hey, this is the Text and Here is the format and here is the value for the link". So I made this little attribute.<br></p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> LinkText : Attribute</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> LinkFormatProperty { get; set; }</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> LinkFormatString { get; set; }</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br /><p>And I went and marked up my DojoDetails with it.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> DojoDetails : ObjectWithIdentity </pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> [LinkText(LinkFormatProperty = <span class="str">"Id"</span>,LinkFormatString = <span class="str">"~/Dojos/Details/{0}"</span>)]</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">string</span> Name { get; set; }</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> Address Address { get; set; }</pre><pre><span class="lnum"> 6: </span> }</pre></div><br /><p>Next task is to make sure we actually get that data down into the metadata for the view model. To do this, we need to write an implementation of DataAnnotationsModelMetadataProvider. It's got one method that we need to override (CreateMetadata). Not too difficult. The MVC stack put an AdditionalProperties Dictionary on ModelMetaData, so we'll just pump in the attribute right there so we can get to it in our templates. Here's how I did it.</p><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MetadataProvider : DataAnnotationsModelMetadataProvider</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">protected</span> <span class="kwrd">override</span> ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<<span class="kwrd">object</span>> modelAccessor, Type modelType, <span class="kwrd">string</span> propertyName)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> var metadata = <span class="kwrd">base</span>.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);</pre><pre><span class="lnum"> 6: </span> var linkText = attributes.OfType<LinkText>().FirstOrDefault();</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">if</span>(linkText != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 8: </span> metadata.AdditionalValues.Add(<span class="str">"LinkText"</span>,linkText);</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">return</span> metadata;</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> }</pre><pre> </pre><pre>Simple enough, just see if the attribute is there, if so, add it. This code smells like a OCP violation, but I'm gunna leave it alone for now, since I wanna stay on task. </pre><pre> </pre><pre>Next up, I'm gunna go write a new template that'll grab that linktext attribute and create an anchor tag. I created a little ViewModel for the link so I can write a template for it - it's silly simple.</pre><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> ModelLinkViewModel</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> ModelLinkViewModel()</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> ModelLinkViewModel(<span class="kwrd">string</span> link, <span class="kwrd">string</span> text)</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> Url = link;</pre><pre><span class="lnum"> 10: </span> Text = text;</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Text { get; set; }</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> <span class="kwrd">string</span> Url { get; set; }</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre></div><br /><div class="csharpcode"> </div><br /><div class="csharpcode">Lets go mark up our view model now.</div><br /><div class="csharpcode"> </div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> DojoDetails : ObjectWithIdentity </pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> [LinkText(LinkFormatProperty = <span class="str">"Id"</span>,LinkFormatString = <span class="str">"~/Dojos/Details/{0}"</span>)]</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">string</span> Name { get; set; }</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> Address Address { get; set; }</pre><pre><span class="lnum"> 6: </span> }</pre></div></div><br /><div class="csharpcode"> </div><br /><div class="csharpcode">Id is on ObjectWithIdentity, and we're just giving a virtual url to the details page. Now I'll create a little helper method that'll a ModelLinkViewModel from the DojoDetails.</div><br /><div class="csharpcode"> </div><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> ModelMetaDataExtensions</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> ModelLinkViewModel LinkToModelDetails<TModel>(<span class="kwrd">this</span> HtmlHelper<TModel> helper)</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> var displayProperty = helper.ViewData.ModelMetadata.Properties.First(p => p.AdditionalValues.ContainsKey(<span class="str">"LinkText"</span>));</pre><pre><span class="lnum"> 6: </span> var linkTextAttribute = (ModelMetaData.LinkText)displayProperty.AdditionalValues[<span class="str">"LinkText"</span>];</pre><pre class="alt"><span class="lnum"> 7: </span> var linkFormat = linkTextAttribute.LinkFormatString;</pre><pre><span class="lnum"> 8: </span> var linkProperty = helper.ViewData.ModelMetadata.Properties.First(p => p.PropertyName == linkTextAttribute.LinkFormatProperty);</pre><pre class="alt"><span class="lnum"> 9: </span> var request = ServiceLocation.CurrentContainer.GetInstance<HttpRequestBase>();</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> var link = request.Resolve(String.Format(linkFormat, linkProperty.Model));</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">if</span>(! (displayProperty.Model <span class="kwrd">is</span> <span class="kwrd">string</span>))</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(String.Format(<span class="str">"[LinkText] can only be used on String properties. {0} is a {1}"</span>,displayProperty.PropertyName, displayProperty.ContainerType.Name));</pre><pre><span class="lnum"> 14: </span> var text = (String)displayProperty.Model;</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> ModelLinkViewModel(link, text);</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> }</pre><pre> </pre><pre>So, on link 5, I just go find the property marked with LinkText attribute. Once we have it, we pull all the data out of it. On Line 8, I'm going and getting the property mentioned in the LinkTextAttribute as the propery that has the value to format. Line 11 is just an extension I made that Resolves virtual url's and we just format up the link.</pre><pre> </pre><pre>So now that that works, lets go make a template for the list item.</pre><pre> </pre><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>@model object</pre><pre><span class="lnum"> 2: </span>@using myDojo.Infrastructure.Web.HtmlHelpers;</pre><pre class="alt"><span class="lnum"> 3: </span>@if (Model == null) {</pre><pre><span class="lnum"> 4: </span> <span class="kwrd"><</span><span class="html">text</span><span class="kwrd">></span>@ViewData.ModelMetadata.NullDisplayText<span class="kwrd"></</span><span class="html">text</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 5: </span>}</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span>else if(ViewData.ModelMetadata.Properties.FirstOrDefault(p =<span class="kwrd">></span> p.AdditionalValues.ContainsKey("LinkText")) != null)</pre><pre><span class="lnum"> 8: </span>{</pre><pre class="alt"><span class="lnum"> 9: </span> var x = Html.LinkToModelDetails();</pre><pre><span class="lnum"> 10: </span> <span class="kwrd"><</span><span class="html">text</span><span class="kwrd">></span>@Html.DisplayFor(m =<span class="kwrd">></span> x)<span class="kwrd"></</span><span class="html">text</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span>}else if (ViewData.TemplateInfo.TemplateDepth <span class="kwrd">></span> 1)</pre><pre class="alt"><span class="lnum"> 13: </span>{</pre><pre><span class="lnum"> 14: </span> <span class="kwrd"><</span><span class="html">text</span><span class="kwrd">></span>@ViewData.ModelMetadata.SimpleDisplayText<span class="kwrd"></</span><span class="html">text</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 15: </span>}</pre></div><pre> </pre><pre>Ok, so this has way too much defensive code, but whatever... really all we care about is line 7. We just go grab the LinkText property from the Model, and use the extension method to get a ModelLinkViewModel, then use our old friend DisplayFor. If there isn't a LinkText propery, it'll just display the DefaultPropery. </pre><pre> </pre><pre>For now, this is just gunna display .ToString() on ModelLinkViewModel - and I doubt our users wanna see the type of ModelLinkViewModel, so lets go write a template for that type.</pre><pre> </pre><pre>Once again, I go add another view to ~/Views/Shared/DisplayTemplates, and I name it the Type that I wanna template.</pre><pre> </pre><pre>Here it is. Really couldn't be much simpler.</pre><pre> </pre><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>@model myDojo.Infrastructure.Web.HtmlHelpers.ModelLinkViewModel</pre><pre><span class="lnum"> 2: </span><span class="kwrd"><</span><span class="html">a</span> <span class="attr">href</span><span class="kwrd">='@Model.Url'</span> <span class="attr">title</span><span class="kwrd">='@Model.Text'</span><span class="kwrd">></span>@Model.Text<span class="kwrd"></</span><span class="html">a</span><span class="kwrd">></span></pre></div><pre> </pre><pre>There we go! </pre><pre>So now, we go change the original list page to this.</pre><pre> </pre><br /><div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>@inherits System.Web.Mvc.WebViewPage<span class="kwrd"><</span><span class="html">IEnumerable</span><span class="kwrd"><</span><span class="html">MyDojo.Query.ViewModels.Dojos.DojoDetails</span><span class="kwrd">>></span></pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span>@{</pre><pre><span class="lnum"> 4: </span> View.Title = "List";</pre><pre class="alt"><span class="lnum"> 5: </span> Layout = "~/Views/Shared/_Layout.cshtml";</pre><pre><span class="lnum"> 6: </span>}</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> <span class="kwrd"><</span><span class="html">h2</span><span class="kwrd">></span>All our Schools<span class="kwrd"></</span><span class="html">h2</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> @Html.ActionLink("Create new school", "Create")</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> @Html.DisplayForModel("UL")</pre><pre class="alt"><span class="lnum"> 13: </span> </pre></div><pre> </pre><pre>Note that I just tell it the template name (file minus extension name in ~Views/Shared/DisplayTemplates) and there we go... It all works.</pre><pre> </pre><pre>There's a WHOLE lot I could do to clean this up... and I will, but I really wanted to see if I could get this to work. Lots and lots of goodies in the MVC stack, we really need to use them more!</pre><pre> </pre><pre>If you wanna see it you can <a href="https://github.com/elliottohara/myDojo">Git the code</a>.</pre><pre> </pre><pre>Have fun!</pre><pre> </pre></div></span></span> Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-70718309228162479382010-11-16T13:53:00.000-08:002010-11-16T13:53:57.390-08:00Contextual View - graceful degrading put still wizbangy!Ok, So in my <a href="https://github.com/elliottohara/myDojo">MyDojo</a> project my I've got a really simple view to create a new school. Controller method looks like so.<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> ActionResult Create()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var model = <span class="kwrd">new</span> CreateSchoolForm();</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">return</span> View(model);</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br />
<br />
<br />
Really really simple... I typically start with UIs like this, then our UI engineers at work come and say "Oh, we wanna do this via ajax! Can you just change that to a Json method? Then we end up with 2 methods. <br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> ActionResult CreateAjax()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var model = <span class="kwrd">new</span> CreateSchoolForm();</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">return</span> Json(model,JsonRequestBehavior.AllowGet);</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br />
So on the actual view, they'll actually set the url on a link to the Create action on the controller, but use Jquery to override the onclick on to just make an Ajax get to Create Ajax. <br />
<br />
Something like this...<br />
<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd"><</span><span class="html">script</span><span class="kwrd">></span></pre><pre><span class="lnum"> 2: </span> $(document).ready(<span class="kwrd">function</span> () {</pre><pre class="alt"><span class="lnum"> 3: </span> $(<span class="str">'#makeAjaxRequest'</span>).click(<span class="kwrd">function</span> (ev) {</pre><pre><span class="lnum"> 4: </span> $.get(<span class="kwrd">this</span>.href.replace(<span class="str">'Create'</span>,<span class="str">'CreateAjax'</span>), <span class="kwrd">null</span>, <span class="kwrd">function</span> (data) { alert(data); });</pre><pre class="alt"><span class="lnum"> 5: </span> ev.returnVal = <span class="kwrd">false</span>;</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre><pre class="alt"><span class="lnum"> 7: </span> });</pre><pre><span class="lnum"> 8: </span> });</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd"></</span><span class="html">script</span><span class="kwrd">></span></pre></div><br />
<br />
<br />
I hate having 2 methods that do the exact same thing, and that the url that I gave them won't work (they've gotta replace the url for ajax). So sometimes I'll do this. <br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> ActionResult Create()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var model = <span class="kwrd">new</span> CreateSchoolForm();</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">return</span> _request.IsAjaxRequest() ? (ActionResult)Json(model) : (ActionResult)View(model);</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br />
Ok, whatever... Simple enough, but I'm sick of forking code like that all the darn time. So I just added this to my abstract base controller.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">protected</span> ActionResult ContextualView(<span class="kwrd">object</span> model, <span class="kwrd">string</span> viewName=<span class="kwrd">null</span>)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">if</span>(Request.IsAjaxRequest())</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">return</span> Json(model, JsonRequestBehavior.AllowGet); </pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">return</span> String.IsNullOrEmpty(viewName) ? View(model) : View(viewName, model);</pre><pre><span class="lnum"> 8: </span> }</pre><pre> </pre><pre>Works like a charm!</pre><pre> </pre><pre>Need to get this puppy encapsulated into it's own ActionResult class, that way we could use it with without inheriting the base controller class. Yeah, one day I'll do that. It just won't be today.</pre></div>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-55692910659244466162010-11-12T09:08:00.000-08:002010-11-12T10:06:46.790-08:00But you can't do that! Command Validators in the MVC stack<h2> </h2><h2> </h2>So, yeah, <a href="http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx">DataAnnotations</a> are great. I use them for Required, StringLength, Regex, and such. I use them, and you should too. In a CQRS system, you want to avoid issuing commands that won't succeed at all costs (uhhhh... duhhhhh). I keep hearing Greg and Udi talk about how a shared library that can used that checks rules to check the validity of a command before issuing it. I can't find one, so I'm gunna do it. I'm thinking we just need a simple interface, like so.<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Commands;</pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.CQRS.Validation</pre><pre><span class="lnum"> 4: </span>{</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IValidate<TCommand> <span class="kwrd">where</span> TCommand : ICommand</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">bool</span> IsValid(TCommand command);</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span>}</pre></div><br />
What I'm thinking is that I'll just use my IOC container to go get all these, execute each of them for a command. If any of them fails, we don't issue the command, and by checking what IValidate failed, we know why, and can communicate that to the user.<br />
<br />
I think something like this...<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Commands;</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.CQRS.Validation</pre><pre class="alt"><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> ICommandValidator<TCommand> <span class="kwrd">where</span> TCommand : ICommand</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">bool</span> IsValid<TCommand>(TCommand command);</pre><pre class="alt"><span class="lnum"> 9: </span> IEnumerable<IValidate<TCommand>> FailedRules { get; set; }</pre><pre><span class="lnum"> 10: </span> }</pre><pre class="alt"><span class="lnum"> 11: </span>}</pre></div><br />
<br />
<br />
I guess that looks good... Lets try and make an implementation. Test first.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Commands;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Validation;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> NUnit.Framework;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Rhino.Mocks;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> StructureMap;</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">namespace</span> myDojo.Domain.UnitTests.Infrastructure</pre><pre><span class="lnum"> 8: </span>{</pre><pre class="alt"><span class="lnum"> 9: </span> [TestFixture]</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> when_using_the_command_validator</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> [Test]</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> if_a_validator_fails_is_valid_will_return_false()</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> if_the_command_is_not_valid();</pre><pre class="alt"><span class="lnum"> 17: </span> when_Validate_Command_is_called();</pre><pre><span class="lnum"> 18: </span> _isValid.ShouldBeFalse();</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> if_the_command_is_not_valid()</pre><pre><span class="lnum"> 22: </span> {</pre><pre class="alt"><span class="lnum"> 23: </span> var validatorThatWillReturnFalse = MockRepository.GenerateMock<IValidate<SomeTestCommand>>();</pre><pre><span class="lnum"> 24: </span> validatorThatWillReturnFalse.Stub(v => v.IsValid(_command)).Return(<span class="kwrd">false</span>);</pre><pre class="alt"><span class="lnum"> 25: </span> ObjectFactory.Inject(validatorThatWillReturnFalse);</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> </pre><pre><span class="lnum"> 28: </span> <span class="kwrd">private</span> CommandValidator<SomeTestCommand> _validator;</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">private</span> SomeTestCommand _command;</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">private</span> <span class="kwrd">bool</span> _isValid;</pre><pre class="alt"><span class="lnum"> 31: </span> </pre><pre><span class="lnum"> 32: </span> </pre><pre class="alt"><span class="lnum"> 33: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> when_Validate_Command_is_called()</pre><pre><span class="lnum"> 34: </span> {</pre><pre class="alt"><span class="lnum"> 35: </span> _validator = <span class="kwrd">new</span> CommandValidator<SomeTestCommand>(ObjectFactory.Container);</pre><pre><span class="lnum"> 36: </span> _isValid = _validator.IsValid(_command);</pre><pre class="alt"><span class="lnum"> 37: </span> }</pre><pre><span class="lnum"> 38: </span> [SetUp]</pre><pre class="alt"><span class="lnum"> 39: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> establish_context()</pre><pre><span class="lnum"> 40: </span> {</pre><pre class="alt"><span class="lnum"> 41: </span> ObjectFactory.Initialize(a => { });</pre><pre><span class="lnum"> 42: </span> _command = <span class="kwrd">new</span> SomeTestCommand();</pre><pre class="alt"><span class="lnum"> 43: </span> </pre><pre><span class="lnum"> 44: </span> }</pre><pre class="alt"><span class="lnum"> 45: </span> }</pre><pre><span class="lnum"> 46: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> SomeTestCommand : ICommand</pre><pre class="alt"><span class="lnum"> 47: </span> {</pre><pre><span class="lnum"> 48: </span> </pre><pre class="alt"><span class="lnum"> 49: </span> }</pre><pre><span class="lnum"> 50: </span>}</pre></div><br />
<div class="csharpcode"><pre>Ok, so I'm just mocking up a validator that will fail, and making sure IsValid returns false. Here's what I did to make it pass.</pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System.Linq;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Commands;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> StructureMap;</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.CQRS.Validation</pre><pre><span class="lnum"> 8: </span>{</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> CommandValidator<TCommand> : ICommandValidator<TCommand> <span class="kwrd">where</span> TCommand : ICommand</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IContainer _container;</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> CommandValidator(IContainer container)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> _container = container;</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> <span class="kwrd">public</span> <span class="kwrd">bool</span> IsValid(TCommand command)</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">foreach</span>(var validator <span class="kwrd">in</span> _container.GetAllInstances<IValidate<TCommand>>())</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">if</span> (!validator.IsValid(command))</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> </pre><pre><span class="lnum"> 28: </span> <span class="kwrd">public</span> IEnumerable<IValidate<TCommand>> FailedRules</pre><pre class="alt"><span class="lnum"> 29: </span> {</pre><pre><span class="lnum"> 30: </span> get { <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException(); }</pre><pre class="alt"><span class="lnum"> 31: </span> set { <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException(); }</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre><pre><span class="lnum"> 34: </span>}</pre></div><pre>So, that one is good. Lets go add a test to make sure the positive is good (when there's only valid commands). Here she is.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>[Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> if_the_validator_passes_is_valid_will_return_false()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> if_the_command_is_valid();</pre><pre class="alt"><span class="lnum"> 5: </span> when_Validate_Command_is_called();</pre><pre><span class="lnum"> 6: </span> _isValid.ShouldBeTrue();</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> if_the_command_is_valid()</pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> var validatorThatWillReturnFalse = MockRepository.GenerateMock<IValidate<SomeTestCommand>>();</pre><pre class="alt"><span class="lnum"> 11: </span> validatorThatWillReturnFalse.Stub(v => v.IsValid(_command)).Return(<span class="kwrd">true</span>);</pre><pre><span class="lnum"> 12: </span> ObjectFactory.Inject(validatorThatWillReturnFalse);</pre><pre class="alt"><span class="lnum"> 13: </span> }</pre></div></div><br />
<br />
<br />
Next up, lets get the failed command.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> failed_rules_will_contain_the_failed_validator()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> if_the_command_is_not_valid();</pre><pre class="alt"><span class="lnum"> 5: </span> when_Validate_Command_is_called();</pre><pre><span class="lnum"> 6: </span> _validator.FailedRules.ShouldContain(ObjectFactory.GetInstance<IValidate<SomeTestCommand>>());</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
<br />
<br />
We just go inject a failing command, then go make sure it gets added to FaildRules. Fails now (because we didn't write the code)... Lets go fix it.<br />
<br />
Couple changes here, but pretty simple.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System.Linq;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Commands;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> StructureMap;</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.CQRS.Validation</pre><pre><span class="lnum"> 8: </span>{</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> CommandValidator<TCommand> : ICommandValidator<TCommand> <span class="kwrd">where</span> TCommand : ICommand</pre><pre><span class="lnum"> 10: </span> {</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IContainer _container;</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> List<IValidate<TCommand>> _failedRules;</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> CommandValidator(IContainer container)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> _container = container;</pre><pre class="alt"><span class="lnum"> 17: </span> _failedRules = <span class="kwrd">new</span> List<IValidate<TCommand>>();</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> <span class="kwrd">public</span> <span class="kwrd">bool</span> IsValid(TCommand command)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">foreach</span>(var validator <span class="kwrd">in</span> _container.GetAllInstances<IValidate<TCommand>>())</pre><pre class="alt"><span class="lnum"> 23: </span> {</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">if</span> (!validator.IsValid(command))</pre><pre class="alt"><span class="lnum"> 25: </span> _failedRules.Add(validator);</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">return</span> _failedRules.Count() == 0;</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span> </pre><pre><span class="lnum"> 30: </span> <span class="kwrd">public</span> IEnumerable<IValidate<TCommand>> FailedRules { get { <span class="kwrd">return</span> _failedRules; } }</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span>}</pre><pre> </pre><pre>Instead of just short-circuiting the check for failed validators in the loop, now I just add each failed one to the collection, and then just return that true if the collection count is 0. Oh yeah, I removed the setter on FailedRules from the interface, since we won't need that.</pre><pre> </pre><pre>And she passes!</pre><pre> </pre><pre>Ok, let's go use it now. What I'm thinking is that (for now), I'll just plug it into my <a href="http://blog.elliottohara.com/2010/11/ddd-cqrs-object-databases-mvc3-razor.html">CommandActionResult</a>. I probably could get prettier, but lets just make it work first.</pre><pre> </pre><pre>Here's what CommandActionResult.ExecuteResult looks like now.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (!context.Controller.ViewData.ModelState.IsValid)</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> ValidationFailedResult().ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> ICommandHandlerResult result = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> var handler = Container.GetInstance<ICommandHandler<TCommand>>();</pre><pre><span class="lnum"> 12: </span> result = handler.Handle(Command);</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">if</span>(result <span class="kwrd">is</span> Success)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> Success().ExecuteResult(context);</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> CommandFailedResult(result).ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre></div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode">So, before we even get the handler (line 11), lets just go get the CommandValidator and check it first.</div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode">Here's what I got now.</div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (!context.Controller.ViewData.ModelState.IsValid)</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> ValidationFailedResult().ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> var validator = Container.GetInstance<ICommandValidator<TCommand>>();</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">if</span> (!validator.IsValid(Command))</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> ValidationFailedResult().ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> ICommandHandlerResult result = <span class="kwrd">null</span>;</pre><pre class="alt"><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> var handler = Container.GetInstance<ICommandHandler<TCommand>>();</pre><pre class="alt"><span class="lnum"> 21: </span> result = handler.Handle(Command);</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">if</span>(result <span class="kwrd">is</span> Success)</pre><pre class="alt"><span class="lnum"> 23: </span> {</pre><pre><span class="lnum"> 24: </span> Success().ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> CommandFailedResult(result).ExecuteResult(context);</pre><pre><span class="lnum"> 28: </span> }</pre><pre> </pre><pre>Kinda ugly... We've got 2 places where we're executing that ValidationFailedResult. That kinda smells, but I'd hate to go get the validator if I don't need it right? Screw it... I'm gunna go try this puppy out, then try to clean it up. </pre><pre> </pre><pre>Lets go make a validator to make sure we don't create dupe email addresses. So, I'm actually making a new assembly for my validators because validation will use both Commands and Queries - and I don't want either of those having a ref to each other. </pre><pre> </pre><pre>Here's my validator.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> myDojo.Infrastructure;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Validation;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> MyDojo.Query.ViewModels;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> myDojo.Commands.Users.Validation</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> EmailAddressMustBeUnique : IValidate<RegisterUser></pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IReadModelRepository<MartialArtistDetails> _readModelRepository;</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> EmailAddressMustBeUnique(IReadModelRepository<MartialArtistDetails> readModelRepository)</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> _readModelRepository = readModelRepository;</pre><pre><span class="lnum"> 14: </span> }</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">bool</span> IsValid(RegisterUser command)</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">return</span> _readModelRepository.GetSingle(m => m.EmailAddress == command.EmailAddress) == <span class="kwrd">null</span>;</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span>}</pre></div><pre> </pre><pre> </pre><pre>Give it a shot..</pre><pre> </pre><br />
<h3>Server Error in '/' Application. <br />
<hr size="1" width="100%" /><br />
</h3><br />
<h4><i>StructureMap Exception Code: 202<br />
No Default Instance defined for PluginFamily myDojo.Infrastructure.CQRS.Validation.ICommandValidator`1[[myDojo.Commands.Users.RegisterUser, myDojo.Commands, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], myDojo.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</i></h4><pre> </pre><pre>I actually knew that was gunna happen, but I wanted my blog to be a bit longer so I could show off about structure map. Gotta go tell SM to add all those validators. Simple enough....</pre><pre> </pre><pre>Here's my quick integration test for my registry.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> get_a_EmailIsUnique_validator()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> _container.GetAllInstances<IValidate<RegisterUser>>()</pre><pre class="alt"><span class="lnum"> 5: </span> .FirstOrDefault(v => v <span class="kwrd">is</span> EmailAddressMustBeUnique).ShouldNotBeNull();</pre><pre><span class="lnum"> 6: </span> }</pre><pre> </pre><pre>Registry to make it pass looks like so.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> </pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Validation;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> StructureMap.Configuration.DSL;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span><span class="kwrd">namespace</span> myDojo.Commands.Users.Validation</pre><pre class="alt"><span class="lnum"> 7: </span>{</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> CommandValidationRegistry : Registry </pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> CommandValidationRegistry()</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> Scan(s =></pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> s.AssemblyContainingType(GetType());</pre><pre class="alt"><span class="lnum"> 15: </span> s.ConnectImplementationsToTypesClosing(<span class="kwrd">typeof</span> (IValidate<>));</pre><pre><span class="lnum"> 16: </span> s.ConnectImplementationsToTypesClosing(<span class="kwrd">typeof</span> (ICommandValidator<>));</pre><pre class="alt"><span class="lnum"> 17: </span> });</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span>}</pre></div></div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode">Lets try the web again (that's what I get for not TDDing, huh?)...</div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode">Same freeking thing?! Oh yeah, that's because I've been working all day and my brain is fried. I forgot to add ref to the new assembly to my web project. Ok... add it, and...</div><br />
<div class="csharpcode">What the??! Same thing... Time to go look at that test again. I'm missing something</div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode">Duh! I only tested that we could get a IValidate<RegisterUser>, not an ICommandValidator. New test looks like so.</div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> [Test]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> get_a_RegisterUser_CommandValidator()</pre><pre class="alt"><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> _container.GetInstance<ICommandValidator<RegisterUser>>().ShouldNotBeNull();</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode">Yeah, there, got the error. Now lets go fix it. Something to do with open generics. I think this'll fix it.</div><br />
<div class="csharpcode"></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> </pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 2: </span> </pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Validation;</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> StructureMap.Configuration.DSL;</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 5: </span> </pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 6: </span><span class="kwrd">namespace</span> myDojo.Commands.Users.Validation</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 7: </span>{</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> CommandValidationRegistry : Registry </pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 9: </span> {</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> CommandValidationRegistry()</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 11: </span> {</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 12: </span> Scan(s =></pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 13: </span> {</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 14: </span> s.AssemblyContainingType(GetType());</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 15: </span> s.ConnectImplementationsToTypesClosing(<span class="kwrd">typeof</span> (IValidate<>));</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 16: </span> s.ConnectImplementationsToTypesClosing(<span class="kwrd">typeof</span> (ICommandValidator<>));</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 17: </span> </pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 18: </span> });</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 19: </span> For(<span class="kwrd">typeof</span> (ICommandValidator<>)).Use(<span class="kwrd">typeof</span>(CommandValidator<>));</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 20: </span> }</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 21: </span> }</pre></div><br />
<div class="csharpcode"><pre><span class="lnum"> 22: </span>}</pre></div><br />
<div class="csharpcode"><pre> </pre></div><br />
<div class="csharpcode"><pre>Finally! Actually we don't need line 16. Why? You know, I'm gunna email the SM user group and find out, cause I ain't quite sure. Remove it and re-run test... Yep, I'm good.
Ok, lets hit website again.</pre></div><br />
<div class="csharpcode"><pre> </pre></div><br />
<div class="csharpcode"><pre>And we're good!</pre></div><br />
<div class="csharpcode"><pre> </pre></div><br />
<div class="csharpcode"><pre>We're not yet communicating to the user why the hell the command failed though. Lets see what we can do there.</pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span> <span class="kwrd">if</span> (!context.Controller.ViewData.ModelState.IsValid)</pre><pre class="alt"><span class="lnum"> 5: </span> {</pre><pre><span class="lnum"> 6: </span> ValidationFailedResult().ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">return</span>;</pre><pre><span class="lnum"> 8: </span> }</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> var validator = Container.GetInstance<ICommandValidator<TCommand>>();</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">if</span> (!validator.IsValid(Command))</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">foreach</span> (var failedRule <span class="kwrd">in</span> validator.FailedRules)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> context.Controller.ViewData.ModelState.AddModelError(failedRule.GetType().Name,failedRule.ToString());</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> ValidationFailedResult().ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> </pre><pre class="alt"><span class="lnum"> 23: </span> ICommandHandlerResult result = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 24: </span> </pre><pre class="alt"><span class="lnum"> 25: </span> var handler = Container.GetInstance<ICommandHandler<TCommand>>();</pre><pre><span class="lnum"> 26: </span> result = handler.Handle(Command);</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">if</span>(result <span class="kwrd">is</span> Success)</pre><pre><span class="lnum"> 28: </span> {</pre><pre class="alt"><span class="lnum"> 29: </span> Success().ExecuteResult(context);</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">return</span>;</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span> CommandFailedResult(result).ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre></div><br />
<div class="csharpcode"><pre> </pre></div><br />
<div class="csharpcode"><pre>Now we just override ToString() on the validation message, and they get added to the Model Errors. Here's what my view looks like - oh yeah, it's Razor.
</pre></div><br />
<div class="csharpcode"><pre> </pre></div><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span>@inherits System.Web.Mvc.WebViewPage<span class="kwrd"><</span><span class="html">myDojo.Web.Models.RegisterUserForm</span><span class="kwrd">></span></pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span>@{</pre><pre><span class="lnum"> 4: </span> View.Title = "Register";</pre><pre class="alt"><span class="lnum"> 5: </span> LayoutPage = "~/Views/Shared/_Layout.cshtml";</pre><pre><span class="lnum"> 6: </span>}</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> <span class="kwrd"><</span><span class="html">h2</span><span class="kwrd">></span>Create your account<span class="kwrd"></</span><span class="html">h2</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd"><</span><span class="html">form</span> <span class="attr">action</span><span class="kwrd">='@Url.Action("Register","User")'</span> <span class="attr">method</span><span class="kwrd">='POST'</span><span class="kwrd">></span></pre><pre><span class="lnum"> 10: </span> @Html.ValidationSummary()</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd"><</span><span class="html">div</span><span class="kwrd">></span></pre><pre><span class="lnum"> 12: </span> @Html.LabelFor(m =<span class="kwrd">></span> m.EmailAddress)</pre><pre class="alt"><span class="lnum"> 13: </span> @Html.TextBoxFor(m =<span class="kwrd">></span> m.EmailAddress)</pre><pre><span class="lnum"> 14: </span> @Html.ValidationMessageFor(m =<span class="kwrd">></span> m.EmailAddress)</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span></pre><pre><span class="lnum"> 16: </span> <span class="kwrd"><</span><span class="html">div</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd"><</span><span class="html">input</span> <span class="attr">type</span><span class="kwrd">="submit"</span> <span class="attr">value</span><span class="kwrd">="ok"</span> <span class="kwrd">/></span></pre><pre><span class="lnum"> 18: </span> <span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span></pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd"></</span><span class="html">form</span><span class="kwrd">></span></pre></div><br />
<div class="csharpcode"><pre> </pre></div><br />
<div class="csharpcode"><pre>And when I try to register with a pre-existing email address I see this.</pre></div><br />
<div class="csharpcode"><pre> </pre><pre><a href="http://lh4.ggpht.com/_hl5c3g00lJk/TNy8_BlbABI/AAAAAAAAANs/G1dn3J_3_Z0/s1600-h/blog%5B14%5D.jpg"><img alt="blog" border="0" height="213" src="http://lh3.ggpht.com/_hl5c3g00lJk/TNy8_RzhEgI/AAAAAAAAANw/u3OgBhpWppo/blog_thumb%5B8%5D.jpg?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px;" width="570" /></a></pre><pre> </pre><pre>I think we can clean up that CommandActionResult a lot, and I probably will - just not tonight.</pre><pre> </pre><pre>I like having my command validation in it's own assembly. This gives me some DRY around my validation, not sure how much I'll reuse it yet, but we'll find out, huh?</pre><pre> </pre><pre>Have fun with it, tell me why it sucks, or why it doesn't.</pre></div><br />
<div class="csharpcode"><br />
<br />
</div><div class="csharpcode"><pre> </pre></div></div></div>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com5tag:blogger.com,1999:blog-682843596462139829.post-87402289257163588162010-11-09T12:58:00.000-08:002010-11-12T10:07:27.927-08:00When just a query don't work - MappedQueryActionResultSo in my <a href="http://blog.elliottohara.com/2010/11/mvc-queryactionresult.html">last post</a> I made a Query action result. This works great for list and details screens. What about edit screens though? If you're posting back your actual entity as your viewmodel this works fine. However, I've gotten to where I make nice little small view models that represent just the form I'm wanting to post. Like this method<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> ActionResult Edit(<span class="kwrd">string</span> email)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var readModel = _detailsReadModelRepository.GetSingle(d=>d.EmailAddress == email);</pre><pre><span class="lnum"> 4: </span> var viewModel = <span class="kwrd">new</span> EditMartialArtistForm(readModel);</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">return</span> View(viewModel);</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
Nothing wrong with this method. Can't be - because I wrote it. *snicker. But, I think I can make it better. So, since I'm not big on solving my own problems, I went and looked at some of <a href="http://www.lostechies.com/members/bogardj/default.aspx">Jimmy Bogard's</a> stuff. Jimmy wrote <a href="http://automapper.codeplex.com/">AutoMapper</a>, and is an all round genius. In fact, the only reason you should be reading this blog is because you've read all his blogs and you're not geek full yet. I pulled down the code from his <a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2010/07/23/mvcconf-slides-and-code-posted.aspx">mvcConf</a> at <a href="http://headspringlabs.codeplex.com/">http://headspringlabs.codeplex.com/</a> and found this little gem. <br />
<br />
<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> AutoMapper;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> HeadspringExample.UI.Helpers</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> AutoMapViewResult : ActionResult</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> Type SourceType { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> Type DestinationType { get; <span class="kwrd">private</span> set; }</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> ViewResult View { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> AutoMapViewResult(Type sourceType, Type destinationType, ViewResult view)</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> SourceType = sourceType;</pre><pre><span class="lnum"> 16: </span> DestinationType = destinationType;</pre><pre class="alt"><span class="lnum"> 17: </span> View = view;</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre class="alt"><span class="lnum"> 21: </span> {</pre><pre><span class="lnum"> 22: </span> var model = Mapper.Map(View.ViewData.Model, SourceType, DestinationType);</pre><pre class="alt"><span class="lnum"> 23: </span> </pre><pre><span class="lnum"> 24: </span> View.ViewData.Model = model;</pre><pre class="alt"><span class="lnum"> 25: </span> </pre><pre><span class="lnum"> 26: </span> View.ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span>}</pre></div><br />
If you don't know what <a href="http://automapper.codeplex.com/">AutoMapper</a> is, well, that's not the point of this blog, but basically, it maps an instance of one type to an instance of another based on conventions. Saves LOTS and lots of boilerplate code that we used to hire interns to write before Bush drove the Economy in a ditch and Obama backed a tow truck over it trying to fix it. Now we just go steal Jimmy's stuff and it gets done for free and interns work at McDonalds and McDonalds workers go beg on the street corner and panhandlers are just SOL. What the heck was that all about??? God I love Dos Equis! <br />
<br />
Back on track. Yeah, it's a good beer!<br />
<br />
Ok, so I wanna send a Query off to an AutoMapper then send that off to a view. Want my consumers to look something like this. <br />
<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> ActionResult Edit(<span class="kwrd">string</span> email)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> MappedQueryView<GetMartialArtistDetailsByEmail,EditMartialArtistForm>(s =>s.EmailAddress = email);</pre><pre><span class="lnum"> 4: </span> }</pre></div><br />
Uhh... I don't know if that's even possible, since IQuery.Execute is object... But hey, lets see what we can come up with. 25 minutes (and 2 more Dos Equis) later... Duhhh... Look at Jimmy's AutoMapViewResult. He made overloads for IMappingEngine.Map that take source, source type and destination type (not the generics).. We're in business! So I wrote this.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> AutoMapper;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Query;</pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> StructureMap;</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.Web</pre><pre class="alt"><span class="lnum"> 9: </span>{</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MappedQueryViewResult<TQuery,TDestination> : ViewResult <span class="kwrd">where</span> TQuery:IQuery</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> <span class="kwrd">static</span> IMappingEngine Mapper { get { <span class="kwrd">return</span> Container.GetInstance<IMappingEngine>(); } }</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">private</span> <span class="kwrd">static</span> IContainer Container { get { <span class="kwrd">return</span> ServiceLocation.CurrentContainer; } }</pre><pre><span class="lnum"> 14: </span> </pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">public</span> MappedQueryViewResult(Action<TQuery> buildUpQuery)</pre><pre><span class="lnum"> 16: </span> {</pre><pre class="alt"><span class="lnum"> 17: </span> BuildUpQuery = buildUpQuery;</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> <span class="kwrd">protected</span> Action<TQuery> BuildUpQuery { get; set; }</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre class="alt"><span class="lnum"> 23: </span> {</pre><pre><span class="lnum"> 24: </span> var query = Container.GetInstance<TQuery>();</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">if</span> (BuildUpQuery != <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 26: </span> BuildUpQuery(query);</pre><pre class="alt"><span class="lnum"> 27: </span> var source = query.Execute();</pre><pre><span class="lnum"> 28: </span> var viewModel = Mapper.Map(source,source.GetType(), <span class="kwrd">typeof</span> (TDestination));</pre><pre class="alt"><span class="lnum"> 29: </span> ViewData.Model = viewModel;</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">base</span>.ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span> </pre><pre class="alt"><span class="lnum"> 33: </span> }</pre><pre><span class="lnum"> 34: </span>}</pre></div><br />
<br />
Not much to it really. I just use go get the Query from StructureMap, build it up (exactly the same way QueryActionResult does) but instead of setting the Model to the query results, I pump it through the IMappingEngine. Add a nice little Convenience method to our base controller.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> ActionResult MappedQuery<TQuery,TDestination>(Action<TQuery> buildUpQuery=<span class="kwrd">null</span>) <span class="kwrd">where</span> TQuery:IQuery</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MappedQueryViewResult<TQuery, TDestination>(buildUpQuery);</pre><pre><span class="lnum"> 4: </span> }</pre><pre> </pre><pre>And there we go! The method now looks like this.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> ActionResult Edit(<span class="kwrd">string</span> email)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> MappedQuery<GetMartialArtistDetailsByEmail,EditMartialArtistForm>(s =>s.EmailAddress = email);</pre><pre><span class="lnum"> 4: </span> }</pre><pre>A nice little side effect to not depending on the repositories directly is that we no longer have ANY direct dependencies in the controller now. Default ctor is fine. All the service location stuff happens in the ActionResult. Personally, I think this is a good thing. I can test my Queries in isolation and have the infrastructure wrapped up well and we know it works.
</pre><pre>Here's what the controller looks like now.
</pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> myDojo.Commands.Users;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> myDojo.Infrastructure.Web;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> MyDojo.Query.Queries;</pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> myDojo.Web.Models;</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span><span class="kwrd">namespace</span> myDojo.Web.Controllers</pre><pre class="alt"><span class="lnum"> 9: </span>{</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> UserController : DefaultController</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> ActionResult Register()</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">return</span> View(<span class="kwrd">new</span> RegisterUserForm());</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> [HttpPost]</pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">public</span> CommandActionResult<RegisterUser> Register(RegisterUserForm form)</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">return</span> Command(<span class="kwrd">new</span> RegisterUser(form.EmailAddress, <span class="kwrd">null</span>), () => RedirectToAction(<span class="str">"Edit"</span>, <span class="kwrd">new</span> {email = form.EmailAddress}));</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">public</span> ActionResult Edit(<span class="kwrd">string</span> email)</pre><pre><span class="lnum"> 22: </span> {</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">return</span> MappedQuery<GetMartialArtistDetailsByEmail,EditMartialArtistForm>(s =>s.EmailAddress = email);</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">public</span> ActionResult Details(Guid id)</pre><pre><span class="lnum"> 26: </span> {</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">return</span> Query<MartialArtistDetailsById>(q => q.Id = id);</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span> [HttpPost]</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">public</span> CommandActionResult<EditMartialArtistInfo> Edit(EditMartialArtistForm model)</pre><pre class="alt"><span class="lnum"> 31: </span> {</pre><pre><span class="lnum"> 32: </span> <span class="kwrd">return</span> Command(<span class="kwrd">new</span> EditMartialArtistInfo(model.Id, model.Name, model.Biography), () => RedirectToAction(<span class="str">"List"</span>));</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre><pre><span class="lnum"> 34: </span> <span class="kwrd">public</span> ActionResult List()</pre><pre class="alt"><span class="lnum"> 35: </span> {</pre><pre><span class="lnum"> 36: </span> <span class="kwrd">return</span> Query<AllMatialArtists>();</pre><pre class="alt"><span class="lnum"> 37: </span> }</pre><pre><span class="lnum"> 38: </span> </pre><pre class="alt"><span class="lnum"> 39: </span> }</pre><pre><span class="lnum"> 40: </span> </pre><pre class="alt"><span class="lnum"> 41: </span> </pre><pre><span class="lnum"> 42: </span>}</pre></div><pre> </pre></div></div>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com0tag:blogger.com,1999:blog-682843596462139829.post-5434116787966554582010-11-08T16:31:00.001-08:002010-11-12T10:07:56.329-08:00The Journey to thinner controllers - MVC QueryActionResultIn the endless drive to smaller and smaller controllers, I'm gunna see if I can't make an ActionResult for query results. Basically, while this code doesn't suck at all.<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> ActionResult Details(Guid id)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var details = _detailsReadModelRepository.GetById(id);</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">return</span> View(details);</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br />
<br />
<br />
I think we can make it even better. I really wanna call out that a method is a Query. So I'm gunna make a QueryActionResult. So I just create the class and inherit ActionResult. It has one abstract on it, ExecuteResults(ControllerContext). Lets start really simple first. I'm thinking we just take in a Func<Object> that will execute the query to get the viewmodel, then send that off to the the rendering engine.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.Web</pre><pre class="alt"><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> QueryActionResult : ViewResult</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> Func<<span class="kwrd">object</span>> Query { get; set; }</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> QueryMustBeSet();</pre><pre class="alt"><span class="lnum"> 13: </span> ViewData.Model = Query();</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">base</span>.ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> </pre><pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> QueryMustBeSet()</pre><pre><span class="lnum"> 18: </span> {</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">if</span>(Query == <span class="kwrd">null</span>)</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentNullException(<span class="str">"Query"</span>);</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span>}</pre></div><br />
Ok, simple enough, but we really didn't accomplish much. The consumer code now looks like this.<br />
<br />
<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> ActionResult Details(Guid id)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> QueryActionResult</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> Query = () => _detailsReadModelRepository.GetById(id)</pre><pre><span class="lnum"> 6: </span> };</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
Yeah, that's probably worse than when we started. To be fair though, the only reason View(object) is convenient is because of the convenience method on Controller that sets the ViewData.Model. So, since I'm all about being fair, I'll go add a convenience method to our base controller.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> QueryActionResult Query(Func<<span class="kwrd">object</span>> query)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> QueryActionResult</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> Query = query</pre><pre><span class="lnum"> 6: </span> };</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
Now the consumer looks like this.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> QueryActionResult Details(Guid id)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> Query(() => _detailsReadModelRepository.GetById(id));</pre><pre><span class="lnum"> 4: </span> }</pre></div><br />
I like that. Pretty nice huh? Of course we can add overloads to the Query method that'll let you send to different Views (just like View() has on Controller). <br />
<br />
But I think I can make it a little better. I think I want it to look like this.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> ActionResult Details(Guid id)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> Query(<span class="kwrd">new</span> GetDetailsForMartialArtist(id));</pre><pre><span class="lnum"> 4: </span> }</pre></div><br />
To do that, I'm gunna create a little Query interface. Essentially, I'll just be using the <a href="http://en.wikipedia.org/wiki/Command_pattern#C.23">command pattern</a>. Here's the interface.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.CQRS.Query</pre><pre><span class="lnum"> 2: </span>{</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IQuery</pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> object Execute();</pre><pre><span class="lnum"> 6: </span> }</pre><pre class="alt"><span class="lnum"> 7: </span>}</pre></div><br />
Next, I just go make a Query that gets details by id. Uh oh! Wait... dependencies. I have to specify the params for the query somewhere, and I don't want to constructor inject them, because that just smells (even though that's what I just wrote - that happens to me a lot.). I'd rather just write the query like this.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> </pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> myDojo.Infrastructure;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Query;</pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> MyDojo.Query.ViewModels;</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span><span class="kwrd">namespace</span> MyDojo.Query.Queries</pre><pre class="alt"><span class="lnum"> 9: </span>{</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MartialArtistDetailsById : IQuery</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IReadModelRepository<MartialArtistDetails> _repository;</pre><pre class="alt"><span class="lnum"> 13: </span> </pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> MartialArtistDetailsById(IReadModelRepository<MartialArtistDetails> repository)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> _repository = repository;</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> </pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">public</span> Guid Id { get; set; }</pre><pre><span class="lnum"> 20: </span> </pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">public</span> MartialArtistDetails Execute()</pre><pre><span class="lnum"> 22: </span> {</pre><pre class="alt"><span class="lnum"> 23: </span> IdMustBeSet();</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">return</span> _repository.GetById(Id);</pre><pre class="alt"><span class="lnum"> 25: </span> }</pre><pre><span class="lnum"> 26: </span> </pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">private</span> <span class="kwrd">void</span> IdMustBeSet()</pre><pre><span class="lnum"> 28: </span> {</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">if</span>(Id.Equals(Guid.Empty))</pre><pre><span class="lnum"> 30: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(<span class="str">"Id"</span>,<span class="str">"Id must be set"</span>);</pre><pre class="alt"><span class="lnum"> 31: </span> }</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span>}</pre></div><br />
This looks pretty good, but how to make it work??? I really don't want to have to new that Query object up, because that means I'll have to DI every dependency for every query on a controller. That just seems counterproductive to me. Let's see what we can do.<br />
<br />
Instead of injecting Func<object>, we'll take an Action<TQuery> that we'll use to build up the query properties so that the params for the queries are properly set. Like so.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="alnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Query;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.Web</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> QueryActionResult<TQuery> : ViewResult <span class="kwrd">where</span> TQuery:IQuery</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> Action<TQuery> BuildUpQuery { get; set; }</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> var query = ServiceLocation.CurrentContainer.GetInstance<TQuery>();</pre><pre class="alt"><span class="lnum"> 13: </span> BuildUpQuery(query);</pre><pre><span class="lnum"> 14: </span> ViewData.Model = query.Execute();</pre><pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">base</span>.ExecuteResult(context);</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span>}</pre></div><br />
So we change the convenience method to look like this.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> QueryActionResult<TQuery> Query<TQuery>(Action<TQuery> buildUpQuery) <span class="kwrd">where</span> TQuery: IQuery</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> QueryActionResult<TQuery></pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> BuildUpQuery = buildUpQuery,</pre><pre><span class="lnum"> 6: </span> };</pre><pre class="alt"><span class="lnum"> 7: </span> }</pre></div><br />
Now the controller looks like this.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">public</span> QueryActionResult<MartialArtistDetailsById> Details(Guid id)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> Query<MartialArtistDetailsById>(q => q.Id = id);</pre><pre><span class="lnum"> 4: </span> }</pre><pre> </pre><pre>Ok... let's use some optional parameters to make this more usable.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> QueryActionResult<TQuery> Query<TQuery>(Action<TQuery> buildUpQuery=<span class="kwrd">null</span>,<span class="kwrd">string</span> viewName=<span class="kwrd">null</span>) <span class="kwrd">where</span> TQuery: IQuery</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var result = <span class="kwrd">new</span> QueryActionResult<TQuery></pre><pre><span class="lnum"> 4: </span> {</pre><pre class="alt"><span class="lnum"> 5: </span> BuildUpQuery = buildUpQuery,</pre><pre><span class="lnum"> 6: </span> </pre><pre class="alt"><span class="lnum"> 7: </span> };</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">if</span> (!String.IsNullOrEmpty(viewName))</pre><pre class="alt"><span class="lnum"> 9: </span> result.ViewName = viewName;</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">return</span> result;</pre><pre class="alt"><span class="lnum"> 11: </span> }</pre></div><pre> </pre><pre>Don't want a null ref for BuildUpQuery in QueryActionResult, so let's just not execute it if it's null.</pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> myDojo.Infrastructure.CQRS.Query;</pre><pre><span class="lnum"> 4: </span> </pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.Web</pre><pre><span class="lnum"> 6: </span>{</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> QueryActionResult<TQuery> : ViewResult <span class="kwrd">where</span> TQuery:IQuery</pre><pre><span class="lnum"> 8: </span> {</pre><pre class="alt"><span class="lnum"> 9: </span> <span class="kwrd">public</span> Action<TQuery> BuildUpQuery { get; set; }</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ExecuteResult(ControllerContext context)</pre><pre class="alt"><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> var query = ServiceLocation.CurrentContainer.GetInstance<TQuery>();</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">if</span>(BuildUpQuery!=<span class="kwrd">null</span>)</pre><pre><span class="lnum"> 14: </span> BuildUpQuery(query);</pre><pre class="alt"><span class="lnum"> 15: </span> ViewData.Model = query.Execute();</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">base</span>.ExecuteResult(context);</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> }</pre><pre class="alt"><span class="lnum"> 19: </span>}</pre></div></div><br />
Let's go fix our List() method on the UserController now.<br />
<br />
Before:<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> ActionResult List()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> var users = _detailsReadModelRepository.GetAll().AsEnumerable();</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">return</span> View(users);</pre><pre class="alt"><span class="lnum"> 5: </span> }</pre></div><br />
After:<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">public</span> ActionResult List()</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">return</span> Query<AllMatialArtists>();</pre><pre><span class="lnum"> 4: </span> }</pre></div><br />
I like it. Do you?<br />
<br />
Code is up on github.Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com2tag:blogger.com,1999:blog-682843596462139829.post-5015902042003787072010-11-07T11:00:00.001-08:002010-11-12T10:12:50.772-08:00My simple embedded Db4o repositories.So, switching gears.... <br />
I hate plumbing code. I hate writing code to translate something from what it really is to something it really isn't. To be fair a "MartialArtist" isn't some c# object. But still, it's a closer representation than some row in a database table isn't it?<br />
I've been using Db4o for quite a while, and I'm a big fan. While NHibernate totally rocks (really, LOVE it), there is something totally liberating about NOT worrying about making everything virtual just so your ORM can proxy it, or thinking about how Nhibernate is gunna handle persistence. I <br />
<br />
The simplicity of Db4o awesome. Instead of going into how it works and all that jazz, I'm gunna show you how I use it. Since this post is a continuation of my DDD / CQRS series, I'm not gunna explain why I use the repository pattern. I'll just show you my 2 interfaces for them.<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">namespace</span> myDojo.Infrastructure</pre><pre><span class="lnum"> 4: </span>{</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IAggrigateRootRepository<T> <span class="kwrd">where</span> T : IObjectWithIdentity</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> <span class="kwrd">void</span> Store(T entity);</pre><pre><span class="lnum"> 8: </span> T GetById(Guid id);</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span>}</pre></div><br />
<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span> </pre><pre><span class="lnum"> 4: </span><span class="kwrd">namespace</span> myDojo.Infrastructure</pre><pre class="alt"><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IReadModelRepository<T> <span class="kwrd">where</span> T : IObjectWithIdentity,<span class="kwrd">new</span>()</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">void</span> Store(T entity);</pre><pre class="alt"><span class="lnum"> 9: </span> IEnumerable<T> Get(Predicate<T> query);</pre><pre><span class="lnum"> 10: </span> T GetSingle(Predicate<T> query);</pre><pre class="alt"><span class="lnum"> 11: </span> T GetById(Guid id);</pre><pre><span class="lnum"> 12: </span> IEnumerable<T> GetAll();</pre><pre class="alt"><span class="lnum"> 13: </span> }</pre><pre><span class="lnum"> 14: </span>}</pre><pre> </pre><pre>IObjectWithIdentity is just a interface (and I made an abstract for it) that encapsulates an object that has a unique identity - an actual entity (duh). Here it is (and the abstract class for it).</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">namespace</span> myDojo.Infrastructure</pre><pre><span class="lnum"> 4: </span>{</pre><pre class="alt"><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">interface</span> IObjectWithIdentity</pre><pre><span class="lnum"> 6: </span> {</pre><pre class="alt"><span class="lnum"> 7: </span> Guid Id { get; set; }</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">int</span> Version { get; set; }</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> ObjectWithIdentity : IObjectWithIdentity</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">protected</span> ObjectWithIdentity()</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> Id = ANew.Comb();</pre><pre><span class="lnum"> 16: </span> }</pre><pre class="alt"><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> <span class="kwrd">protected</span> ObjectWithIdentity(Guid id)</pre><pre class="alt"><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> Id = id;</pre><pre class="alt"><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> Guid Id { get; set; }</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">int</span> Version { get; set; }</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> Equals(<span class="kwrd">object</span> obj)</pre><pre class="alt"><span class="lnum"> 25: </span> {</pre><pre><span class="lnum"> 26: </span> <span class="kwrd">if</span> (ReferenceEquals(<span class="kwrd">null</span>, obj)) <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">if</span> (ReferenceEquals(<span class="kwrd">this</span>, obj)) <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre><pre><span class="lnum"> 28: </span> <span class="kwrd">if</span> (!(obj <span class="kwrd">is</span> ObjectWithIdentity)) <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">return</span> Equals((ObjectWithIdentity) obj);</pre><pre><span class="lnum"> 30: </span> }</pre><pre class="alt"><span class="lnum"> 31: </span> </pre><pre><span class="lnum"> 32: </span> <span class="kwrd">public</span> <span class="kwrd">bool</span> Equals(ObjectWithIdentity other)</pre><pre class="alt"><span class="lnum"> 33: </span> {</pre><pre><span class="lnum"> 34: </span> <span class="kwrd">if</span> (ReferenceEquals(<span class="kwrd">null</span>, other)) <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre><pre class="alt"><span class="lnum"> 35: </span> <span class="kwrd">if</span> (ReferenceEquals(<span class="kwrd">this</span>, other)) <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre><pre><span class="lnum"> 36: </span> <span class="kwrd">return</span> other.Id.Equals(Id);</pre><pre class="alt"><span class="lnum"> 37: </span> }</pre><pre><span class="lnum"> 38: </span> </pre><pre class="alt"><span class="lnum"> 39: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">int</span> GetHashCode()</pre><pre><span class="lnum"> 40: </span> {</pre><pre class="alt"><span class="lnum"> 41: </span> <span class="kwrd">return</span> Id.GetHashCode();</pre><pre><span class="lnum"> 42: </span> }</pre><pre class="alt"><span class="lnum"> 43: </span> }</pre><pre><span class="lnum"> 44: </span>}</pre><pre> </pre><pre>Why the Version? Fair question -- it's there cause I use NHibernate a lot :), old habits die hard?</pre><pre> </pre><pre>So what really happened was that I just wrote two classes that implemented these interfaces, then noticed the abstract concept of using Db4o and pulled an abstract out of that, but I suck at git logs still, so I'm not gunna find that code, I'll just show you my abstract class now.</pre><pre> </pre><br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System.Linq;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Db4objects.Db4o;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.Db4o</pre><pre class="alt"><span class="lnum"> 7: </span>{</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> Db4oRepo<T> <span class="kwrd">where</span> T:IObjectWithIdentity</pre><pre class="alt"><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">protected</span> IObjectContainer Db { get; <span class="kwrd">private</span> set; }</pre><pre class="alt"><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> Db4oRepo(IObjectContainer db)</pre><pre class="alt"><span class="lnum"> 13: </span> {</pre><pre><span class="lnum"> 14: </span> Db = db;</pre><pre class="alt"><span class="lnum"> 15: </span> }</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> Store(T entity)</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> entity.Version++;</pre><pre class="alt"><span class="lnum"> 19: </span> Db.Store(entity);</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">public</span> <span class="kwrd">virtual</span> T GetById(Guid id)</pre><pre><span class="lnum"> 22: </span> {</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">return</span> Db.Query<T>(t => t.Id == id).First();</pre><pre><span class="lnum"> 24: </span> }</pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">protected</span> <span class="kwrd">virtual</span> IEnumerable<T> Get(Predicate<T> query)</pre><pre><span class="lnum"> 26: </span> {</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">return</span> Db.Query(query);</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span> </pre><pre><span class="lnum"> 30: </span> }</pre><pre class="alt"><span class="lnum"> 31: </span>}</pre></div><pre> </pre></div></div><br />
Seriously, could it be much simpler? IObjectContainer (I hate that name, I think IOC's have a trademark on the word "Container" darnit!) is basically a ISession (if you're an Nhibernate person) or DbContext if you're an idiot (I mean Ado.Net user). <br />
<br />
Let's look at the 2 classes that implement my 2 repo interfaces.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System.Linq;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Db4objects.Db4o;</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> myDojo.Infrastructure;</pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">using</span> myDojo.Infrastructure.Db4o;</pre><pre><span class="lnum"> 8: </span> </pre><pre class="alt"><span class="lnum"> 9: </span><span class="kwrd">namespace</span> MyDojo.Query.Infrastructure</pre><pre><span class="lnum"> 10: </span>{</pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> Db4oReadModelRepository<T> :Db4oRepo<T>, IReadModelRepository<T> <span class="kwrd">where</span> T : IObjectWithIdentity,<span class="kwrd">new</span>()</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> Db4oReadModelRepository(IObjectContainer db):<span class="kwrd">base</span>(db){}</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">public</span> IEnumerable<T> Get(Predicate<T> query)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">return</span> <span class="kwrd">base</span>.Get(query);</pre><pre class="alt"><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span> </pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">public</span> T GetSingle(Predicate<T> query)</pre><pre><span class="lnum"> 20: </span> {</pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">return</span> Db.Query(query).FirstOrDefault();</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span> </pre><pre><span class="lnum"> 24: </span> <span class="kwrd">public</span> IEnumerable<T> GetAll()</pre><pre class="alt"><span class="lnum"> 25: </span> {</pre><pre><span class="lnum"> 26: </span> <span class="kwrd">return</span> Db.Query<T>();</pre><pre class="alt"><span class="lnum"> 27: </span> }</pre><pre><span class="lnum"> 28: </span> }</pre><pre class="alt"><span class="lnum"> 29: </span>}</pre></div><br />
And<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> Db4objects.Db4o;</pre><pre><span class="lnum"> 2: </span> </pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">namespace</span> myDojo.Infrastructure.Db4o</pre><pre><span class="lnum"> 4: </span>{</pre><pre class="alt"><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> Db4OAggrigateRootRepository<T> : Db4oRepo<T>, IAggrigateRootRepository<T> <span class="kwrd">where</span> T : ObjectWithIdentity</pre><pre class="alt"><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> Db4OAggrigateRootRepository(IObjectContainer db):<span class="kwrd">base</span>(db){}</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span>}</pre></div><br />
Couldn't be much simpler could it? <br />
<br />
Let's go look at some integration tests really quick. <br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System.IO;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> Db4objects.Db4o;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Db4objects.Db4o.Internal;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> myDojo.Domain.UnitTests;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> myDojo.Domain.Users;</pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> myDojo.Infrastructure;</pre><pre class="alt"><span class="lnum"> 7: </span><span class="kwrd">using</span> myDojo.Infrastructure.Db4o;</pre><pre><span class="lnum"> 8: </span><span class="kwrd">using</span> NUnit.Framework;</pre><pre class="alt"><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span><span class="kwrd">namespace</span> myDojo.Domain.IntegrationTests</pre><pre class="alt"><span class="lnum"> 11: </span>{</pre><pre><span class="lnum"> 12: </span> [TestFixture]</pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> test_the_repo</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> [Test]</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> store_and_retrieve_a_martialartist()</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> var ma = <span class="kwrd">new</span> MartialArtist(<span class="kwrd">null</span>);</pre><pre class="alt"><span class="lnum"> 19: </span> _repo.Store(ma);</pre><pre><span class="lnum"> 20: </span> _dbContainer.Close();</pre><pre class="alt"><span class="lnum"> 21: </span> var newContainer = Db4oFactory.OpenFile(dbFile);</pre><pre><span class="lnum"> 22: </span> var repo2 = <span class="kwrd">new</span> Db4OAggrigateRootRepository<MartialArtist>(newContainer);</pre><pre class="alt"><span class="lnum"> 23: </span> var retrieved = repo2.GetById(ma.Id);</pre><pre><span class="lnum"> 24: </span> retrieved.ShouldEqual(ma);</pre><pre class="alt"><span class="lnum"> 25: </span> newContainer.Close();</pre><pre><span class="lnum"> 26: </span> }</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">private</span> IObjectContainer _dbContainer;</pre><pre><span class="lnum"> 28: </span> <span class="kwrd">private</span> Db4OAggrigateRootRepository<MartialArtist> _repo;</pre><pre class="alt"><span class="lnum"> 29: </span> <span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> dbFile = <span class="str">"tests.db"</span>;</pre><pre><span class="lnum"> 30: </span> [SetUp]</pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> setup()</pre><pre><span class="lnum"> 32: </span> {</pre><pre class="alt"><span class="lnum"> 33: </span> _dbContainer = Db4oFactory.OpenFile(dbFile);</pre><pre><span class="lnum"> 34: </span> _repo = <span class="kwrd">new</span> Db4OAggrigateRootRepository<MartialArtist>(_dbContainer);</pre><pre class="alt"><span class="lnum"> 35: </span> }</pre><pre><span class="lnum"> 36: </span> [TearDown]</pre><pre class="alt"><span class="lnum"> 37: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> teardown()</pre><pre><span class="lnum"> 38: </span> {</pre><pre class="alt"><span class="lnum"> 39: </span> _dbContainer.Close();</pre><pre><span class="lnum"> 40: </span> File.Delete(dbFile);</pre><pre class="alt"><span class="lnum"> 41: </span> }</pre><pre><span class="lnum"> 42: </span> }</pre><pre class="alt"><span class="lnum"> 43: </span>}</pre></div><br />
I showed you this first because it's easier to see the code than explain it. Db4o gives you a Db4oFactory object that lets you do a lot of stuff to create IObjectContainers. My repositories don't care what kind of IObjectContainer it is, just that it is one. This test just creates a new db (line 33). If the file isn't there, it just creates it. No mapping, nothing, just store your stuff. How awesome is that? <br />
<br />
Ok, so, let's go look at my actual application and how it injects an IObjectContainer into repositories.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System.Web;</pre><pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Web.Mvc;</pre><pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> System.Web.Routing;</pre><pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Db4objects.Db4o;</pre><pre class="alt"><span class="lnum"> 5: </span><span class="kwrd">using</span> myDojo.Web.Init;</pre><pre><span class="lnum"> 6: </span><span class="kwrd">using</span> StructureMap;</pre><pre class="alt"><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span><span class="kwrd">namespace</span> myDojo.Web</pre><pre class="alt"><span class="lnum"> 9: </span>{</pre><pre><span class="lnum"> 10: </span> <span class="rem">// Note: For instructions on enabling IIS6 or IIS7 classic mode, </span></pre><pre class="alt"><span class="lnum"> 11: </span> <span class="rem">// visit http://go.microsoft.com/?LinkId=9394801</span></pre><pre><span class="lnum"> 12: </span> </pre><pre class="alt"><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> MvcApplication : HttpApplication</pre><pre><span class="lnum"> 14: </span> {</pre><pre class="alt"><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Dispose()</pre><pre class="alt"><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">base</span>.Dispose();</pre><pre class="alt"><span class="lnum"> 19: </span> <span class="kwrd">if</span>(Db!=<span class="kwrd">null</span>)</pre><pre><span class="lnum"> 20: </span> Db.Dispose();</pre><pre class="alt"><span class="lnum"> 21: </span> </pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span> <span class="kwrd">public</span> <span class="kwrd">static</span> IEmbeddedObjectContainer Db { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">private</span> IEmbeddedObjectContainer OpenDatabase()</pre><pre class="alt"><span class="lnum"> 25: </span> {</pre><pre><span class="lnum"> 26: </span> <span class="kwrd">string</span> relativePath = DbFileName;</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">string</span> filePath = HttpContext.Current.Server.MapPath(relativePath);</pre><pre><span class="lnum"> 28: </span> <span class="kwrd">return</span> Db4oEmbedded.OpenFile(filePath);</pre><pre class="alt"><span class="lnum"> 29: </span> }</pre><pre><span class="lnum"> 30: </span> </pre><pre class="alt"><span class="lnum"> 31: </span> <span class="kwrd">public</span> <span class="kwrd">const</span> <span class="kwrd">string</span> DbFileName = <span class="str">"myDojo.db4o"</span>;</pre><pre><span class="lnum"> 32: </span> </pre><pre class="alt"><span class="lnum"> 33: </span> <span class="kwrd">protected</span> <span class="kwrd">void</span> Application_Start()</pre><pre><span class="lnum"> 34: </span> {</pre><pre class="alt"><span class="lnum"> 35: </span> </pre><pre><span class="lnum"> 36: </span> AreaRegistration.RegisterAllAreas();</pre><pre class="alt"><span class="lnum"> 37: </span> </pre><pre><span class="lnum"> 38: </span> RegisterGlobalFilters(GlobalFilters.Filters);</pre><pre class="alt"><span class="lnum"> 39: </span> RegisterRoutes(RouteTable.Routes);</pre><pre><span class="lnum"> 40: </span> Db = OpenDatabase();</pre><pre class="alt"><span class="lnum"> 41: </span> </pre><pre><span class="lnum"> 42: </span> ObjectFactory.Configure(c =></pre><pre class="alt"><span class="lnum"> 43: </span> {</pre><pre><span class="lnum"> 44: </span> c.Scan(s =></pre><pre class="alt"><span class="lnum"> 45: </span> {</pre><pre><span class="lnum"> 46: </span> s.AssembliesFromApplicationBaseDirectory();</pre><pre class="alt"><span class="lnum"> 47: </span> s.LookForRegistries();</pre><pre><span class="lnum"> 48: </span> });</pre><pre class="alt"><span class="lnum"> 49: </span> </pre><pre><span class="lnum"> 50: </span> });</pre><pre class="alt"><span class="lnum"> 51: </span> ControllerBuilder.Current.SetControllerFactory(<span class="kwrd">new</span> StructureMapControllerFactory(ObjectFactory.Container));</pre><pre><span class="lnum"> 52: </span> </pre><pre class="alt"><span class="lnum"> 53: </span> }</pre><pre><span class="lnum"> 54: </span><span class="rem">/*lots of other stuff</span></pre><pre class="alt"><span class="lnum"> 55: </span><span class="rem">*/</span></pre><pre><span class="lnum"> 72: </span>}</pre></div><br />
Ok, so you see I'm actually creating an IEmbeddedObjectContainer, but not where I'm using it. This happens with some IOC nested container and ControllerFactory magic.<br />
<br />
Let's go look at my StructureMapControllerFactory.<br />
<br />
<div class="csharpcode"><pre class="alt"><span class="lnum"> 1: </span> <span class="kwrd">protected</span> <span class="kwrd">override</span> IController GetControllerInstance(RequestContext requestContext, Type controllerType)</pre><pre><span class="lnum"> 2: </span> {</pre><pre class="alt"><span class="lnum"> 3: </span> <span class="kwrd">if</span> (controllerType == <span class="kwrd">null</span>) <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 4: </span> var nestedContainer = _container.GetNestedContainer();</pre><pre class="alt"><span class="lnum"> 5: </span> nestedContainer.Configure(c => c.AddRegistry(<span class="kwrd">new</span> PerRequestRegistry(requestContext)));</pre><pre><span class="lnum"> 6: </span> requestContext.HttpContext.Items[ContainerKey] = nestedContainer;</pre><pre class="alt"><span class="lnum"> 7: </span> ServiceLocation.CurrentContainer = nestedContainer;</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">return</span> (IController) nestedContainer.GetInstance(controllerType);</pre><pre class="alt"><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> </pre><pre class="alt"><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> ReleaseController(IController controller)</pre><pre><span class="lnum"> 12: </span> {</pre><pre class="alt"><span class="lnum"> 13: </span> var baseController = controller <span class="kwrd">as</span> Controller;</pre><pre><span class="lnum"> 14: </span> <span class="kwrd">if</span> (baseController != <span class="kwrd">null</span>)</pre><pre class="alt"><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> var nestedContainer = (IContainer) baseController.HttpContext.Items[ContainerKey];</pre><pre class="alt"><span class="lnum"> 17: </span> var db = nestedContainer.GetInstance<IObjectContainer>();</pre><pre><span class="lnum"> 18: </span> db.Close();</pre><pre class="alt"><span class="lnum"> 19: </span> nestedContainer.Dispose();</pre><pre><span class="lnum"> 20: </span> }</pre><pre class="alt"><span class="lnum"> 21: </span> <span class="kwrd">base</span>.ReleaseController(controller);</pre><pre><span class="lnum"> 22: </span> }</pre><pre class="alt"><span class="lnum"> 23: </span> }</pre><pre><span class="lnum"> 24: </span> </pre><pre class="alt"><span class="lnum"> 25: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> PerRequestRegistry : Registry</pre><pre><span class="lnum"> 26: </span> {</pre><pre class="alt"><span class="lnum"> 27: </span> <span class="kwrd">public</span> PerRequestRegistry(RequestContext requestContext)</pre><pre><span class="lnum"> 28: </span> {</pre><pre class="alt"><span class="lnum"> 29: </span> ForSingletonOf<IObjectContainer>().Use(c => MvcApplication.Db.Ext().OpenSession());</pre><pre><span class="lnum"> 30: </span> For<RequestContext>().Use(requestContext);</pre><pre class="alt"><span class="lnum"> 31: </span> For<HttpContextBase>().Use(requestContext.HttpContext);</pre><pre><span class="lnum"> 32: </span> }</pre><pre class="alt"><span class="lnum"> 33: </span> }</pre><pre><span class="lnum"> 34: </span>}</pre><pre> </pre><pre>So what's going on here? When the MVC stack creates an instance of the requested controller, we create a new nested container (line 4) and add a PerRequestRegistry to it. If we look at line 29, you see I register IObjectContainer as a Singleton, and tell StructureMap to call Ext().OpenSession() on the Db we created in Application_start to construct it. </pre><pre>When the Stack releases the controller, we want to actually commit our changes (ISession.Flush()). To do that, I go get the nested container from HttpContext.Items (where I stored it on GetControllerInstance), fetch the ObjectContainer from there, and call Close(). Close automatically does a flush. So I'm good. I then Dispose the container, which will dispose of any IDisposable in my container.</pre><pre> </pre><pre>It's really that simple. This just works. You've already seen code that uses these repo's so not sure what else to show ya here. I strongly recommend you check out Db4o, it makes life nice.</pre></div>Elliotthttp://www.blogger.com/profile/15663763049861827485noreply@blogger.com3