 Thursday, November 30, 2006
The evil designer attribute ;o)
I'm working on a project at the moment where the client want's an "Add-in" for an existing piece of software... it's a COM interop project, .Net 2.0 (and yes, it uses Castle, IoC sits in nicely with the IServiceProvider :) and it hosts a custom designer, and the add-ins must exist in their own directory, outside of the host applications base directory.
At any rate, it's been working just fine so far, but today I was trying to introduce some custom designers... and they weren't being constructed... which highlighted some odd behaviour in the design time support - I have 6 or so assemblies, most are support assemblies, some containing controls and their associated designers, something like this:
public class MyButtonDesigner : ControlDesigner { ..}
[Designer(typeof(MyButtonDesigner))] public class MyCustomButton : ButtonBase { .. }
And then there is the core assembly which hosts the design surface etc. and contains a number of COM-visible classes - one of these class in the core assembly is constructed via Com interop by it's Name, and everything starts it's life from there in their Add-in, much like a Main class in a WinForms project.
So the core assembly references the support assemblies, and has no problem creating controls at runtime within these assemblies...
But then...
However, the design time support fails to construct or make use of the custom designers... and doesn't blow up, it just fails silently - personally I hate this kind of behaviour... I like things to fail fast, in your face :)
It appears the custom designer is never constructed because the design time infrastructure fails to locate the assembly it's held within, even though my code has just created an instance of a control from the same said assembly not an instant before... arghh!
To my mind this is madness... I'm not passing a string reference to the type, and the assembly is already loaded, it should be as simple as using the Type in the attribute to create an instance of the designer - looking at the internal implementation of this attribute, I can see where it all goes terribly wrong:
public DesignerAttribute(Type designerType) { this.designerTypeName = designerType.AssemblyQualifiedName; this.designerBaseTypeName = typeof(IDesigner).FullName; }
It's still a puzzle as to why this isn't being resolved... the assembly is already loaded against the current AppDomain - I guess I could mess with the fusion logger to figure it out, there must be some subtle different - but I didn't have the time to waste, so I just cheated, implementing an AppDomain.AssemblyResolve event handler to take care of returning the assembly that's already loaded when matching against a set of pre-loaded assemblies.
I think the silent failure is what annoys me the most, this is something which could be easily introduced and might not be imediately detected when performing a minor update... I'm still not sure how I could write a test to detect it...
Speaking of custom designers... I'm unimpressed by how most custom designers are marked internal... especially for classes that are designed to be subclassed... why the hell is the ButtonBaseDesigner marked internal?
 Sunday, November 26, 2006
