Easier object matching in TDD with SpecsFor
I love using SpecsFor to write tests. However, there’s a function in the library called ShouldLookLikePartial that works on anonymous objects, that has always left a little to be desired. Recently I decided to fix it, and with the next release of SpecsFor, my change will be in! This involved me diving into some expression parsing, which was much easier than I thought it would be. I also thought it was pretty interesting, so I decided to share what I dug up. Read on if you’re interested!
The Problem
One common thing you do when testing is assert that your result (say a view model) looks like you expect. But lets say your result involves a new domain object being created and saved. You want to verify its properties are set correctly, but there’s probably some fields you don’t care about (like an ID).
Originally, you would use the ShouldLookLikePartial method in SpecsFor.
[Test]
public void then_only_certain_properties_are_set()
{
SUT.ShouldLookLikePartial(new { Name = "blah" });
}
This accepts an anonymous object, so it only compares what you’ve defined. However, you’ve lost one of the big advantages of a statically typed language. Your tests are no longer refactor friendly. We want our feedback cycle to tell us ASAP when something is wrong. If you renamed a field, your code would still build. It wouldn’t fail until you ran your tests. With tools like ReSharper, renaming a field is typically very safe, but we’ve just made it a little more brittle.
The Solution
I worked with Matt Honeycutt (owner and creator of SpecsFor) to come up with the syntax we wanted - which is pretty much “works just like the old Partial version, except you declare your type now”. So that’s what I did!
[Test]
public void then_only_certain_properties_are_set()
{
SUT.ShouldLookLike(() => new TestObject { Name = "blah" });
}
It works with more than just a simple object though. We can even test nested complex objects:
SUT.ShouldLookLike(() => new TestObject
{
Name = "Test",
Awesomeness = 11,
Nested = new TestObject
{
Name = "nested 1 test",
Nested = new TestObject
{
Name = "ULTRA NEST COMBO KILL",
Awesomeness = 69 //thanks, Bill & Ted, real mature.
}
}
})
As well as IEnumerables:
list.ShouldLookLike(() => new[]
{
new TestObject
{
Name = "one",
Awesomeness = 1
},
new TestObject
{
Name = "two",
Awesomeness = 2
}
})
And you can of course mix and match all that:
SUT.ShouldLookLike(() => new TestObject
{
Name = "Test",
Awesomeness = 11,
Nested = new TestObject
{
Name = "nested 1 test",
NestedArray = new []
{
new TestObject
{
Name = "level 1 nested 1",
Awesomeness = 11
},
new TestObject
{
Name = "level 1 nested 2",
NestedArray = new []
{
new TestObject
{
Name = "lets get nested son"
}
}
}
},
Nested = new TestObject
{
Name = "ULTRA NEST COMBO KILL",
Awesomeness = 69, //thanks, Bill & Ted, real mature.
NestedArray = new []
{
new TestObject
{
Name = "nested array 1",
},
new TestObject
{
Name = "nested array 2"
}
}
}
}
})
The How
Well, the simplest way to see how is to check out the code, of course. I’m going to talk about some high level parts here, but suggest you take a look at the code to see it all working together in action. The meat of it is simply in checking the Body property of the expression. For a simple initializer, it’ll be MemberInitExpression.
var memberInitExpression = matchFunc.Body as MemberInitExpression;
From there, you can use the helper function off the Expression class to turn the expression into a lambda, compile it, and execute it. Then you have your expected value:
var expected = Expression.Lambda<Func<object>>(memberInitExpression).Compile()();
Now, since we don’t want to compare the entire value, only the specified properties, we can loop through the Bindings property of the MemberInitExpression. If this is another expression, we call ourselves with recursion. Otherwise, we use reflection and just test the values. Here’s the code snippet of us checking to see if we need to use recursion or not:
var bindingAsAnotherExpression = memberBinding as MemberAssignment;
if (bindingAsAnotherExpression != null &&
bindingAsAnotherExpression.Expression.NodeType == ExpressionType.MemberInit)
{
ShouldMatch(actualValue, bindingAsAnotherExpression.Expression as MemberInitExpression);
}
We want to handle arrays as well! Turns out, this is simple too. Just like we tested the Body property to see if it was an MemberInitExpression, we can test if it’s a NewArrayExpression.
var newArrayExpression = matchFunc.Body as NewArrayExpression;
Then from there, we just loop off the Expressions array property on the NewArrayExpression type, calling ourselves with recursion.
var array = actual.ToArray();
for (int i = 0; i < arrayExpression.Expressions.Count; i++)
{
ShouldMatch(array[i], arrayExpression.Expressions[i] as MemberInitExpression);
}
Put those bits all together, and we have a pretty simple class that makes our tests a lot easier to read and maintain. I really do recommend checking out the finished class if you want to see how it all gels together!
TL;DR
Don’t use ShouldLookLikePartial anymore. Use ShouldLookLike! You can now have your cake and eat it too!
If you find any bugs, please report them at the GitHub SpecsFor repository! Also please let us know if there’s some functionality that ShouldLookLikePartial used to perform that ShouldLookLike does not.
11:35 AM | Labels: Coding, General, Testing/TDD/BDD | 0 Comments
My experience with FubuMVC so far
I’ve been checking out FubuMVC with a little hobby project. Why? Honestly, just curiosity. I don’t have much problem getting stuff done in Asp.NET MVC, but I love StructureMap, so was just curious how the creator of that would envision a MVC framework. Here are my thoughts so far. This is not me trying to post a end-all / be-all post about FubuMVC, just detailing a little of my experiences so far.
The good
There’s lots to love about FubuMVC. Lets start with organization. I absolutely hate the “Controllers / Models / Views” folder layout that the Asp.NET MVC project template uses. That’d be like designing a library by making the folders “Classes / Interfaces / Enums”. So out of the gate FubuMVC is directing you to something better, I feel. I opted for Namespace-per-feature. Granted, I should be able to put controllers and models wherever in a Asp.NET MVC project (not sure on Views, but probably), but with the template doing that, it’s going to be hard to do that outside of greenfield development.
I also really enjoy the behavior chains pattern. They work well and are easy to implement. I don’t know how much they differ from similar features in Asp.NET MVC like Handlers (and to an extent, ActionResults), but they’re still nice.
It took me a while to fully grok it, and while I’m not 100% there, I really dig the way routes are handled. In Asp.NET MVC, you define a pattern, which then tries to match controller actions. In FubuMVC, that’s turned around – you define your actions, then your convention for how actions are discovered, and those are converted to routes. It feels much more intentional and direct, and after using it some, it makes Asp.NET MVC’s way feel bass ackwards.
Everything, and I mean everything, is extensible. You can define your own conventions for pretty much everything. It has some great, sensible conventions baked in that will probably suit like 90% of people, and if nothing else, get you going out of the door. But when you’re ready for something different, you got the hooks to do it, typically by implementing a simple interface then telling StructureMap to pick it up.
One of the best features thought, is their diagnostics. Your site will have a “/_fubu” path. Opening it up will show you all your routes and the behavior chains wrapped around them. A great way to see why your routes aren’t working, what chains are getting applied, and a good early warning system for issues. There’s a lot more there, but I haven’t had a chance to dig into it all yet. But I love what I see. So few tools put support to the forefront like this.
The bad
FubuMVC bills itself as “The MVC framework that gets out of your way”. Well, and this isn’t really a fair statement but I’ll say it anyways – often times it feels like it’s out of my way because there’s nothing there. It still has some areas that left me very frustrated and annoyed.
The documentation is just sad. Their own docs are woefully incomplete with many sections left empty. And some of their examples do not seem to work as advertised. One that sticks out was their “asset pipeline”. It’s what you use to include CSS and Javascript files. There’s the very nice Bundles features of Asp.NET MVC, so I was looking for the FubuMVC alternative, and this was it. However, I cannot find a way to get it work. If I just want it to spit out the per-file link and script tags, it’s fine. But when I tried to use a combination, following what’s on their documentation, it would not work. I did not get an error (even with the option to show a YSOD turned on), and nothing gets emitted. It felt very black-box. I found a post on the FubuMVC google groups page with the same issue, with no response.
The Razor story in FubuMVC is miserable. I feel it should just be pulled. From the docs, you’ll think you can have a rewarding experience with it, but you just can’t. I followed the instructions, then had to tweak things to work with the latest release of Razor. I could not get intellisense working, which makes developing Views extremely cumbersome and “guess-and-test”. On top of that, a lot of Razor is built on the Html helpers, which will not work in FubuMVC. I assume full Razor support is possible, but it’s probably not worth it, due to how Razor was unfortunately designed (lots of static helpers, dependencies on HttpContext, etc). I think the better path is to ditch Razor altogether, and have them fully embrace and support a view engine. Perhaps Spark is that view engine? I understand FubuMVC is trying to stay open and allow people to pick their own, but seeing Razor is tempting, and while it’s not directly FubuMVC’s fault, it leaves with an altogether underwhelming experience that made me want to go back to Asp.NET MVC. Do not use Razor if you’re going to use FubuMVC. The FubuMVC examples though show returning strings of HTML, and that’s not really going to work. So I feel lost – unless the answer from the team is indeed “use Spark”.
I still have no idea how to render a partial view (or an action) with Razor, passing down a submodel. I probably shouldn’t be doing that anyways, as I thought of a better design for what I wanted using knockout templates, but it was still very frustrating. I can’t find anything in the documentation about how to do that. Probably because it all hinges on what view engine you use, and how much support it has.
Caveats
I don’t really like saying negative things, especially without giving the full context. So here’s some caveats I want to add in addition to the troubles I faced.
For one, I didn’t ask for help. There’s the aforementioned FubuMVC google group, that the FubuMVC developers seem to be responsive on. I was able to usually find a post there about my issue, but there was no good resolution. Still, I could’ve reached out more to make sure I’m not making newbie mistakes with easy answers.
Also, FubuMVC goes to version 1.0 in January (or that’s at least the plan). This is still a product in Alpha/Beta stage or some such. One of the things I know is coming with 1.0 is real documentation. Jeremy Miller has kinda a reputation for releasing a great product with poor docs (StructureMap), but with StructureMap I never ran into an issue I couldn’t solve with a little GoogleFu. Hopefully with 1.0 some of my issues get resolved.
Then the last caveat, that I’ve already mentioned – this is just a post of my experiences so far. I’m not saying there’s no good and easy way to do bundling and minification of CSS files in FubuMVC, for example. Just that I couldn’t get it to work after banging away for a few hours (or completely rolling my own solution). That goes for most of my “bad” notes.
Final thoughts
FubuMVC has a lot of promise. What’s there is mostly great, it’s what’s not there that hurts it the most (especially when compared to Asp.NET MVC). If you’re looking to start a new project for your business – I hate to say it, but my opinion is to stick with Asp.NET MVC. You’ll get things done faster, and have more resources available to you. If you’re doing a hobby project and want to learn something new…I’d probably recommend to wait until FubuMVC 1.0 comes, and see what it holds at that time. It has a lot of promise, and I think it’s headed in a great direction. Just didn’t feel it was ready for “primetime” yet. Even then though, just stay away from Razor – from what I’ve read on their group, unless someone champions it on themselves, the Razor story in FubuMVC will always be lackluster. Which is a shame, because I really like that syntax.
9:02 AM | Labels: ASP.Net, Coding | 6 Comments
RavenDB and creating indexes – newbie gotcha! (and other Raven thoughts)
I’ve been playing with a hobby project on RavenDB and FubuMVC. I’m 100% newbing it up to both technologies, but I like a lot of what I see in both.
Anywhos, almost all the “getting started” RavenDB tutorials recommend putting a line of code in your application startup (Global.asax for Asp.NET MVC) that will scan a given assembly for any indexes, and add them for you. Pretty awesome, right? Because you can’t query against an index if it isn’t there. Here’s the line:
IndexCreation.CreateIndexes(Assembly.GetCallingAssembly(), DocumentStoreInstance);
I had this, yet my new index did not get picked up! I changed the Assembly.GetCallingAssembly() line to be typeof(MyIndexType).Assembly to make sure I wasn’t grabbing the wrong assembly. My query on the index still blows up with “index not found”. I open the RavenDB web UI, and the index isn’t there. I look on the file system, and I can even *see* the index in the folder!
To cut a long story short (4 hours worth, boy do I feel dumb), I was trying to be A Good Developer, and I was using tenants in RavenDB. Tenants are like creating a database in SQL Server. See, you can use RavenDB without creating a tenant. It just dumps everything in System. That’d be like creating your tables in the “master” database in SQL Server. The line above, of course, does not specify a tenant. The solution?
var catalog = new CompositionContainer(new AssemblyCatalog(typeof(MyIndexType).Assembly)); IndexCreation.CreateIndexes(catalog, DocumentStoreInstance.DatabaseCommands.ForDatabase("YourTenantName"), DocumentStoreInstance.Conventions);
I’d like to see an overload for IndexCreation that makes that a little simpler and easier to consume, but not the end of the world. Hopefully that helps someone else getting started to RavenDB!
Other Raven thoughts
Because I can’t stop talking…I’m not sure why RavenDB uses the words “tenants”. Even the web UI calls them Databases, and the method above says “ForDatabase”. Yet you’ll see some talk about multi-tenancy in relation to RavenDB. The 10 tips and tricks page on RavenDB’s site calls them tenants. I’m not sure if I’m using the wrong term, or if it’s a case of there not being a preferred term, and it all means the same thing.
I also don’t like what I call “the broken intro”. Remember when you were new to the world of software, and you wrote your first hello world website? And how that tutorial had you happily concatenate SQL right from a textbox and push that down to your database? I call that a broken intro – when the introduction to something is The Wrong Way to do it. I’ll probably make a bigger post on this some day, but the point is most of the intro stuff on RavenDB’s site has you dumping stuff into the “system” database. That’s even a protected database on the web UI, and it bitches at you for going there.
My last thought is how much I love working with RavenDB so far :3. A native .NET API, no fighting persistence related nightmares, and a great administrative interface. I know it’s just the honeymoon stage, but I really enjoy it.
9:34 PM | Labels: Coding, RavenDB | 1 Comments
Saving the internet!
Tired of all the hate spreading on the internet lately? I don't blame you. With recent tirades like "The framework I like working in is way more betterer than yours" and great responses like "nu-uh! Shut up!, it can get a bit tiresome.
I think it'd help to remember though, that all these are, are the expressions of opinions. Unfortunately they're often presented as fact, generally because people just get tired of reiterating that it is indeed, just their opinion.
However, I now have the solution! Simply bookmark this link, and anytime you're reading something and you start getting a little "hot under the collar", click your bookmark, and read it again.
There! I've made the internet safe again!
(Edit: Sorry if you got this post multiple times. Original bookmarklet was trying to do too much, got too complex and buggy. This one has nuances, but it's fine for the joke, and much simpler ;-) )
1:15 PM | Labels: Coding | 0 Comments
The case for MSBuild
I was talking to a fellow developer on Facebook (I know, that’s not twitter, wtf!) and he was asking about build and deployment issues – specifically some frustrations around MSBuild. I suggested some things to help with his issues and utilize it better. That kinda got me thinking about how it seems many people don’t know how to use MSBuild in-depth (not that I’m an expert by any means). It seems to be dismissed very early and easily as a build and deployment solution. So, I’m taking the not trodden road, the dusty path, going where no man has gone before…and blogging in favor of MSBuild…in 2011!
(Note: This post isn’t actually how to use MSBuild itself. It’s me talking about why it’s worth taking the time to actually consider/learn it.)
The Case Against
<XML />
Man, do people hate XML! Nothing more to really say about this – you either hate it or you don’t care. I don’t think anyone (well, human at least) loves XML. At least it’s not EDI, right? However – remember that writing your own MSBuild task can be as simple as inheriting from a base class and overriding Execute(). So if you want to do your whole build/deploy with a .NET language, you can, with a minimal amount of XML.
It’s not sexy
Okay, well, noone has said those words, I don’t think, but that’s what people’s faces say whenever you mention MSBuild. It’s not “the new hotness” by any means and it requires a learning curve. Any other new sexy build technologies have a learning curve too, but for whatever reason, developers (myself included) are much less likely to want to learn something when it’s an old product.
Rob wrote a post saying you should consider it
If I say it, it must already be wrong! I’m totally dooming myself to internet unpopularity! Zomgz! And anyone else who agrees with me even in the slightest will share my dark corner of internet shame. Next I’ll be posting about how awesome VB.NET is, how MSTest is soooo much more awesomer than nUnit, and how WebForms really got the web right! <CharlieTheUnicornsFriends>SHUUUUUUN!!!!</CharlieTheUnicornsFriends>*
*Oh god, I’m using XML even in my blog posts now!
It’s not updated for user friendliness…or I don’t think is…hell I don’t know!
There doesn’t seem to be a real….community around MSBuild. If new updates are happening to it, I’m unaware of them (and I follow several MS blogs..maybe it’s the wrong ones?). Either way, I do not believe it gets updates that would make consumption of it easier, or have more things “baked in”, and some of the other new build tools are more likely to have that user-friendliness and open-source community guiding the directions of their product.
The Case For
*Man, I’m making lots of “notes” in this one! Anyways, none of the “case for” necessarily mean it in a “competing-product-x-cant-do-this” fashion. I’m simply saying that MSTest CAN do these things, and a lot of people seem to think it can’t, or that it’s a big hassle to do these things with MSBuild, which is not the case.
You, and your grandma, already have it installed
“Laziness” isn’t a good criteria for choosing your toolset, but simplicity is. As long as you’re building on a Windows machine, you already have all the tools you need*. You just run it from the command prompt (or better yet, a batch file).
*note that the normal .NET installation does not have web-related targets by default however, which docks 5 internet points from MSBuild. What I’ve done is make a self-extracting exe, that you can call from a custom MSBuild target. You run it once on a given machine, and it’s set from there on out. You could probably do something similar for installing needed software for other builders too though.
You’re already using it
Like it or not, you’re using MSBuild! Until I started writing my own custom targets, I never really groked what was going on in a *.sln or .*proj file. This helped me learn more about how .NET solutions and projects are arranged, how things are coordinated “underneath the hood”. You can always read a book or a blog about these things, but I always find you can’t really understand something until you have to deal with it by hand. Knowing more about what you’re dealing with can a help a lot when you start having issues in your project/solution files, or want to add/edit post/pre build steps.
There’s a nice existing library of community addons
Chances are, if you’re thinking about doing some lower-level task (like, for example, reading XML out of a config, or starting/stopping windows services), there’s probably already a task out there. Granted, other frameworks have this too, and the MSBuild Community Tasks haven’t been updated in forever (remember, it’s old! Time to move on!), but the point is, there is a library out there that’s extremely useful and tackles a whole lot of common cases. The point being – there’s more than what’s just built-in that’s easy to get. A lot of people don’t seem to know this.
Yes, you can do that with it
Whatever “it” is, you can accomplish it with MSBuild. Again, not saying that other frameworks/methods don’t have this either – but MSBuild is extremely powerful. A lot of people seem to equate “XML = Useless”, and I just want people to understand that’s not the case. The fact that you can write your own C# library and have MSBuild run commands in it literally means “can do anything”.
Highly reusable
It’s extremely easy to factor-out common targets to reuse them across multiple projects or even multiple solutions. On a previous project we had 3 websites being deployed to 5 machines, all using the exact same target, with no repetition. You can usually factor out things into more targets and chain them or composite them together as needed in higher-level build files. Yet again – not a unique feature, just saying MSBuild supports it, and it’s easy.
What Really Matters
It’s good to remember to come back and focus on what’s important – delivering value. Build and deployment is a very serious part of any project, and unfortunately most of the time it gets cast aside, only to be rushed through at the last minute. Good agile software techniques are teaching us and the industry as a whole to consider these things up front and to make them painless. The fact that we’re even discussing it at all is a great thing! Noone really recommends “deploy from Visual Studio to prod” or “copy these files, then hand-edit that config, the don’t forget to insert these rows in the database…” as a serious technique anymore. What’s even better is that there ARE options out there!
I just hope that after this people keep an open mind about MSBuild, and realize that it’s a powerful solution that can work great on .NET solutions. It’s as “correct” a solution as alternatives such as Albacore, Rake, PowerShell, etc. The only real differences are likely to be your opinion for what fits your style/team/situation better.
So what it boils down to, again, is delivering that value. Pick something that your team is interested in, that your company can easily support and maintain, and that efficiently works with your processes.
7:23 PM | Labels: Coding, Continuous Integration | 3 Comments
Focus on the team – not the product
I'm going to state something you probably already know
Agile is about the team - not about the products.
It feels like a very obvious truth to me, but I didn't realize it until recently. It was one of those things that as soon as I said it, I realized that it was extremely true. I hadn't heard it before (or it didn't stick - I'm pretty dumb, after all).
Why haven't I heard this before? Why does this even matter? This is probably due to, at least in part, always working on small teams. On the rare occasions where I've been on a larger team (6-7 developers), we had several products. Usually to the point where each developer worked on a product by themselves, or mostly by themselves.
Now I'm going to talk about more things you probably already know!
How things typically seem to go:
Traditional (typically rooted in an attempt to do Waterfall) organization in regards to software development seems to be centered around the products. There's a request for some new software. It gets added to a Product Manager's "list of products I'm PM for". The PM makes a request to the development group. The development group allocates some resources to the effort. This is done by taking one or more developers away from something else they're doing - or worse, they're asked to "split time" between multiple products. Each way has their own major problems.
If a developer is asked to split their time, they now have conflicting priorities. They can ask whoever represents the Product group which product feature is the most important across multiple products, and will get different answers. Everyone will always want whichever product they're closest too (or is the squeakiest wheel) done first. Getting a single clear direction, that doesn't change on a daily (or worse, hourly!) basis will be neigh-impossible.
If a developer is made dedicated on this new product, while at least they won't have conflicting priorities, other issues arise. That developer will likely be working in a silo. No ability to do peer reviews (other than simple adherence to coding standards possibly). Higher stress due to not being able to take sick time. Developing in a black box. Harder to bounce ideas off of peers, since no one else will know the domain. Chances are if someone else has to join in on the product later, that person will be very lost, and will likely end up needing either a lot of time learn the existing codebase, or re-writing the product.
In both cases, any existing products are damaged, as their ability to do planning will be interrupted. If they're attempting to do Scrum, for example, now their velocity will be incorrect, and will have to be recalculated. Again, if things are ran by product, and not by team, and especially if a developer is spread across products, getting any consistency will be extremely difficult, if not impossible.
How organizing around the team fixes things:
When you organize around the team, in a proper Agile environment, one of the first things you'll get is a single Product Owner. Without getting into the full definition of the PO role in Agile terms, the key part that is important in relation to this post is the fact that the PO will set the priority of work for the team. This gives a single person the responsibility of knowing what's important to each product the team supports and the relative priority among the products. This is critical.
The next thing you'll get is predictability. With the developer/QA resources (boy do I hate using that word, but stick with me) being constant, the amount of work the team can get done will be consistent - regardless of the product.
You'll also avoid silos this way. Since a team grooms stories together, everyone will be knowledgeable about the products. This gives people freedom to take time off work as needed, without being "the one guy" that knows how something works. The Company typically likes this aspect when phrased as "If we do this, then if [So-and-so] gets hit by a bus, the product isn't doomed!". If you're practicing some good policies like peer reviews, that will also help spread knowledge about the products throughout the team, as well as let everyone feel that they know all the products on that team.
Avoiding possible pitfalls:
What if it gets to where you have lots of products (typically, several smaller ones and a larger one or two)? Then you have another problem - but it's not solved by organizing around the product. Not that I'd even really want to call that a problem. If one product consumes 80% of the team's time, it may be viable to split the team at that point, so that a larger portion is dedicated to that product, and a smaller team is formed to handle the other products. Be prepared to keep the teams consistent after that point though, and don't start "borrowing" resources from other teams, or you'll end up right where you started.
Splitting the team also means getting another PO - one for each team. It might, in theory, be possible to have a single PO over multiple teams, but more than likely it'll end up with that person not being able to focus on each team as they should.
Many businesses want "on demand" time spent on their product. They want that product right then and there, and later on, interest in that product might slow down (especially once it's been launched, and initial revenue gained). That leads them to want to shuffle development around as needed. Agile is great at providing visibility in the effect these decisions have. By switching the focus from the product to the team, it allows businesses to see how it actually hurts other products, instead of creating the illusion that development is an unlimited resource. It's up to the PO to communicate back to the Powers That Be that the new product can be delivered, but that existing products will have their timelines affected. It's going to happen anyways - but when focusing on the product, there's the false sense that other products are unaffected.
Often the most important thing when selling software (or selling anything, honestly), is managing expectations. By organizing around the team, you will get consistent, predictable results for your clients. Your developers will be able to focus on the work laid out for them, instead of trying to juggle priorities and emails. If you can deliver what you say, your clients will be happier. You'll have happier developers this way too, which lead to less turnover - which will again help with more predictability, feeding back into happy clients.
There are other things I feel are beneficial as well, but this post is long enough! Suffice it to stay, focusing on the team helps solve many problems, and will prevent other issues from arising. Again, this is based on my experience, which is limited to tiny teams, or a larger team with more products than they can handle.
4:21 PM | Labels: Agile, Coding, General | 3 Comments
Decorating a generic interface with StructureMap
While at The Day Job, I noticed we had some services that all shared the same cross-cutting concerns. In particular, validating a request object. You may think of some other ones, like logging, auditing, standard error-handling, etc. Due to other architectural concerns of our project, it made sense to decorate a common interface we had as a way to achieve the validation. First, here’s our generic interface that we want to decorate:
public interface IServiceOperation<in TRequest, out TResponse> where TResponse : ServiceResult, new() { TResponse PerformService(TRequest validatedRequest); }
Simply put, it just says given a request object, I’ll return a standard response object. To show it “in action”, here’s a sample implementation. Note that the interface has already assumed at this point that the request object has been validated.
public class SignUpService : IServiceOperation<SignUpRequest, SignUpResult> { private readonly IUserRepository _userRepo; public SignUpService(IUserRepository userRepo) { _userRepo = userRepo; } public SignUpResult PerformService(SignUpRequest validatedRequest) { var user = Mapper.Map<User>(validatedRequest); user.MarkAsLoggedIn(); user.ChangePassword(validatedRequest.UnhashedPassword); using(var transaction = _userRepo.BeginTransaction()) { _userRepo.Save(user); transaction.Commit(); } return new SignUpResult(); } }
Pretty standard stuff, nothing special. Just a domain service that takes in a request, performs some domain operations, then saves changes to a repository.
Now for our decorator. If you’re unfamiliar with the decorator pattern, here’s the simple version – a decorator is a class that implements an interface, and also consumes an instance of that interface in it’s constructor. This allows you to “intercept” any calls against the passed-in instance to add extra behavior, while wrapping the consumed instance. Your consuming application doesn’t have to change it’s code at all – your IoC container will do the magic for you. You just code against the interface like normal, and everything is happy. You can even have multiple decorators (each one consuming another!).
public class ValidateServiceDecorator<TRequest, TResponse> : IServiceOperation<TRequest, TResponse> where TResponse : ServiceResult, new() { private readonly IServiceOperation<TRequest, TResponse> _serviceOperation; private readonly IValidationService _validationService; public ValidateServiceDecorator(IServiceOperation<TRequest, TResponse> serviceOperation, IValidationService validationService) { _serviceOperation = serviceOperation; _validationService = validationService; } public TResponse PerformService(TRequest request) { var response = new TResponse(); var validationResult = _validationService.Validate(request); if (!validationResult.IsValid) { response.ValidationErrors = validationResult.ValidationErrors; return response; } return _serviceOperation.PerformService(request); } }
Easy peasy! It takes in IService<A,B>, and implements it as well. So, again – the magic needs to be handled by our IoC Container, in our case, StructureMap. StructureMap already has some great support for auto-registering open generic types with the “ConnectImplementationsToTypesClosing” function, and decorator support with the “EnrichWith” function. However, combining the two can be prolematic, as EnrichWith except your type, if generic, to be a closed generic type (meaning you know the type parameters explicitly). Obviously, we don’t know that – well, we could, but that’d mean every time we added a new request/response pairing for our IServiceOperation, we’d have to modify our container. This would lead to a lot of copy/pasted code in our container, be something everyone would forget to do, and another spot to be affected when renaming objects. Not exactly ideal.
It took quite a bit of digging, even posted the question to StackOverflow, but did not get a response. Eventually, I was able to find a solution – a custom IRegistrationConvention! If you’re not familiar with this (as I wasn’t), this is a hook StructureMap provides. By implementing this interface from Structuremap, you’ll get a hook in the form of a function that is called for every type StructureMap finds when starting up (depending on what assemblies you tell it to scan). From here, I was able to inspect each type, and see what interfaces it implemented. If the implemented interface was IServiceOperation, then through reflection I could get what two types (the response and request) were used to close the implementation, then tell StructureMap that for that closed type, to return my decorator using those same two types (with Activator.CreateInstance).
Here’s my final StructureMap code, showing it all hooked together. I hope this helps other people!
public class StructureMapServiceScanner : Registry { public StructureMapServiceScanner() { Scan(scanner => { //Tells StructureMap to scan all types in the assmebly where the interface is defined scanner.AssemblyContainingType(typeof (IServiceOperation<,>)); //Tells StructureMap that for all requests of IServiceOperation to automatically return //the concrete type that closes the interface with the same as the requested parameters scanner.ConnectImplementationsToTypesClosing(typeof (IServiceOperation<,>)); //And finally, we add our new registration convention scanner.Convention<ServiceRegistrationConvention>()); }); } }
public class ServiceRegistrationConvention : IRegistrationConvention { public void Process(Type type, Registry registry) { var interfacesImplemented = type.GetInterfaces(); foreach (var interfaceImplemented in interfacesImplemented) { if (interfaceImplemented.IsGenericType && interfaceImplemented.GetGenericTypeDefinition() == typeof(IServiceOperation<,>)) { var genericParameters = interfaceImplemented.GetGenericArguments(); var closedValidatorType = typeof(ValidateServiceDecorator<,>).MakeGenericType(genericParameters); registry.For(interfaceImplemented) .EnrichWith((context, original) => Activator.CreateInstance(closedValidatorType, original, context.GetInstance<IValidationService>())); } } } }
And of course, if you know of another/better way to do this, please respond!
8:02 PM | Labels: Coding | 1 Comments