ProfileWell, I thought it was i...PhotosBlogListsMore ![]() | Help |
|
|
September 29 Two colors redIf you take a quick look here you’ll have read an interesting article about “well known values” of common types. One example Abhinaba gives is Color.Red and new Color(255,0,0), which he says are “the same”. Moving swiftly over the fact that he meant Color.Red and Color.FromArgb(255,0,0), what about that equivalence claim? Well, I suppose it depends how you define equivalence. If you use either of those colors to construct a SolidBrush and paint a nice big rectangle on the screen, then they’ll both look the same. So, in that sense they are “the same”. But try running this little console application, and examine the output. using System; using System.Collections.Generic; using System.Text; using System.Drawing;
namespace TwoColorsRed { class Program { static void Main(string[] args) { Color red1 = Color.Red; Color red2 = Color.FromArgb(255, 0, 0); //Color red2 = Color.FromName("Red");
if (red1 == red2 ) { Console.WriteLine( String.Format("Red1({0},{1},{2},{3}) and Red2({4},{5},{6},{7}) " + "are the same...the Universe is safe", red1.A, red1.R, red1.G, red1.B, red2.A, red2.R, red2.G, red2.B) ); } else { Console.WriteLine( String.Format("Red1({0},{1},{2},{3}) and Red2({4},{5},{6},{7}) " + "are not the same...call the ChromaCops", red1.A, red1.R, red1.G, red1.B, red2.A, red2.R, red2.G, red2.B) ); } Console.ReadKey(); } } } Delve into Color.Equals() or Color.op_Equality() (operator ==) and you’ll find that things are not quite so simple. In fact, both of these methods of determining equivalence also check other properties. First, there’s the state of the color. Did you know that not all colors are actually useful? If we pass an unknown name to Color.FromName(), then you still get a Color object back – it just isn’t valid. Then, it checks whether the colors in question are well-known or not, and also compares by Name (which is a read-only property, so you have no real control over this). So, when it comes to our first example Color.Red is a known color, but Color.FromArgb(255,0,0) is not; and anyway, their names don’t match either. So, why would I care? Red is red, isn’t it? Well – there are often good reasons for trying to compare two colors – trying to find a match in a fixed palette, or looking for similar pixels in a bitmap region, for example. You need to check the (A)RGB values by hand, rather than using the seemingly-handy comparison functions. This is a more general issue, both for API designers and consumers. From the API designer’s point of view it is recommended that, if you are providing equality overrides, that your comparator should perform an identity-like compare. The Color.Red and your Color.FromArgb(255,0,0) are clearly not the same color, in an identity sense – they’ve got different names for a start! However, from a consumer’s point of view, you often don’t want an “identity” comparison – especially with value types. More often than not, you need a semantic comparison appropriate to your application (like comparing the color values). And you have to write that yourself. If it is a very common comparison to perform, then you might be tempted to ask the API designer to roll it into the class concerned (especially if you are the API designer, too), but you should be very careful before deciding to do so. You don’t want to confuse the situation further by adding an army of subtly different comparison functions to a simple class or struct – especially one like Color. Tricky fellow, equality. October 25 VacanciesI've got three open vacancies at the moment - one for a C# Dev, one for a Dev/Test and one for a junior SQL Server DBA. Ideally, you've got some smart client experience, and some understanding of real-world problems in highly distributed, multi-enterprise systems. You're probably interested in 2D graphics and imaging, too.
If you're interested in coming to Cambridge (England) and working in the healthcare space for a fast-growing, highly motivated team, who are able to make a real difference to people's lives, then I'd love to hear from you.
The only proviso is that you'd need a valid working visa for the UK (or you're already an EU citizen, of course).
Drop me an email if you're interested, or go via our website (www.digital-healthcare.com).
Team SystemWe've recently moved on from the "experimentation" phase, and we've kicked off a small project to trial Visual Studio Team System properly. This has been up-and-running for a few days, and I thought I'd provide some initial comments.
Scoping
The project has a team of 9 devs, 3 QA, a Business Analyst and a PM. Its aim is to produce a shrink-wrap product for a particular vertical, based on a large existing source-code base. For a team of that size, we picked a single-server solution and specced out a quad-Xeon box with 4Gb RAM and a RAID5 disk array to support it.
We decided to stick with our current build and test system (NAnt, Draco, NUnit2) for this particular project, as we are still targetting DotNetFX1.1 and the pain of migration would be too high.
However, we're experimenting with a migration of a snapshot of the source tree from CVS into TeamSystem source control. Some initial experimentation reveals that it is possible to side-by-side these two source control systems without too much pain. Both being merge-based means that you have to pick which system is actually going to do the merge for you - and you can then see if they disagree! You can then commit the results into both.
Our build-and-test server lives on another box in the domain, and continues to check out from CVS, which itself lives on another server.
For this project, we're going with an out-of-the box MSF Agile process. We want to find out how this differs from our standard processes and documentation, and to determine how much we want to change v. how much we need to change it.
Installation
The most important point is that you've *got* to follow the instructions *to the letter*. The installation checklists provided in the TFS Installation CHM are nice and accurate - but you must follow them precisely; it is very sensitive to variation. We ran into a couple of problems during installation - it turned out that the "system healthcheck" can be a bit misleading. Our 4Gb, quad-Xeon box had "insufficient memory" and "insufficient processor speed". Ignore those warnings and carry on... We also dismally failed to install SQL Server on the original x64 edition of Win2k3 we had installed on the box. We repaved with the x86 build, and everything went swimmingly.
The installation instructions will take you right through from "the CD has stopped spinning from my Win2k3 server install". It is better *not* to have performed any of your standard application server configuration and lock-down before installing TFS; it has some particular IIS requirements (no FP Server Ext for example), and if your IT guy has prepped it to some "standard" configuration it might be tricky to get things going. Better to perform post-installation lock-down after you've proven to yourself that the installation is working.
I also found that it can be a bit tricky to set up your first administrative user; the TFS instructions are a bit sketchy about where to find the correct places in Reporting Services and SharePoint Services to configure the site admins. It is well worth reading the admin guides for those products too, and familiarize yourself with the infrastructure first, because you *have* to get it right before you try to create your first TFS project.
Why is that?
Well, it turns out that some irreversible database commits occur in TFS (e.g. source control) before the SharePoint site is created. While it tries to roll-back from most failures, under some circumstances, you can end up with a SharePoint site creation failure that leaves an orphaned, invisible TFS project that the TFS delete tool can't remove (even with the -force option), but which prevents you from reusing that project name again. It is possible to unpick that by hand in the SQL Server - but that's a painful afternoon's work and only marginally better than reinstallation!
Setup
Creating the new TFS project is very straightforward, and almost completely automatic. I was working inside VS2k5RC, but any Team Explorer instance will do. Unfortunately, adding team members to a project is mind-numbingly awful; you have to add them to TFS, SharePoint and Reporting Services. The UI is completely different for each of these tasks, and the conceptual models don't exactly map neatly onto one another. On the plus side, there is a handy chart indicating the privileges you need to apply in each of the application servers for each user role, and more-or-less where to apply them. Again, it is useful to understand the admin model for each server before you do this, rather than just blindly following the instructions.
For even a 15-person team, this is somewhat manual and therefore error-prone. I'm hoping for some automation in this area by RTM.
Having created a project, there are a bunch of Work Items already waiting in the system, and assigned to the project creator. Some of these are around source migration and policy, some are around actual project set-up and inception. These all point you at the project guidance, so it is pretty straightforward to get going.
Project Inception
I was slightly disappointed with the Persona and Scenario templates available in this version of MSF Agile, but pleasantly surprised by the Vision document. In our regular process we usually create a "Scope" document which lives alongside the "3 paragraph" Vision and the detailed elaboration of Personas and Scenarios; it helps us keep an eye on policy around what is in- and out-of-scope; this is chiefly to avoid feature creep, which is the dark side of an agile, scenario-orientated project. The Vision template was easily understood by the BA, and the "Persona" document also offered a reasonable template. The scenario templates are a little light on structure, and we will probably want to enhance it a bit for the next project. One quite powerful feature is the ability to create work items with associated "type" information. For example, you can create a "Scenario" work item to have someone elaborate a scenario. This takes a little getting used to for people familiar with a "bugzilla"-type "one-size-fits-all" approach.
Out-of-the-box there are a couple of MS Project plans on the SharePoint site - one for test and one for implementation. As a result of a bug in Beta 2 it is unnecessarily tricky to use these - TFS cannot integrate with the Read-Only SharePoint files and throws an exception when you try to push your selected work-items into it.
Incidentally, mapping from work items to project plans or Excel spreadsheets is not really mentioned in the getting started guide (you actually have to read the docs properly; what's that all about?), but it is the core of the TFS management process. You just select a bunch of work items in Team Explorer and you can then insert them into a given MS project file. You can then "publish" your changes to TFS (as distinct from publishing the document to SharePoint), or get updates from TFS.
To get around the Beta 2 bug, the Known Issues document suggests that you have to Save As... the MPP to a local disk. In fact, I've found that you have to Save As..., quit Project, restart, reload the file and *then* push the docs into it. Obviously, you have to replace the document on the SharePoint server when you're done.
You can happily add your own structural elements, milestones etc. to the project plan, which survice round-tripping. You do have to be a bit smart about duration and start time, as these can easily be lost when publishing and updating - I've not quite worked out why that happens yet, as it isn't entirely predictable.
Source Control
Source control has moved to a merge-model by default. This is a good thing, in my opinion, as we are used to this from CVS, and we find it very flexible. You can enforce various rules, such as code review, FxCop validation, and one-work-item-per-checkin. All of these are part of our standard process, so we just turned them all on. And then turned the FxCop validation off again when we realized that it didn't really work unless we were using VS2k5. Unfortunately, the source control system still has the same difficulties as Visual SourceSafe when it comes to getting the repository-tree to reflect the file system tree. Because it is integrated with the Solution System, it has a tendency to make up its own folder names, and ignore a more complex, multi-solution on-disk structure. I would have thought that this was common with the kind of large software projects at which TFS is targetted, so this is a bit frustrating.
The most important limitation is that you cannot check files in at the root of source control repository - you must create a folder below this to check items that live at a solution root.
This is nothing you can't work around, but it is more taxing that it needs to be. An explorer-integrated source control system (like TortoiseCVS/SVN) is, IMHO, superior to an IDE-integrated system, particularly when you move to a merge-based model, and there is no longer a correspondence between touching a source file and "checking it out".
Non-developers
It is a shame that there is no easy way of accessing and managing your work items from the SharePoint portal; for non-dev/test team members, that's the only reason for having the Team Explorer - everything else they do is essentially managed through the portal.
So far, so good... September 02 Installing Visual Studio 2005 August CTPWhen installing VS.NET 2005 August CTP on a machine with a previous CTP installed, the MSDN download page has a set of instructions listing the components you need to uninstall (and a couple of MSI GUIDs in case of uninstallation difficulties).
There's one item they left off the list - don't forget to uninstall the Microsoft Document Explorer 2005 in addition to the rest. June 10 XML Messaging, Indigo, Object MappingThere seem to be two camps in the world of SOA - those who want to live somewhere near the top of the stack (using ASMX or Indigo's service-level "object mapping"-like technologies), and those who live nearer the XML messages themselves. I'm firmly in the latter camp - and I've got two justifications. But first, I'd better make clear a couple of assumptions. I am assuming that the benefits of service-orientated architectures lie fundamentally in the asynchronous exchange of well-defined messages to request well-defined semantic services from providers residing at well-defined end points. Secondly, I am assuming that we are talking about designing and building services, not consuming them. I'll take each of those italicised points in turn:
None of these points require any particular implementation technology, platform, language or infrastructure, although, in general, we think of the WS-* standards, and stacks like ASMX or WSE over network technology like TCP/IP. I've recently had to implement a message-passing application based on eBXML (don't ask!). eBXML is largely SOAP based, and requires a stack analagous to the WS-* bits; very similar in many ways, but completely incompatible. But susceptible to a service-oriented analysis nonetheless, and susceptible to the same arguments. So, on that basis - on with the justifications. Justification the first In real-world service-orientated designs, there are two things which are critically important 1a) Getting the granularity of the services right 1b) Getting the granularity of the messages right "Right" in this case can mean a number of things. Appropriately coupled, appropriate amounts of information being transferred, appropriate for reuse. It has got a lot to do with both the semantics of the service, the expected consumer model and the deployment model. Abstracting away from the actual message design makes this much harder to get right, apart - possibly - from the expected consumer model. Unless the message payload is very simple (and the canonical calculator example is not very "real world"), then imposing an object-mapping on service designers seems overly (if not dangerously) restrictive, even if an object-mapping might ultimately be useful to a consumer (although that should be the consumer's decision). This is not to say, then, that service consumers shouldn't be presented with an object mapping. It's just that providers and consumers rightly have a different view of the world, and the message-level design is more useful to the service builder. Justification the second Having got the service design "right", it is important to consider how a message-passing design impacts on the rest of your architecture. You have to assume that messages will get lost in the post, go missing, get routed to the wrong place. You have to assume that messages that you thought were lost will turn up unexpectedly weeks later. You have to assume that malicious individuals will try to route messages unexpectedly. Not all of this is simply a "plumbing" problem. To sort out the real-world semantic implications of these kinds of async message exchange problems, you often need to involve end-users with domain knowledge to help sort things out; ideally these are not "sysadmin" type people, but real domain users who are prepared to deal with the semantic results of an infrastructure problem. If you don't actually control or understand the actual message layer of your infrastructure, it is very difficult to come up with a sensible design for that part of your application. This isn't a problem that can be mitigated by a general, horizontal solution - it critically depends on the nature and intent of the messages. In the eBXML example above, for instance, presenting views on the "missing" or "unexpected" messages, often aggregating several together for presentation to determine what to do (accept some or all of the implied semantics, modify, resend, carry out other function x,y or z...etc...etc...) If you've lost (or never really understood) the granular nature of the services you define, implement and deploy, it is very hard to get this kind of resolution right. If you don't address these problems, then your applications will be brittle and prone to intermittent, difficult-to-resolve problems. So, that's why I'm in a "design your messages, think about message interactions, and only provide object-mappings to consumers who need them" camp. Come and join me. I'm toasting marshmallows on a stick over here. June 08 PDC and a pleaGosh. Doesn't time just fly. This morning I opened my desk drawer, and saw the shiny black PDC 2003 highlights DVD at the top of the pile. Minutes later, I was registering for PDC05 in September. If you've not been to a PDC before (and I've only been to one, and can therefore speak with great wisdom and sagacity, oh yes), and you're wondering whether you can really justify going - then wonder no more! It really is a must-attend event. Go on, tell your boss I said so. Although PDC03 was cool - because we got a first look at Avalon and Indigo - it really just hammered home the "you want to think about moving to .NET, and soon" message. There was plenty of groovy stuff, and the vision was exciting, but it wasn't (yet) a coherent story. You were "there at the beginning(tm)" but that didn't perhaps, fully justify the cost of attending in and of itself (although there were plenty of other things that did). So don't beat yourself up that you missed it. What about 05? I'd be willing to bet that we don't quite have a coherent future story at PDC05, either. But I reckon it will be mostly coherent, and the apps that we build in 2007-8 will recognizably be of the same breed as the technology presented at the conference. Also, there's important stuff of the "very nearly here and now" that was only touched on at the last PDC. Team System is probably the most significant, but in-depth sessions on .NET 2.0 technologies are going to be as important (if not more important) than the futurology. And that's only the stuff that we know about. PDCs have always seemed to have a surprise. (Even if it was only the surprise of "What? That's it?!" Hailstorm PDC in Orlando, 2001 But - what if you don't get to PDC? I really hope that MS UK repeat the fantastic job they did in '03, running a local "Not the PDC". While I get to go and be threatened by bored immigration officials at LAX, the folks in my team in the UK don't, and they benefitted enormously from the "home grown" conf-ette last time around. April 23 The Woe and Beta 2Well - the woe still exists in VS 2005 Beta 2... And it is still intermittent. The occasional appearance of the classic "open the solution having closed it with a designer window open" problem. You see the (kinda to be expected) error window indicating that some dependencies are missing - but the designer round-tripping still deletes the code referencing the "missing" components. (Note - I think that VS.NET Beta 2 is otherwise a work of genius. Just the rename-refactoring smart-tag makes it worth the entry price) April 01 Component.Dispose()Last week I posted about Component.Dispose(). MS have now resolved the bug I reported. Their reasoning is essentially that:
I agree with (1). (2) and (3) make me uncomfortable. It doesn't surprise me that they were left with InitializeComponent in two halves; and clean-up is the flipside of intiialization, so why don't they look the same to the end user? If the Disposed event is the way to deal with clean-up, why didn't they create an Initializing event to handle bootstrapping? March 25 Back to the threads again...I've made a couple of posts in recent months on when to avoid using threads, and why it is so difficult to use them correctly. But I haven't really posted anything about when when we will really gain any benefit from multithreading. There are several conversations on this subject around at the moment, and we were also having a discussion in the office, so I thought I'd distill my thoughts into this entry. First, the parameters for this discussion have changed in the last twelve months. Until recently, I'd have said that there's nothing intrinsically "multithreaded" about the average user's PC. They tended to have a single processor, executing instructions in what amounts to a one-at-a-time scheme (ignoring implementation details like pipelining). Windows, on the other hand, very kindly gives us an environment which looks like it is executing more than one instruction at the same time. It isn't, of course. It is faking it, and the overhead to create that environment is huge. (Probably huger than it needs to be, but I think we're stuck with that legacy. Longhorn has some stuff to help improve matters, in terms of execution guarantees, but still...). Thread context switches can take forever...almost literally, although on the whole they take order of a few 100s of usec or so. But a few 100usec - that's a heck of a long time in the modern world. And if that's an overhead on top of our actual processing work, how come we ever see any benefit from multithreading? Well, it turns out that most of the time our processors just aren't doing anything much at all - except servicing a bunch of context switches amongst threads blocking and waiting for something to happen. But, as I mentioned, things are a bit different now. Oh yes. We've got *multiple* processors (or processing units masquerading as separate processors in the case of Intel's rather nifty HT technology). And they're *all* mostly servicing a bunch of context switches amongst threads blocking and waiting for something to happen. Oh, Brave New World. So, how can we take advantage of all this idling? Well - if we've got an operation we want to carry out that can be decomposed into several independent, parallel operations, each of which involve waiting for something to happen where we're *not* using the processor very much (such as local disk IO - loading a big file for instance; or network traffic - like talking to a remote database or service), then each of those subtasks is a good candidate for being pushed out onto a separate thread. Making several database or web-service calls, and then processing the results. Each database query or web service call could be executed on a separate thread, and then the results aggregated by the controlling thread when all the results are in. Of course, there's the cost of setting up and tearing down multiple connections, and that might swamp your actual execution time, so you should still measure before implementing this kind of optimization, but the potential benefits can be huge. But there comes a limit where the cost of scheduling the threads + the actual work being done on the threads equals the amount of processing power we have available. At that point, it is no longer beneficial to spawn another thread, because there isn't any more processor grunt available, and - worse - we're going to have to pay that extra context-switching overhead. That's why we use *thread pools*. We limit the number of threads that can execute at once, based on a heuristic which estimates the average amount of processing vs. blocking we're doing on any given thread. .NET has a couple of thread pool implementations internally, like the one we use when doing Delegate.BeginInvoke()/Delegate.EndInvoke(). While heuristics used for the built-in pools are not going to be ideal for *all* situations, they're probably right enough for most. We should have strong, metric-based evidence if we think we need to change those rules, by implementing our own pool, or using the new 2.0 APIs which let us tweak the threadpool limits. 9/10 apps say they prefer the system defaults. By those ratios, mine probably isn't the one, and yours probably isn't either. But don't forget that starting/using other threads is not the only option. For example, back in the day, we used to have the idle processing in MFC apps... (We've still got it now in .NET, but this cooperative multi-threading has fallen out of favour, mainly because of uncooperative usage models!) How did this work? well, the main UI thread spends most of its time blocking waiting for a message to arrive, because something needs repainting or the user moves the mouse or whatever. But it spends a looooong time blocking, and a very short (but intensive) time dealing with the message. So, MFC's message loop implemented a call to an OnIdle() method. We could (quickly) do a bunch of background work and it would appear to happen "in the background", without interrupting the main business of the UI thread. Actually, there was no special "multithreading" magic going on. We were just calling a bit of code "between messages", taking advantage of that huge amount of idle time. But it *looked* like multithreading magic, by making *better use* of the time we spend just waiting for something else to happen. And no locking problems, either. Of course, you have to be cooperative to make a cooperative scheme work, and that's often where this falls down. So, to sum up. 1) Multi-threading is useful if the total amount of actual processing work you want to do at any moment is within the capacity of your processing units, otherwise you're just adding overhead and it would be better to queue the work on a single thread and blitz through as fast as possible 2) Multi-threading is useful even on a single processor machine *because* most operations involve a lot of hanging around waiting for non-processor-intensive activity to occur (like network traffic, IO etc) 3) Multi-threading is useful if you've got multiple operations that must appear to the client to occur concurrently - like UI responsiveness and "anything else" 4) Thread-pools are a technique used to help keep your multi-threaded activities within the bounds of common sense. Mess with the built-in common sense at your peril. 5) Using idle time effectively doesn't necessarily mean starting another thread. There are other patterns available. March 08 Why VSTS?What's the big deal about Visual Studio Team System? After all, we've got NUnit, NAnt, CVS/Subversion/SourceSafe(yak),Bugzilla,MS Project... One word - integration. MS' killer secret to success. I've just spent a couple of days planning our next software iteration. The team has spent time triaging candidate bugs in our bugzilla-based database, making risk assessments, implementation plans and estimates. I've worked those into our iteration project plans (for release on two project branches, with 3 SKUs of each), correlated them with deployment plans from the applications team, used my magic linking macros to hyperlink from project to bugzilla and back. (I'll be glad when we complete a hire for a new Program Manager, and this isn't my problem any more! The most time consuming parts of all of this are just boilerplate: transferring data from one system to another. Even with an internal investment in integration, it still requires a lot of manual overhead. We'd have to spend a small fortune in developer time to get these tools to play seamlessly together. VSTS promises that integration "out of the box". But what about the existing investment, and the transitional costs? I'm thinking hard about what the right decisions are going to be for my business, what my return on investment will be short, medium and long term. I'll keep you posted on what we do, and why. January 31 FxCop and value typesFxCop has an excellent rule which encourages you not to do this kind of thing: if( foo is Bar ) { ((Bar)foo).DoBarStuff(); }
but to do this kind of thing instead. Bar bar = foo as Bar; if( bar != null ) { bar.DoBarStuff(); }
The rationale for this is that example one involves two casts, whereas example 2 involves just the one (and a simple reference test for nullness).
The snag is that FxCop moans at us when we do this with a value type.
The difference is that you can't use the as operator to determine the type of a boxed valuetype, and if you try the "cast and test" approach, you are no longer dealing with a test for nullness, you're catching an InvalidCastException, which is (usually) even more painful than the double-test.
I think that FxCop is being somewhat agressive here, and the is...cast approach is appropriate for value types. Unless you folks have a better idea? January 14 Close Design ViewsIanG (thanks for the plug!) pointed out that I was being v. lazy by not posting the handy macro code I alluded to earlier, which closes the designer windows. So here it is. Paste it into a new macro project. Imports EnvDTEImports System.DiagnosticsImports System.Windows.FormsImports System.ComponentModel.DesignPublic Module CloseDesignViews Sub CloseDesignViews() For Each win As EnvDTE.Window In DTE.Windows If TypeOf win.Object Is IDesignerHost Thenwin.Close(vsSaveChanges.vsSaveChangesPrompt) End If Next 'DTE.ExecuteCommand("Debug.Start") 'DTE.ExecuteCommand("Build.BuildSolution")End Sub End Module This just enumerates all the open windows in the IDE (including the toolbars) and works out whether any of them are IDesignerHosts, and closes them, giving you the option of saving them as you go along. I've included a couple of commented-out lines that use DTE.ExecuteCommand(), to tag on something else after closing the designers - the two most useful are Build.BuildSolution and Debug.Start (equivalent to...well...building the solution; and the build/run command bound to F5)
|
|
|