Vista
Well I've been playing with a couple of things this weekend... first off is Vista... it's been on my laptop for a while now, but I haven't really been using it because It's a bit of a drag getting my development environment set up... but I've been working on that tonight, and have my current project being built successfully... so that's cool... the only real problem is my laptop is no longer a laptop since I've installed Vista...
The problem is power... where as I could squeeze upwards of 2 to 2.5 hours from the battery under xp, in power saving mode with Vista I get an hour if I'm lucky... it says exactly 1 hour of available capacity after boot, every "build all" in Visual Studio reduces that number by about 5 minutes... this is not particularly useful, heading to a cafe to code for an hour or two is looking problematic - At first I thought that perhaps the minimum speed step value wasn't being utilized, but according to the resource monitor it's at "37%" of the maximum frequency, which is ~800mhz, which seems right... So I have no idea why my battery life is so bad.
Second to this, max performance is only workable for about two hours before the machine will freeze - too much heat - not a new problem, certainly this was an issue under xp sp2 or win2k3 - Incidentally it's an HP NW8240 which scores 4 on the "Vista" performance scale (Pentium M 2.13ghz, 2gig ram, ATI FireGL V5000) and the cooling solution is really not adequate to deal with the heat generated from the processor and GPU at max performance, but it only cropped up while gaming in the past with XP SP2... now this happens while working with Visual Studio 2005 for a couple of hours... I suspect it's probably because I'm using Aero, the laptops definitely hotter then it used to be, I'll switch off Aero tomorrow and see how I fare, problem goes away if I switch to balanced performance mode of course, but without max performance Resharper 2.02 is an utter dog.
Other problem I have is getting bluetooth to work, which is a bit of a pain when using my phone as I've lost the mini-SD adaptor so I can't slot it into my monitors card reader... more a minor niggle though, I can live without it for now.
Supreme Commander
The other thing I've been playing with this weekend was the Supreme Commander beta, spiritual successor to Total Annihilation... TA was the defacto RTS we played while I was at uni, and it also served as the standard by which we measured any future RTS's (and generally found them wanting, especially when it came to an interface for selecting, scheduling and command units)...
SupCom is very faithful to the concepts and feel of TA, and it runs just fine on Vista, but I did end up lowering all the settings, and still suffered from overheating and crashes after half an hour of play on my laptop... *sigh* I need to experiment a little further to see how the dual screen support works, but so far the game looks great, and it provides some wonderful strategic views which make it easier to control masses of units (And I do mean masses, hopefully the multi-threading support is good, I could definitely see this game benefiting from dual-core, or maybe quad-core once an implementation is released which isn't quite so gimped)... looking forward to getting my hands on the finished product when it's released next year.
 Tuesday, November 14, 2006
More mocking...
The title for this entry should probably be "more IRepository<T> mocking" - as that's what I'm mocking out... but at any rate, let's get down and dirty... In this entry I will cover mocking out a "complex" method, incidentally I think the size of this test suggests that our test isn't fine grained enough (and that individual features within the data loader should be exposed so we can test them better) - but that's my problem, not yours ;o)
Background
So, as some background, this is a test for a class called "DataLoader" - a most unimaginative name for a class that loads data... it's used for loading an XML file which has a bunch of info for different entities... if your recall the last entry, I was talking about simplistic music information, well this class can be used to load musical data into the store, including something not mentioned so far, which is the "test suite" - the test suite defines tests which can be run against the music in the store, the details aren't really up for discussion, but it's for executing customer acceptance tests, not unit tests.
The test below checks one of the primary paths, where by no pre-existing data is available, so it must all be created by the data loader first, before creating the customer acceptance test, which references the track, release etc. data (the definition for customer acceptance test suites are also stored in the store, as are the results from run the customer acceptance tests).
Diving In...
So here is the test for loading a "test suite" for a single track, the song "Dirty Harry" by the "Gorillaz", we are testing the behavior of loading the file when the track, release and artist do not already exist in the store - most of this is basic RhinoMock's usage (though I'm no expert, I'm probably misusing some of the features...).
[Test]
public void LoadDataFreshForSingleTrack()
{
/**** RECORD ****/
Environment environment = new Environment();
SourceDevice sourceDevice = new SourceDevice();
RecordingDevice recordingDevice = new RecordingDevice();
Track track = new Track();
ObjectPath artistPath = (Artist.Fields.Name == "Gorillaz");
Expect.Call(_artistRepository.FindOne(artistPath)).Constraints(Base4Query.PathEqual(artistPath)).Return(null);
_artistRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "Gorillaz"));
ObjectPath releasePath = (Release.Fields.Name == "Demon Days");
Expect.Call(_releaseRepository.FindOne(releasePath)).Constraints(Base4Query.PathEqual(releasePath)).Return(null);
_releaseRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "Demon Days") &&
Property.ValueConstraint("Artist", Property.Value("Name", "Gorillaz")));
_trackRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "Dirty Harry") &&
Property.ValueConstraint("Release", Property.Value("Name", "Demon Days")));
ObjectPath encodingPath = (TrackContentEncoding.Fields.Name == "MP3");
Expect.Call(_encodingRepository.FindOne(encodingPath)).Constraints(Base4Query.PathEqual(encodingPath)).
Return(null);
_trackContentRepository.Save(null);
LastCall.Constraints(Property.ValueConstraint("Track", Property.Value("Name", "Dirty Harry")));
ObjectPath trackReferencePath = (Track.Fields.Name == "Dirty Harry" &&
Track.Fields.Release.Name == "Demon Days" &&
Track.Fields.Release.Artist.Name == "Gorillaz");
Expect.Call(_trackRepository.FindOne(trackReferencePath)).Constraints(
Base4Query.PathEqual(trackReferencePath)).Return(track);
Expect.Call(_trackRepository.FindOne(trackReferencePath)).Constraints(
Base4Query.PathEqual(trackReferencePath)).Return(track);
ObjectPath environmentPath = (Environment.Fields.Name == "Indoors");
Expect.Call(_environmentRepository.FindOne(environmentPath)).Constraints(
Base4Query.PathEqual(environmentPath)).Return(environment);
ObjectPath sourceDevicePath = (SourceDevice.Fields.Name == "Loopback");
Expect.Call(_sourceDeviceRepository.FindOne(sourceDevicePath)).Constraints(
Base4Query.PathEqual(sourceDevicePath)).Return(sourceDevice);
ObjectPath recordingDevicePath = (SourceDevice.Fields.Name == "Loopback");
Expect.Call(_recordingDeviceRepository.FindOne(recordingDevicePath)).Constraints(
Base4Query.PathEqual(recordingDevicePath)).Return(recordingDevice);
_testSuiteRepository.Save(null);
LastCall.Constraints(Property.Value("Name", "One track, one loopback sample") &&
Property.Value("Description",
"Testing with 30 second snippets pulled from the source files with audacity (Loopback)") &&
Property.ValueConstraint("Tracks",
ItemListOf<Track>.NumberOfItems(1) && ItemListOf<Track>.IndexedItemConstraint(0, Is.Same(track))));
/***** REPLAY *****/
_mockRepository.ReplayAll();
DataLoader loader = CreateLoader();
loader.LoadData(OneTrackFile, ContentPath);
_mockRepository.VerifyAll();
}
First you'll probably see that the callback and anonymous delegates from the last post are absent, they've been replaced with constraints... which has made the syntax a lot more concise - so far the rhino mocks support is pretty basic, we have two static classes being used for creating the constraints:
- Base4Query - for constraints on ObjectQuery and ObjectPath arguments/properties.
- ItemListOf<T> - for constraints on IItemList<T> arguments/properties.
The constraints method in RhinoMocks expects the same number of arguments as the associated method call on the mock object, constraints work on the arguments value, however you can apply constraints to the value of single property of an argument, and those constraints can be logically AND or OR'd together - for instance here:
_testSuiteRepository.Save( null);
LastCall.Constraints(Property.Value("Name", "One track, one loopback sample") &&
Property.Value("Description",
"Testing with 30 second snippets pulled from the source files with audacity (Loopback)") &&
Property.ValueConstraint("Tracks",
ItemListOf<Track>.NumberOfItems(1) && ItemListOf<Track>.IndexedItemConstraint(0, Is.Same(track))));
We are applying these constraints:
- The first argument should:
- Have a property called "Name", and it's value should be "One track, one loopback sample".
- And have a property called "Description", and it's value should be "Testing...." etc.
- And have a property called "Tracks", which is of type IItemList<Track> which:
- Contains a total of 1 items
- And the item at index 0 should:
- Be the same as the instance returned from an earlier save call.
Notice that we have to call the method we want to apply constraints to first, so that we can address it with LastCall - a necessary evil when the method has a return type of void... otherwise you can use the more pleasant "Expect.Call(...)" convention.
Also, I'm declaring instances of the expected Base4 object paths, and then applying them in the constraints... I could in-line the object paths, but I then need to disambiguate the overloaded call to the FindOne method, so we have my preference:
ObjectPath trackReferencePath = (Track.Fields.Name == "Dirty Harry" &&
Track.Fields.Release.Name == "Demon Days" &&
Track.Fields.Release.Artist.Name == "Gorillaz");
Expect.Call(_trackRepository.FindOne(trackReferencePath)).Constraints(
Base4Query.PathEqual(trackReferencePath)).Return(track);
or this:
Expect.Call(_trackRepository.FindOne(( ObjectPath) null)).Constraints(
Base4Query.PathEqual(Track.Fields.Name == "Dirty Harry" &&
Track.Fields.Release.Name == "Demon Days" &&
Track.Fields.Release.Artist.Name == "Gorillaz")).Return(track);
The second involves less code, but I think it's easy to get confused between the compile time query language's logical operators and those of the RhinoMock's constraints... each have merits of course.
Conclusion...
I'm still working through some of the finer points of the implementation (and so won't publish any code just yet...) but this should give you some ideas about how you can test base4 interactions in your application with RhinoMocks, instead of hitting the base4 server and testing the results... and I think it's a lot more intuitive when attempting to do TDD with base4...
I would also like to make a special mention that this code won't actually work with the latest published release of Base4... there is an issue preventing the addition of items to an IItemList<T> in some situations when you have not set a valid default context - there is a fix (Which works well) in the Base4 trunk, and I assume Alex James will release this at some point... and thanks for resolving that issue so quickly Alex :) much appreciated.
 Saturday, November 11, 2006
Domain Driven Design Afterthoughts...
I just finished reading Jimmy Nilsson’s Applying Domain-Driven Design and Patterns [ADDDP] book... I actually read it cover to cover, something I’ve found difficult to compel myself to do with some of the other books that have been lying around my desk for a wee while now (such as Petzold’s “Applications = Code + Markup” – a great book to assault someone with in a dark alley)...
First off, I think this book is a pretty good, it didn’t cover a lot of new ground for me, but It’s very down to earth, which I liked, and it’s encouraged me to have a read of Evan’s and Fowler’s more definitive works on the subject too... I also liked the fact that this book does attempt to tie the whole story together, from rough sketch through to identifying the domains language, using TDD to build up your domain model and even some of the gritty integration work, including evaluating OR/M features, using NHibernate as an example, and even looks into inversion of control contains (spring sadly, it would’ve been great to have seen Castle get a mention) and finally AOP. On the down side – I think the book could’ve tackled the application of rules to your domain model a little better (I wanted to see more code) and depending on your TDD knowledge, you might find that a couple of the chapters don’t really do much for you as there’s little focus on the model so much as the key concepts of red, green, refactor...
At any rate, one thing I did keep rolling around in the back of my mind is just how Base4.Net fits into the “domain model” picture ... it’s difficult to nail down, there are plenty of mechanisms for implementing most of what you need to create a domain model, for instance:
- Inheritance, though it’s support for discriminators in user types isn’t quite up to scratch – it only works via “ItemBase” at the moment – though I think Alex James mentioned that this would be implemented at some point... and though I haven’t tried, you could roll your own in some way.
- Aggregates (through extended properties).
- Various hooks (Events) to allow for the application of custom behavior, for instance you could wire up to a BeforeSave event on a type and implement some custom validation rules... not that easy to test though .
- A reasonable query abstraction - Good query support.
- A “logical” transaction mechanism, suitable for supporting the concept of a “UnitOfWork”, though it’s explicit, rather the implicit, and based on the examples in the documentation this could be a little annoying to work with when you’re trying to persist the entire graph for an aggregate – however using a similar implementation for “UnitOfWork” as Ayende does in the Rhino.Commons library for NHibernate I’m sure I could get it all working nicely, without too much trouble.
And it sounds like I’m on to a winner... but I think what I struggle with is that the types in your schema are not really the focal point for your domain model, because they’re not POCO, unlike say a domain model implemented with NHibernate as the backing O/RM can be, you can’t enrich or decorate them with additional functionality all that easily... It’s not that you have to build your domain model this way, it’s just that’s the way I would like to do it, at least to satisfy me that I'm not being railroaded into a bad design choice – but I think it’s the small blood price you pay for letting Base4 generate the schema assemblies for you, that loss of control is also a boon in immediate productivity when you start developing apps with Base4 apps... I’ve come close while using ActiveRecord, but it’s still not the same.
So... given the restrictions I’m left to implementing additional abstractions for my domain model... which means using repositories and services for encapsulating the business logic... which, in turn, brings me to mocking...
Mocking out my Base4 Implementation...
Now, if you recall a while back I talked about my repository implementation... basically it let you do things like:
IRepository<Order> orderRepository = IoC.Resolve<IRepository<Order>>();
Order orderForApples = new Order();
OrderLine greenOnes = new OrderLine();
OrderLine redOnes = new OrderLine();
orderForApples.OrderLines.Add(greenOnes);
orderForApples.OrderLines.Add(redOnes);
orderRepository.Save(orderForApples);
Big woop, but what I probably neglected to mention is that the repository is great for implementing a chain of generic decorators (which can be set up in your IoC container of course)... so at the bottom/base of the chain we may have our “Base4StorageRepository” and layered on top of that we might have various decorators (each injected with a dependency for the next repository in the chain) for implementing some useful concepts...
Things that spring to mind are:
- Security
- Validation
- Logging
Being able to configure these things is quite useful, and there’s little stopping you deploying additional decorators as additional assemblies for an already installed product – just throw in some additional container configuration - and it is a great deal more elegant then implementing this functionality with AOP.
But you do kind of paint yourself in a corner at the same time... this generic decorator pattern stops you from being able to decorate the repository with additional methods for implementing business level functionality (because any decorations that are applied to your base repositorty will mask out methods and properties no present in the IRepository<T> interface)... that’s fine though, I guess we just have to write the repository off as being more of a persistence mechanism, It’s really a logical separation of concerns anyway... you can decorate a "wrapper" at the top of the chain though (and this is how Ayende does, but lets ignore that for now ;o)
So... A higher level entity for dealing with the business level concerns is required... I call mine services, that may or may not sit right with you, but it makes reasonable sense in my application – and these service are injected as dependencies of the controllers re: MVC, yeah this is a Monorail app (or at least, part of it is)...
These services often aggregate the features of multiple repositories, like this catalogue service below which deals with a simple music structure:
public class CatalogueService : ICatalogueService
{
public CatalogueService(IRepository<Track> trackRepository, IRepository<Release> releaseRepository,
IRepository<Artist> artistRepository, IRepository<Genre> genreRepository)
{
if (trackRepository == null) throw new ArgumentNullException("trackRepository");
if (releaseRepository == null) throw new ArgumentNullException("releaseRepository");
if (artistRepository == null) throw new ArgumentNullException("artistRepository");
if (genreRepository == null) throw new ArgumentNullException("genreRepository");
_trackRepository = trackRepository;
_releaseRepository = releaseRepository;
_artistRepository = artistRepository;
_genreRepository = genreRepository;
}
In this case we have a dependency on four different repositories...
For this example we have a pretty simple schema... with a child-parent relationship between Track, Release and Artist... and a Many to Many relationship between Tracks and Genres...
Track -> Release -> Artist Track(s) <-> Genre(s)
The catalogue service implements the business rules for dealing with the catalogue, in some cases this is no more than querying the associated repository... so for getting a list of releases for a particular artist we have this (and yeah, I know it’s pretty daft):
public PagedItemList<Track> ListTracksForArtist(Artist artist, int pageSize, int pageNumber)
{
ObjectQuery query = new ObjectQuery(typeof (Track));
query.Scope.Add("Release");
query.Scope.Add("Release.Artist");
query.Path = (Track.Fields.Release.Artist.ID == artist.ID);
query.Path.AddOrderBy("Name", OrderByDirection.Ascending);
return _trackRepository.Find(query, pageSize, pageNumber);
}
I say daft because it doesn’t support sorting and filtering by a query... but it’s here to illustrate a point, and until the customer actually asks for these features I’m not going to bother building them :P
At any rate, the point is not to critique the service, but instead how can we test this catalogue service without being connected to a Base4 server... and of course it’s RhinoMocks to the rescue!
Mocking with RhinoMocks...
So here’s the guts of the test in mid-refactoring ... post red-green for those sticklers for the rules ;o) (there’s still plenty yet to clean up, but it would muddy the waters a bit for this example I think...)
[ Test]
public void ListReleasesForArtist()
{
Artist artist = new Artist();
PagedItemList<Release> releases = new PagedItemList<Release>(new ItemList<Release>(), 1, 10, 20);
Func<bool, ObjectQuery, int, int> callback
= delegate(ObjectQuery query, int pageNumber, int pageSize)
{
ObjectPath path = Release.Fields.Artist.ID == artist.ID;
path.AddOrderBy("Name");
Base4Assert.ArePathsEqual(path, query.Path);
Base4Assert.AreScopesEqual(new string[] { "Artist" }, query.Scope);
Assert.AreEqual(1, pageNumber, "pageNumber");
Assert.AreEqual(10, pageSize, "pageSize");
return true;
};
Expect.Call(_releaseRepository.Find(null, 1, 10)).Callback(callback).Return(releases);
_mockRepository.ReplayAll();
ICatalogueService service =
new CatalogueService(_trackRepository, _releaseRepository, _artistRepository, _genreRepository);
PagedItemList<Release> results = service.ListReleasesForArtist(artist, 1, 10);
Assert.AreSame(releases, results);
_mockRepository.VerifyAll();
}
Pretty chunky I know - but as more tests are added there will be opportunities for removing some of that duplicated effort... however the key points to take away are:
- We don’t need to have the base4 service running.
- We are actually testing the catalogue service’s interactions with the repositories, instead of relying on detecting expected side effects in the underlying storage.
- We’re verifying the object path and scope for the query, as well as paging information, and insulating ourselves from difficult to detect changes (like forgetting to apply an object scope, which may have a severe impact on performance).
Just to complete the story, the mock repository was created in the Setup (as we use it for every test case)... here's the code for it:
[ SetUp]
public void SetUp()
{
_mockRepository = new MockRepository();
_trackRepository = _mockRepository.CreateMock<IRepository<Track>>();
_releaseRepository = _mockRepository.CreateMock<IRepository<Release>>();
_artistRepository = _mockRepository.CreateMock<IRepository<Artist>>();
_genreRepository = _mockRepository.CreateMock<IRepository<Genre>>();
}
It doesn’t stop us from incorrectly spelling an object scope I think compile time query support for ordering and scoping of an object query will help to make this a little more robust... small potatoes.
The callback in this case is a bit of a “bad smell” – there’s support in rhino mocks for parameter constraints, but the object paths and scopes are a little too complex to test using the out the box ones... though you can get surprisingly close, they are pretty powerful... but I believe you can write custom constraints yourself – which is something I’ll do for the next post (I’ve never done it before, I'm guessing it's easy) and hopefully that will reduce the complexity of these tests quite a bit, and replace the less concise anonymous delegate, and more importantly, make it easy to develop the catalogue service in a test-driven manor.
For those more observant people you may have noticed the “Base4Assert” as well – that’s a little static helper class I’m using in these tests... it’s not perfect, but it works for simple cases including things like multi-level scopes and gives meaningful failure methods like “expected scope ‘Release.Artist’ but found nothing.” Or "expected OrderBy Name Descending, but found OrderBy Name Ascending"... which can quickly narrow down problems for you, especially if your writing these tests first (which is the whole point of this exercise I feel).
Conclusion... For Now ;o)
Last thing I’ve done is to have tossed away a lot of the additional query overloads in my original repository design in favor of just using a single ObjectQuery parameter with and without page number and page size (returning a PagedItemList<T> when providing a page number and page size, and returning an IItemList<T> when querying without them) – It makes everything a lot more... predictable, and is a lot cleaner when you start considering generic decorator implementations (10 overloads is 10 more code paths that need testing in a decorator... ) - looking towards to the future it should conceivable that a compile time query will allow you to construct the entire object query, not just the unordered path, and I’m quite happy to build these up in a few lines of code before passing them to a find method for now.
Edit (Sunday 12th):
In a similar vein, I just noticed Ayende's great MSDN article this evening - which covers that whole IRepository<T> story, including generic decorators and most importantly the intricacies of registering and chaining these components in the windsor container... great stuff!
 Wednesday, November 08, 2006
Nothing particularly interesting to report, while I'm sitting here watching a custom-built automated test suite chug away (which incidentally takes approximately 5 hours to run... at 100% CPU usage on 1 core... ugh) - I thought I'd muck around with Iron python and base4... my inspiration was this "On the fly" schema creation on the base4 site, however I wanted to just grab an existing assembly (from the server no less) and start using it's types in Iron Python... it's pretty easy (and quite cool, think about the implications of no references, every time you run your app the schema might have magically grown some more features ;o)
Getting Started...
First off, lets fire up the trusty iron python console... I like colours and being able to tab through member lists, so I'm going to throw in some command line parameters...
ipy -X:TabCompletion -X:ColorfulConsole
Cool, consoles up and running so lets get down to business... first things first, loading up the Base4.Storage assembly...
import sys
import clr
sys.path.Add("C:\\Program Files\\Base4 Solutions Ltd\\Base4 version 2.1.1.85")
clr.AddReferenceToFile("Base4.Storage.dll")
from Base4.Storage import *
All done... now before we can do anything useful, we'll need to setup our default connection string (read: default context)...
StorageContext.SetDefault("tcp://Server:@localhost:999/Base4_default")
This is the amiga speaking...
Now the fun begins... if you have System.Speech available, grab that puppy too... because deep down we all know that the thrill of making your computer talk still exists... especially if you started out your days on an Apple or better yet Amiga...
clr.AddReference("System.Speech")
from System.Speech.Synthesis import *
synth = SpeechSynthesizer()
Sweet, now lets view (or listen) for all the available schema assemblies...
for asmFile in StorageContext.FindAll[SchemaAssemblyFile]():
synth.SpeakAsync(asmFile.Name)
print asmFile.Name
Loading the schema...
At this point you'll end up with a list of the assembly files (and hopefully, your machine will be droning away notifying of you just what they're called... ;o) in my case I have a little "Experiment.dll", so lets load that up... I'm pressed for time so I won't be putting in speech detection for selecting the appropriate schema, though it's both entirely possible, and pretty easy to do...
asmFile = StorageContext.FindOne[SchemaAssemblyFile]("Name='Experiment.dll'","")
assembly = experimentAssembly.LoadAssembly()
clr.AddReference(assembly)
from Experiment import *
And yes, the strongly typed schema is now there for me to play with...
The experiment schema contains among other things an "Order" type, which holds a collection of "OrderLine" line's... so we can now do this:
order = Order()
line1 = OrderLine()
line1.UnitCost = 30
line1.NumberOfUnits = 2
order.OrderLines.Add(line1)
order.Save()
I think it makes quite a handy diagnostic tool, especially when you start considering that your dev, test and live environments may all have different schema versions... it could certainly save a lot of stuffing around - it would be fairly tedious to repeat this exercise in C# by comparison (especially once you've written a few useful helper functions in your own "base4" python module... I could shorten this entire post to 3 or 4 lines instead).
 Friday, November 03, 2006
IronPython on ASP.Net ?
Yes, that's right... dynamic language support in ASP.Net ... and some interesting things I didn't know existed in ASP.Net already, such as no-compile pages - well worth a skim through the whitepaper here (http://www.asp.net/ironpython/WhitePaper.aspx?tabid=62) - it's an interesting approach to bringing in dynamic language support to asp.net - and would certainly work for ruby on the CLR as well... probably better actually because ruby's statements are balanced and so you have less problem with whitespace.
Speaking of the deadly space that is white, It will be interesting to see if they've dealth with this horrible python integration issue at all...
I haven't seen any evidence of whitespace agnostic additions in the sample code that's been posted, so I'm guessing not... in which case there may be tears before bed time for some developers who want to do quick-and-dirty asp-like code (which I see is one of the advantages of using a dynamic language in this situation) ... As it all tends to blow up in your face when your tabs are slightly out of line - something which visual studio might just do for you when it feels like shuffling your html ;o)
I'm not sure how easy it would be to introduce a whitespace agnostic mode in IronPython for ASP.Net (Boo's whitespace agnostic mode, as used in Brail is quite elegant... the alternative is to do something like the spyce framework, using opening and closing braces... which just looks mucky to me because you end up with stuff like this:
[[ if display:{ ]] <strong>something here</strong> [[ }else:{ ]] <strong>nothing here</strong> [[ } ]]
Seems a bit too noisey... the "if display:" is enough to know you need to locate a matching closing block, so adding a construct like "end" should do the trick... which is how it's done in Brail...
<%if display:%>
<strong>something here</strong> <%else:%>
<strong>nothing here</strong> <%end%>
As clean as...
 Thursday, November 02, 2006
Castle goes RC2
Well it's an exciting week for Castle users, RC2 is out the door, and with it a new website look and feel with up-to-date content, and most interesting to me is the introduction of a new entity "CastleStronghold" a commercial venture, run by Hammet which offers professional support for developers and organisations implementing solutions in Castle, including guaranteed response times to inquiries and access to additional skilled development resources.
Personally Castle Stronghold is great news, as a developer for a company which has been using the Castle IoC for over a year now it adds a certain weight to our decision to run with this technology, and I think it offers a clear indication of the longevity this technology and product has... In time as our customer base grows it certainly looks appealing to have guaranteed support backing us up - and also helps customers to understand it's not some half backed open source project that's just going to fizzle out one day...
The RC2 release itself is exciting from a community point of view, it should be better then ever for people to pick up and play with this stuff - though we generally use interim snapshots of the trunk (ie. the last trunk release that didn't break our build...) so we've been exposed to most of the features for a while now, albeit without alot of the bug fixes ;o)
IronPython
I've been a bit quiet on the IronPython front... which is mostly because I haven't had the time to play around with it much lately - however I'll try to finish off my look into IronPython as a scripting engine, and in particular the good and bad aspects of getting it to play with your .Net code... In the mean time a collection of useful IronPython links is slowly growing here: http://ironpython-urls.blogspot.com/
Base4.Net
A week into using the Base4.Net's latest release, with compile-time query support, I'm loving it... it definitely gives a huge leap in productivity and expressability (if that's even a word ;o) - looking forward to seeing these features rounded out at some point (with support for scopes, projections and ordering) - Alex James is talking about migrations (my "most-wanted" feature) and it's looking encouraging, it's where I feel the most pain at the moment as base4 isn't particularly friendly when you try to approach the problem of building your schema in a YAGNI fashion - there is quite a bit of pain involved in adding and removing properties from types during development as requirements are refined.
The rub is that the current "on the table" solution will require restarting the base4 service to apply the migration... which at the moment means restarting the windows service with a different command line... or more likely, stopping the service, then starting the standalone server with the right command line arguments, waiting till it's done, stopping the standalone server and starting the service again... I was originally thinking of implementing these as a Nant or MSBuild task (much like RoR's migrations work with rake) but I think it might be a bit chunky... I need to think about it a bit more.
Splicer
I haven't forgot about my little side project (Splicer is a library I've written for "attempting" to eliminate the pain of using DirectShow.Net to encode audio and video) - and I do intend to keep updating and supporting it as-needed ... next release should see WinForm samples for encoding audio and video added, and updated code examples... after that I might review the implementation a bit, to see how effects and transitions can be made easier to use via relative times - I'm interested in DSL's at the moment, so maybe I could create a DSL for video editing ;o)... if nothing else it would be amusing... I've yet to establish if anyone actually uses the library yet (other than myself of course).
 Tuesday, October 31, 2006
Though I knew you could do it, I never have till today... and that's supplying a type for a generic parameter in another type, which is registered in a container... Of course I didn't have to do it that way, but it it kept the configuration a little thinner.
Basically I have some simple WCF services I wanted to host in a container.. here's the service's interface...
[ServiceContract(Namespace="http://localhost/schemas/testrunner/", SessionMode=SessionMode.Required)]
public interface ITestRunner
{
[OperationContract]
Guid StartTestRun(Guid testSuiteId);
[OperationContract]
TestRunStatus GetRunStatus(Guid testRunId);
}
To get this to work for my implementation I had to do one thing, which was set the InstanceContextMode to "single" for the service implementations behaviour, otherwise the service host would die when I tried to pass in an instance (it expects a type for any other mode)... I haven't dug very deep into WCF, but it would be nice if they supported a mechanism for supplying a component activator instead...
[ ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TestRunner : ITestRunner
{
Now, I build a component for hosting my service... it implements IStartable...
public class HostingWrapper<T> : IStartable
where T: class
{
private ServiceHost _host;
private T _service;
private ILogger _log;
public HostingWrapper(ILogger log, T service)
{
if (log == null) throw new ArgumentNullException("log");
if (service == null) throw new ArgumentNullException("service");
_log = log;
_service = service;
}
public void Start()
{
_host = new ServiceHost(_service);
if (_log.IsDebugEnabled) _log.Debug("Opening ServiceHost for service: {0}", _service.GetType());
_host.Open();
if (_log.IsInfoEnabled) _log.Info("Opened ServiceHost for service: {0}", _service.GetType());
}
public void Stop()
{
if (_log.IsDebugEnabled) _log.Debug("Closing ServiceHost for service: {0}", _service.GetType());
_host.Close();
((IDisposable)_host).Dispose();
if (_log.IsInfoEnabled) _log.Info("Closed ServiceHost for service: {0}", _service.GetType());
_host = null;
}
}
And then you just need to regsiter it in the container's configuration:
<components>
<component id="testRunnerService.default"
service="BoatsForGoats.Services.Testing.ITestRunner, BoatsForGoats.Services"
type="BoatsForGoats.Services.Testing.TestRunner, BoatsForGoats.Services" />
<component id="testRunnerHost.default"
type="BoatsForGoats.Services.HostingWrapper`1[[BoatsForGoats.Services.Testing.ITestRunner, BoatsForGoats.Services]], BoatsForGoats.Services" />
About the only tricky thing is that I guessed (incorrectly) that I would only need a single set of square brackets around the generic parameter.
 Friday, October 27, 2006
Well I blew up my feedburner yesterday... my RSS feed was set to
max out at 50 items, which, with my love for pasting html-bloated
source code was well over the 256K limit.... but feedburner never
bothered tell me it was dead... only noticed today... at any rate, I tried resetting the size
to a max of 6 posts in the feed... and pinging it... but it was still dead, so I did a resync...
still no joy... In the end I found that I had to turn off feed burner support in das blog and then resync the feed.
Moral of the story, don't bloat your feed (It was bad form at any rate, especially in bandwidth starved New Zealand ;o)
If you can see this post it's all working again happily...
 Thursday, October 26, 2006
Well lots to blog about, but no time to do it... at least not
today - at any rate I just thought I would say if you haven't
considered using Base4 up until now, why not give it a go with the latest drop
:) I'm loving these new changes with the support for Compile-time query
handling... I think at this point you can start prototyping stuff with
base4 very quickly, between the UI for designing or discovering an
existing schema, and now the ability to avoid learning the ObjectPath
query syntax it's pretty compelling...
At any rate, as mentioned in the various posts from Alex James, you can now write these kinds of queries:
IItemList<FileBase> matchingFiles = StorageContext.Find<FileBase>(FileBase.Fields.FileSize > 1024000 && FileBase.Fields.MimeType == "audio/wav");
Faboo, and of course you can drill through the relationships:
Track track = StorageContext.FindOne<Track>(Track.Fields.Name == reference.TrackName
&& Track.Fields.Release.Name == reference.ReleaseName
&& Track.Fields.Release.Artist.Name == reference.ArtistName);
This is for a simple hierarchy, where we have Artists-> Releases
-> Tracks (sadly organising Music in reality isn't quite this
easy...)
But as you can see these queries are getting quite verbose, personally
this is where the elegance of this approach comes in... reuse is
possible, for example - we can build a static class to hold common
queries:
public static class FileBaseQueries
{
public static ExpressionField LargerThenOneMegabyte
{
get
{
return FileBase.Fields.FileSize > 1024000;
}
}
public static ExpressionField MimeTypeIsAudioWav
{
get
{
return FileBase.Fields.MimeType == "audio/wav";
}
}
}
And now the first query could be rewritten:
StorageContext.Find<FileBase>(FileBaseQueries.LargerThenOneMegabyte && FileBaseQueries.MimeTypeIsAudioWav);
The main thing I like about this approach is your queries start looking
like sentences... "Larger Then One Megabyte and Mime Type Is Audio
Wav"...
Another idea might be to use a method on the static class instead...
public static ExpressionField MimeTypeIs(string type, string subType)
{
return FileBase.Fields.MimeType == string.Format("{0}/{1}", type, subType);
}
And then writing something like:
StorageContext.Find<FileBase>(FileBaseQueries.LargerThenOneMegabyte && FileBaseQueries.MimeTypeIs("audio","wav"));
At this point it still reads quite nicely, but it's a little more flexible...
Edit: I just noticed this entry wasn't actually marked as syndicated... fixed it now.
© Copyright 2009 Alex Henderson
Theme design by Bryan Bell
newtelligence dasBlog 1.9.6264.0  | Page rendered at Tuesday, January 06, 2009 6:59:46 AM (New Zealand Daylight Time, UTC+13:00)
|
Search
FeedCount
Tags...
|