One of my goals for the year was to speak at least three times this year and from the looks of things, it appears I’ll meet that goal in the first half of the year.
This is what’s going on this year for me (subject to additions/changes):
RMTT (hash tag #rmtt) will be the last weekend of February. I’m going to be doing a Birds of a Feather presentation. From what I understand, the BoF presentations will be at 7AM for one hour. I will be facilitating/leading a group discussion on automation tools. Here is the abstract:
Build/Deploy Automation & Developer Automation Tools This will be an open discussion about build/deploy and developer automation. In the .NET world, there has been a serious need for better tooling in this area and we can discuss the needs/deficits. If the conversation steers toward demos, we can take a look at a build tool called UppercuT, which is the fastest zero to professional build you will find in the .Net market to date (plus it's 100% free!). We can also look at a database change management tool known as RoundhousE. Another tool that may be shown is a project (solution and everything else) templating tool known as Warmup. Other tools are open for discussion and demos. If you have a developer automation tool you love, or you are are a developer automation tool builder, come ready to discuss your tools!
Build/Deploy Automation & Developer Automation Tools
This will be an open discussion about build/deploy and developer automation. In the .NET world, there has been a serious need for better tooling in this area and we can discuss the needs/deficits. If the conversation steers toward demos, we can take a look at a build tool called UppercuT, which is the fastest zero to professional build you will find in the .Net market to date (plus it's 100% free!). We can also look at a database change management tool known as RoundhousE. Another tool that may be shown is a project (solution and everything else) templating tool known as Warmup. Other tools are open for discussion and demos. If you have a developer automation tool you love, or you are are a developer automation tool builder, come ready to discuss your tools!
Coders For Charities (hash tag #c4c) will be the last weekend of March. This is the 3rd Annual C4C event where “dozens of Kansas City area web developers, designers, and business analysts will engage with local non-profit organizations for a weekend of ‘giving back’ to their communities.” This is something I’ve been wanting to do since the first event three years ago. It looks like an awesome event. If you are in the KC area and you haven’t already signed up for this event, what are you waiting for? This is going to be a great place to meet other developers and work hard on something that benefits a charity or non-profit organization.
I believe I will be presenting on UppercuT when I go out to Chicago Alt.NET’s monthly meeting in May. I’ve never been to Chicago, and I’ve heard that it’s beautiful in May. There may be some touring in order. I’m pretty excited to get out to see the sites and hang out with a bunch of cool, smart people up in the windy city. If I present on UppercuT, this is the abstract:
Automated Builds: How to UppercuT Your Code! “Build – it’s not just for F5 anymore.” How you build your code and verify quality is something that is usually not thought of at the beginning of a project, but is one of the most important things you can add to code! During this session we will go over the conventions in building and verifying code quality. We will see a project that is using automated builds and how all of the conventions are applied. We are going to see UppercuT and how well suited it is for automated builds. UppercuT is a build framework (based in NAnt) that allows rapid and powerful use of NAnt without having to understand the intricacies of NAnt. The last thing we will do is apply UppercuT to a project to show you how fast you can go from F5 to automated builds!
Automated Builds: How to UppercuT Your Code! “Build – it’s not just for F5 anymore.”
How you build your code and verify quality is something that is usually not thought of at the beginning of a project, but is one of the most important things you can add to code! During this session we will go over the conventions in building and verifying code quality. We will see a project that is using automated builds and how all of the conventions are applied. We are going to see UppercuT and how well suited it is for automated builds. UppercuT is a build framework (based in NAnt) that allows rapid and powerful use of NAnt without having to understand the intricacies of NAnt. The last thing we will do is apply UppercuT to a project to show you how fast you can go from F5 to automated builds!
At the KC DNUG I’ll be presenting on RoundhousE in July. KC DNUG is a fantastic group and I enjoy every event I can get to with them! Here is the abstract:
Database Change Management with RoundhousE! "Because kicking your database is a good thing!" Many would not argue that you need to version your code, and few would argue that you should version your code in a way that can lead you back to a specific point in source control history. However, most people don't really think of doing the same with your database. That's where RoundhousE comes in. RoundhousE versions your database how you want. Not to mention it's one of the most intelligent database migrations tools out there, it also helps you keep your scripts in source control in a way that makes sense. We'll walk through the tool and its features and then open for questions. You'll see how it can make database change management extremely simple for you and how it makes auditors and DBAs smile. RoundhousE - you know you want to learn more...
Database Change Management with RoundhousE! "Because kicking your database is a good thing!"
Many would not argue that you need to version your code, and few would argue that you should version your code in a way that can lead you back to a specific point in source control history. However, most people don't really think of doing the same with your database. That's where RoundhousE comes in. RoundhousE versions your database how you want. Not to mention it's one of the most intelligent database migrations tools out there, it also helps you keep your scripts in source control in a way that makes sense. We'll walk through the tool and its features and then open for questions. You'll see how it can make database change management extremely simple for you and how it makes auditors and DBAs smile. RoundhousE - you know you want to learn more...
KCDC (hash tag #kcdc) doesn’t have a firm date yet from what I hear. I don’t know much about this one yet, other than that I am so there. Hopefully talking about something cool as well!
Virtual Alt.NET, lovingly known as the VAN hosts meetings on Wednesday nights. Now I believe that is going to be once a month. I’m hoping to present on two topics this year. One of those is UppercuT and the other is RoundhousE (same abstracts as above). The dates for these are not set in stone yet, and I probably shouldn’t be trying to jinx it by talking about them yet! I’m really excited to do a presentation online though and how that will work out!
I loved Iowa Code Camp last year. It was awesome! I am definitely looking forward to another round with a bunch of awesome guys/gals! Tulsa TechFest was another event I really enjoyed and I would like to get back to this year. One I’ve never been to and think I would like to check out is the Heartland Developers Conference (HDC).
I’m open to other cool events and meeting more people smarter than I am. What conferences are you going to that I might find interesting?
Ultimately most complexity in software comes not from the requirements, the business logic, or even the underlying systems. Most complexity comes out of a poorly considered and managed architecture, and this is commonly seen in tightly coupled systems that rapidly degrade into Big Balls of Mud.
The key to simplifying architectures is to decouple their component parts, making each more specialised and less dependent on the others. This allows every component to focus on a specific task without risk of those tasks becoming compromised by, or interfering with, others.
The basic premise of most architectures that succeed in this way is that they are message based, rather than being glorified CRUD systems pulling data to and from databases. Operations are central to the system, data is ultimately just the storage for the results of those operations.
This separation of concerns allows development teams to concentrate on component parts with little impact upon other teams, and no more reliance upon them than an agreement on the messages they will listen for and those they will publish. While messages share some characteristics of service interfaces, they differ mostly in their granularity and lack of dependence upon one another. Messages allow true decoupling, where traditional service layers only promise this and usually end up creating even more tightly coupled systems.
"Message based" does not inherently mean SOA, and in many ways differs substantially. Although both can be considered message based, SOA brings significant overhead and generalisation, along with an organisation wide remit. Simple message based systems operate on the basis of message within the application rather than across an enterprise.
Poor software architecture ends up impacting more than just the software system under development, and frequently the impact can be felt across an organisation; in the way development teams approach problems, in the way they respond to new requirements, and throughout the SDLC.
More often than not, the SDLC is a reflection of the weaknesses in software architecture, and is frequently trying to play 'catch up' to attempt to deal with the issues weak architecture brings. These compromises and aspects of the SDLC then start to propagate and impact other areas of the development and support process. Often decisions about software architecture become the underlying drivers for the entire IT department, and the SDLC turns into a point of conflict as it becomes more about trying to restrain and control a big ball of mud than it does about providing a framework for providing business value.
Introducing a New Blog Series
Rarely do I make new year's resolution. But I found myself pinky swearing myself a single resolution for this year: write a series of tractable posts concerning the development of software for robotics. Accordingly, I intend to introduce background, techniques, and examples with respect to the following topics and more:
I'm writing this series of posts for two reasons. The first, as always, is to bounce ideas off readers as a sanity check. The second is to instill interest in readers in this rapidly evolving field. The sophistication of software algorithms within robotics is beginning to lag behind the potential capabilities of available hardware. Accordingly, if this series acts as motivational tool to get others involved in tackling the current algorithmic challenges involved in robotics, then all the better.
To make this series more interesting and applicable to the development audience that reads http://devlicio.us, I will attempt, when appropriate, to focus on patterns and general techniques that could possibly be applicable to scenarios outside of robotics. Additionally, due to this series' focus on software, I'll concentrate on leveraging simulators over physical, often expensive, robotics hardware to make it easier for readers to duplicate provided examples.
Enjoy!
Introduction to Architectural Paradigms of Robotic Control
While "architecture" is likely one of the least definable terms in software development, it is unavoidably a topic which has one of the greatest impacts on the extensibility and maintainability of an application. Indeed, a frequent cause of an application re-write is due to the architectural decisions that were made early in the project, or the lack thereof. Certainly one of the difficulties in instituting proper architectural practices in a project lies in the fact that architectural decisions must carefully be decided at many different levels of project development. Obviously, the architectural decisions at each level should be made in context of the project requirements in order to facilitate a proper balance of speed of development, scalability of development, and maintainability of the application in the future.
To illustrate, consider a basic eCommerce site vs. a corporate financial management application which integrates with various third party tools. There are two aspects of architecture which must be carefully considered. The first is deciding which project contexts will require architectural consideration. The second is defining the architectural approach to meet the needs of the given project context, and implementing any necessary prefactoring, accordingly.
For a basic eCommerce site, the project contexts requiring architectural consideration would likely include: appropriate separation between presentation and control, separation between control and the domain, separation between the domain and data access, and appropriate integration with a payment gateway. Perhaps the active record pattern might be chosen as the data access mechanism. If inversion of control seems overkill for such a small application, perhaps direct communications to the payment gateway via the domain objects might be chosen as the means of integration. Accordingly, judicious prefactoring would suggest that an architectural spike be created demonstrating appropriate use of the active record pattern with a selected tool along with an example of how and where integration with the payment gateway would take place.
For the more complex and demanding needs of the financial management application, architecture considerations, in addition to those taken into account for the basic eCommerce site, might include: what integration patterns might be leveraged to facilitate client integration, messaging patterns that might facilitate server side integration with third party financial calculators, and where inversion of control is appropriate. Perhaps RESTful services would be included to provide integration support for clients requiring such capabilities. Messaging via a publish/subscribe mechanism using a composed message processor might be selected as the ideal means for coordinating data with third party vendors on the server side. Again, proper refactoring, from an architectural perspective would include developing architectural spikes along with the foundational pieces of such an application to provide appropriate guidance for developers assisting with the project.
Developing software for robotics is no different, in this respect, than developing any other application. One must decide where the key architectural decisions need to be made and then provide adequate decisions and guidance to facilitate development in accordance with those decisions. As stated, these decisions must be made in consideration of the project needs. This post focuses on one project context, the architectural context of determining the overall approach to robotic motor control. We'll delve into three major architectural approaches to motor control along with highlighting a few specific implementations.
Deliberative vs. Reactive Robotics
Certainly the best source of intelligent systems to study and emulate are those found in nature. From the humble ant to the exalted human, evolution has honed a variety of strategies for dealing with the physical world. Accordingly, if we are to create intelligent systems, a good place to start is by emulating the behaviors and responses to stimuli demonstrated by living creatures.
In the early days of robotics, it was presumed that the most effective approach to emulating intelligence was by taking in detailed measurements provided by sensors, creating an internal representation of the world (e.g., grid-based maps), making plans based on that internal representation, and sending pre-planned commands to actuators (devices which convert energy into motion) for moving and interacting with the world. Inevitable, this approach presumes that the internal representation of the world is highly accurate, that the sensor reading are precise, and that there is enough time to carry out a plan before the world changes. In other words, this approach is highly deliberative. Obviously, the world is highly dynamic, sensor readings are sometimes erroneous, and time is sometimes of the essence when interacting with the world. E.g., you probably wouldn't want to spend much time creating an internal representation of the world if there's a Mac truck speeding towards you.
At the other extreme from deliberative is a reactive approach to interacting with the world. Taking a purely reactive approach does away with plans and internal representations of the world altogether; instead, a reactive approach focuses on using the world as its own model and reacts, accordingly. Instead of plans, reactive approaches often rely upon finite state machines to modify behavior as the world changes. Rodney Brooks changed robotics thinking upside down when he introduced this paradigm shift in the 80s. The robots he and his team produced were much faster in their reaction times, when compared to deliberative robots, and exhibited surprisingly complex behavior, appearing quite intelligent in many scenarios. But as is any extreme, a purely reactive approach to the world had it's own drawbacks; its difficulty in managing complex scenarios which demand careful planning is one such example.
The figure at right illustrates some of the differences between deliberative and reactive systems. Adapted from (Arkin, 1998).
While there are certainly pros and cons to either approach, there are some scenarios which are appropriate to one or the other. While still others may require more of a hybrid approach. Let's now take a look at each approach in more detail.
Deliberative/Hierarchical Style
One of the first successful architectural implementations on a mobile robotic platform was the well known robot known as Shakey, created at Stanford University in the 1960s. Of particular note was the robot's architecture which was made up of three predominant layers: a sensing layer, a planning layer, and an execution layer. Accordingly, as information would be made available by sensors, Shakey would make plans on how to react to the perceived world and send those plans on to the execution layer for low level control of actuators. This began the architectural paradigm known as sense-plan-act (SPA). As the SPA approach matured, additional layers were introduced in a hierarchical style, typically all of which having access to a global world model. The upper layers in the hierarchy would use the world model to make plans reaching into the future. After plan development completed at the highest levels, the plans would be broken down into smaller commands and passed to lower levels for execution. Lower layers would then further decompose the commands into more actionable tasks which would ultimately be passed on to actuators at the lowest level. In a hierarchical approach, the sensing layers participate in keeping the internal representation of the world updated. Furthermore, to better accommodate dynamic changes to the world environment, lower planning layers may suggest changes to plans based on recent changes to the world model.
The diagram at right roughly illustrates this hierarchical architectural approach. As should be noted, the sensing layers update the world model while a hierarchy of planning and execution layers formulate plans and breaks those plans down into actionable tasks. Adapted from (Kortenkamp, 1998).
4D/RCS
The current pinnacle of hierarchical architectures may be found in the reference architecture known as 4D/RCS (4 Dimensional / Real-time Control System). 4D/RCS is the latest version of the RCS reference model architecture for intelligent systems that has been evolving for decades. With 4D/RCS, six (more or less) layers are defined for creating and decomposing plans into low level action:
At the heart of each and every layer are one or more 4D/RCS nodes which contain a behavior generation process which accepts commands from the next higher level and issues subgoals to the behavior generation process at lower levels. Furthermore, each node reports its task execution status up the chain for consideration into further planning. While plans are being solidified and disseminated, a sensory processing process in each node receives sensory input from lower levels, updates the world model, and collates the sensory data into larger units which it passes on to the next higher level; e.g., points get converted to lines which get converted to surfaces which get converted to objects with each successive rise through the 4D/RCS layers A visual summary of a 4D/RCS node quickly shows just how complex such a system can become. But sometimes the demands of a task require a respective amount of sophistication in the architectural approach. A comprehensive review of the 4D/RCS approach is discussed in (Albus, 2006).
The primary advantage to deliberative, hierarchical approaches such as 4D/RCS is that competent plans for managing complex scenarios can be generated and broken down into smaller chunks for lower levels to execute. But an obvious disadvantage to this comes in the form of much added complexity to the overall system while penalizing the speed at which the system can react to a changing environment.
Reactive Style
In the 1980s, Rodney Brooks, in an effort to overcome some of the limitations of sense-plan-act that Shakey and other such robots exhibited, introduced the concept of reactive control. "Simply put, reactive control is a technique for tightly coupling perception and action, typically in the context of motor behaviors, to produce timely robotic response in dynamic and unstructured worlds." (Arkin, 1998). In other words, by eliminating the reliance on maintaining an internal world model and avoiding large amounts of time generating plans, simple responses can be executed in reaction to specific stimuli, thus exhibiting behavior similar to that of living organisms. If there was any doubt in what Brooks was trying to imply, he boldly titled one of his works, Planning is Just a Way of Avoiding Figuring Out What to do Next. This paradigm shift away from planning turned sense-plan-act into a simpler sense-act.
Accordingly, in a sense-act paradigm, the primary focus is in carefully defining behaviors and the environment stimulus which should invoke those behaviors. This focus is deeply rooted in the idea of behaviorism; from a behaviorism perspective, behavior is defined in terms of stimulus and response by observing what an organism does. This approach of developing robotics around such behaviors, unsurprisingly enough, is commonly referred to as Behavior Based Robotics. By leveraging simple state machines, which we'll examine below, to define which behaviors are active for a given stimuli, the overall architectural complexity is greatly reduced while giving rise to responsive, seemingly intelligent behaviors. (The question of whether or not the robot is truly intelligent and/or self-aware is a debate that I'll leave to others such as Searle and Dennett.) While the reactive approach took the spotlight for a number of years, and is still very appropriate in some cases, its limitations in managing complex scenarios and performing sophisticated planning indicated that this approach was not the end all panacea for all situations.
The diagram at right clarifies that reactive systems remove planning and deal with sensory input within the context of each behavior. I.e., "Behavior 1" has no knowledge of the feedback coming from "Sensor 3." Adapted from (Kortenkamp, 1998).
A Design Methodology for Reactive Systems
Due to the simplistic nature of reactive systems, the corresponding design methodology for developing such systems is rather straight-forward (Kortenkamp, 1998):
When implemented, we find that the defined behaviors can be realized as states within a finite state machine and that found conditions act as the mechanism for changing from one behavior state to another. What's missing is an arbitration mechanism to determine which behavior wins out in light of competing conditions. Let's briefly look at implementation approaches to reactive systems along with how such arbitration is achieved.
Subsumption & Motor Schemas
A key concern with reactive, behavior based robotics is determining which behavior should take precedence if conditions exist to activate two or more behaviors. An arbitration mechanism needs to be introduced to resolve such competing situation. Brooks dealt with this issue by proposing an architecture known as subsumption. Simply enough, a subsumption architecture still uses a finite state machine to codify behaviors and transitions, but introduces behavioral layers which can provide input to other layers and override behaviors of lower layers. The advantage is in facilitating more sophisticated behaviors as a sum of "lesser" behaviors. It's better understood by reviewing the following example from (Arkin, 1998):
In the example, there are three behavioral layers: Avoid-Objects, Explore, and Back-Out-of-Tight-Situations. The Avoid-Objects Layer's responsibility is to avoid objects by moving away from any perceived obstacles. The Explore Layer's responsibility is to cover large areas of space in the absence of obstacles. The highest level assists the robot in getting out of tight spaces if the lower layers are unable to do so. Each discrete behavior can invoke transitions to other behaviors and/or provide input or advice to subsequent behaviors based on perceived conditions. The "trick" of subsumption is that higher levels can suppress commands between lower level behaviors; consequently, higher layers are able to handle more complex scenarios by manipulating lower level behaviors in addition to its own. To illustrate suppression, note, in the above example that the "Reverse" behavior in the Back-Out-of-Tight-Situations Layer suppresses any commands that the "Forward" behavior is sending to the actuators. By doing so, complex behaviors may emerge using a number of basic behaviors and a relatively simple architectural approach.
A major challenge in using a subsumption architecture is deciding the appropriate hierarchy of behavioral layers. In more complex scenarios, it quickly gets sticky deciding which layer should have the right to suppress which other layers. (Otherwise known as spaghetti-prone.) Additionally, since the layers have bi-directional dependencies amongst each other, in order to provide input and suppression, changes to layers can have large impacts on other layers, often resulting in shotgun surgery with any change. Ronald Arkin introduced a subsequent architecture to address these, and other concerns, with an approach known as Motor-Schema based control which does away with arbitrating competing behaviors. Each active behavior in a Motor-Schema based control system calculates a vector to be carried out by an actuator (e.g., wheels or arm). Using vector addition, a final vector for each actuator is computed and sent for execution. With this approach, the output of behaviors is combined instead of arbitrated. This avoids the need to determine suppression hierarchies and makes a more extensible application, within the limits of behavior based robotics.
Hybrid Style
Most modern architectural approaches to robotics control attempt to combine the planning capabilities of deliberative systems with the responsiveness of reactive systems. Appropriately enough, this is referred to as a hybrid style. While the deliberative approach takes a sense-plan-act perspective and the reactive approach follows with plan-act, a hybrid approach typically takes the form of plan, sense-act. So while the sense-act layers carry out behaviors, the deliberative planning layer can observe the progress of reactive behaviors and suggest direction based on reasoning, planning and problem-solving. The diagram at right crudely demonstrates this combination of the two approaches. Adapted from (Kortenkamp, 1998).
Three-Layer Architecture (3T)
James Firby's thesis proposing Reactive Action Packages (RAPs) (Firby, 1989) provided a solid approach for integrating deliberative planning with reactive behaviors in the form of a three layer architecture. A multitude of subsequent architectures have emulated a similar approach, coming to be known as 3T architecture. From Eran Gat's essay Three Layer Architecture (Kortenkamp, 1998):
Three-layer architectures organize algorithms according to whether they contain no state, contain state reflecting memories about the past, or contain state reflecting predictions about the future. Stateless sensor-based algorithms inhabit the control component. Algorithms that contain memory about the past inhabit the sequencer. Algorithms that make predictions about the future inhabit the deliberator. ... In 3T the components are called the skill layer, the sequencing layer, and the planning [deliberative] layer.
As implied by being a hybrid approach, this 3T architecture is not mutually exclusive to behavior driven robotics. Indeed, the skill layer itself is made up of unique behaviors which resemble that of reactive systems. The sequencing layer then uses the world model to determine when a change in behavior is required. The planning layer can then monitor the lower layers and perform more deliberative, time consuming processes such as path planning. The primary variation amongst 3T implementations lies in the decision between whether the planning layer "runs the show" or if the sequencing layer takes command and invokes the planning layer only when needed. Atlantis is one such example that leaves primary control to the sequencing layer to invoke the planning layer. Other examples of 3T implementations include Bonasso's Integrating Reaction Plans and Layered Competences through Synchronous Control and Standford's Junior which took 2nd place in Darpa's Urban Challenge.
Wrapping Up
The intention of this article has been to provide an introductory overview of paradigms of robotic control including deliberative/hierarchical systems, reactive systems, and hybrid systems. While each approach is appropriate in select contexts, a hybrid architecture is very adaptable for accommodating a large variety of robotic control scenarios with sufficient planning and reactive capabilities. The interested reader is encouraged to dig further into the references and links provided to learn more about these various approaches and design methodologies for implementation.
In the next post, we will examine messaging strategies to facilitate the communications amongst the layers and components of robotic systems.
References
Albus, J., Madhavan, R., Messina, E. 2006. Intelligent Vehicle Systems: A 4D/RCS Approach.
Arkin, R. 1998. Behavior Based Robotics.
Firby, J. 1989. Adaptive Execution in Complex Dynamic Worlds.
Kortenkamp, D., Bonasso, R., Murphy, R. 1998. Artificial Intelligence and Mobile Robots.
With any framework, technology, tutorial, book, idea, <insert pedagogical sources here>, it's often difficult to figure how the technique should be used in the real world. Sure, things sound great when the scope of your project deals cleanly with Customers, Orders and Order Items; and sure, auto-binding works great in a nicely controlled scenarios...but how should things work in the "real world." No, not the Real World in which Brooke has a complete meltdown in season 18. I'm talking about the real world in which many scenarios aren't clean cut in determining where the controller should end and the application services layer should take over; the world in which you're trying to bind from unique data collection mechanisms to appease odd requests from the client; the world in which even the GoF would retort with "heh, that's a tricky one." Yes, we're faced with these kinds of dilemmas on a frequent basis. What I find to be truly helpful is to look at full blown, real-world applications. While a real-world application is never perfect, you can often find great gems for dealing with tricky situations and using patterns and complex scenarios. Howard van Rooijen has released just such a real-world application demonstrating the use of S#arp Architecture, AutoMapper, ELMAH, Spark View Engine, and other great tools.
In Howard's own words... "A few months ago I wrote an email to the community about a site we had just launched – http://fancydressoutfitters.co.uk that used S#arp Architecture at its core along with a whole myriad of other Open Source Frameworks and Tools (Spark, AutoMapper, PostSharp, xVal...).
In the run up to the festive period, myself and two of the development team – Jonathan George & James Broome, decided that in the spirit of giving, we wanted to gift something back to the communities that gave so much to us throughout the year; so we decided to build a new sample web application to showcase the use of these various frameworks & tools called “Who Can Help Me?” which is based on the same architectural style as http://fancydressoutfitters.co.uk.
Who Can Help Me? started out as a small web application I built a few years ago to solve a small and specific business problem within our consulting organisation (and to test out .NET 3.5, LINQ to SQL, ASP.NET WebForms & MS AJAX!). The problem was, that as the organisation grew and new members of staff started, they found it difficult to find the right people who could help them solve specific problems they’d encounter in their consulting gigs. As I have worked for the organisation for a long time (>9 years) I generally knew everyone, had worked with most of them and knew what their areas of expertise were, thus I’d get a few calls every day asking “Do you know anyone who knows about X that could help me?”. The solution was to create a searchable skills matrix that would allow people within an organisation find other people who had specific skills or expertise who could help them solve a particular problem.
So Jonathan, James & I decided to re-write the Who Can Help Me? from scratch, using the architecture style, frameworks and tools we used to build http://fancydressoutfitters.co.uk - it might seem like we've massively over-complicated the architecture for such a simple application - but we really wanted this to demonstrate some of the concepts & techniques we used to build a full scale, public facing enterprise web application.
Who Can Help Me? utilises the following:
The project is currently hosted at Codeplex: http://whocanhelpme.codeplex.com/ and we’ve also released a live demo: http://who-can-help.me. We’ve added some documentation on the Codeplex homepage and will continue to refine this and augment it with blog posts covering some topics in more depth – so if you’re interested – please keep an eye on the following blogs / twitter:
Thanks very much for this contribution Howard! Besides myself, I'm sure others have found this project helpful and will continue to do so in the future.
Billy McCafferty
It took a little longer than I planned but here we go again. In the meantime ActiveRecord 2.1 was released, and soon after that a minor update bringing one cool big feature. From now on we’ll be working on version 2.1.2. Picking up from where we left off last time. We have a user entity. Since we’re building a website where users can publish benchmark results, we’ll create now a benchmark entity, and create a relation between these two.
Let’s start by adding an appropriate field to the User class:
private readonly ICollection<BenchmarkResult> benchmarkResults = new HashSet<BenchmarkResult>();
We also create a property:
public IEnumerable<BenchmarkResult> BenchmarkResults
{
get
foreach (var result in benchmarkResults)
yield return result;
}
So far this is just a regular property. To map it as a one-to-many relation we use the HasManyAttribute.
[HasMany(Access = PropertyAccess.FieldCamelcase,
Cascade = ManyRelationCascadeEnum.SaveUpdate,
RelationType = RelationType.Set,
Inverse = true)]
There’s quite a lot going on here, so let’s go over it piece by piece
Let’s now build our BenchmarkResult class.
[ActiveRecord]
public class BenchmarkResult : ActiveRecordLinqBase<BenchmarkResult>
protected BenchmarkResult()
public BenchmarkResult(User user, string benmchmarkName, string computerModel, double score)
if (user == null)
throw new ArgumentNullException("user");
if (benmchmarkName == null)
throw new ArgumentNullException("benmchmarkName");
if (computerModel == null)
throw new ArgumentNullException("computerModel");
User = user;
BenmchmarkName = benmchmarkName;
ComputerModel = computerModel;
Score = score;
So far there’s nothing new here. Computer configuration and benchmark will become entities themselves soon, but let’s not get ahead of ourselves.
The only interesting property at this point is the User.
[BelongsTo]
public User User { get; private set; }
It has a BelongsToAttribute to denote it points to another entity (on our case the ‘one’ end of our one-to-many).
Let’s now let our users to actually save benchmark results, and we’re more or less done:
public BenchmarkResult RunBenchmark(string benchmarkName, string computerModel, double score)
var result = new BenchmarkResult(this, benchmarkName, computerModel, score);
benchmarkResults.Add(result);
return result;
We now have all the logic in place, so let’s build a test:
[Fact]
public void Can_perform_benchmark_runs()
var stefan = new User
Email = "stefan@gmail.com",
Name = "Stefan",
Password = "Super compilcated password!",
About = "Stefan is a very cool."
};
stefan.RunBenchmark("Foo bar!", "AyeMack Pro", 3.2);
stefan.Save();
var user = User.FindAll().Single();
Assert.NotEmpty(user.BenchmarkResults);
Assert.Equal(1, user.BenchmarkResults.Count());
var result = user.BenchmarkResults.Single();
Assert.NotNull(result);
Assert.Equal("Foo bar!", result.BenmchmarkName);
Assert.Equal("AyeMack Pro", result.ComputerModel);
Assert.Equal(3.2, result.Score);
If we run it now, it will fail. Good news is, that it does not have anything to do directly with our logic. Bad news is, that we have a passing test nonetheless, so let’s have a look at it.
We get “Incorrect syntax near the keyword 'User'.” error message. Here’s the SQL that was sent to the database:
Looks good doesn’t it? Well not – really, User is a SQL Server keyword, as we can’t just use it as identifier – we have to escape it. To do it, we have to specify the column name explicitly, and escape it by enclosing it within two ` characters (located above tab key on my keyboard).
Yes I’m aware of hbm2ddl.keywords auto-quote. However I had some issues getting it to work with ActiveRecord. Any help doing this will be appreciated.
[BelongsTo(Column = "`User`")]
Now the test will pass, and the following SQL will be generated:
Now that we have the correct SQL, let’s look at what our schema looks like:
The most daunting task when trying out or learning a new framework or technology is figuring out where the heck to start. Take S#arp Architecture for instance (arguably, the world's most insanely amazing framework ever written by mankind...more or less). As incredibly cool as S#arp is, it can be intimidating trying to figure out where to start. To make that first step a bit easier, the following will get you up and running with your first S#arp project in about 15 minutes or less. (Steps verified on WinXP Pro.)
Here's what you'll have in less than 15 minutes:
Ready?
Download S#arp Architecture and Generate Your Project
Hook Up to a Database
Generate CRUD Scaffolding
new EntityScaffoldingDetails("ProductCategory");
entityScaffoldingDetails.EntityProperties.Add( new EntityProperty("Name", "string", "Seafood", "[NotNull, NotEmpty]", true) );
generator.Run()
Create the Database Table
Enforce Object Uniqueness
[HasUniqueDomainSignature( Message="Provide a unique name dag nabbit!")] public class ProductCategory : Entity { ...
Wanna learn more? Go to http://wiki.sharparchitecture.net/
And for all you S#arp junkies out there...the next quarterly release is coming soon...and it's going to have proper utilization of the application services layer during CRUD generation!
What if there was a tool out there that could let you specify a structure for a project (visual studio solution + everything else) and save you up to 3+ hours of work every time you started a new project?
Warmup was an idea by Dru Sellers to remove all of the setup work required every time you set up a new project. You know, create the solution, add projects, put in your references, etc. Then how about getting the infrastructure for your service/website/console set up as well with things like IoC, etc? What about patterns and other pet items that you put in any project?
Yeah – there’s an app for that. And it’s pretty simple to use. Plus you can change your templates when you have new ideas, so it’s totally rockstar!
The first thing you do is to set up templates somewhere in source control (svn or git). Then you specify where that is to the configuration and what type of source control.
<warmup sourceControlWarmupLocation="git://github.com/ferventcoder/warmup-templates.git" sourceControlType="git" />
Then you run a simple command. If base was one of the folders below the directory above my source control, then that is what I would specify as the first argument.
warmup.exe base nameOfProject
I specify nameofProject. That is what I want my project to be named when I am complete.
Let’s start by taking a look at the base template. The basic idea here is simple. Place __NAME__ everywhere you want to be replaced when running Warmup. In the same way UppercuT does token replacement with ConfigBuilder, DocBuilder, SqlBuilder, and DeployBuilder, Warmup does token replacement for an entire solution.
If we were to look at some of the files you would see the absolute depth of how naming really can be replaced. Let’s take a look at the solution though, that will hold quite a bit of meaning for you.
Now that we have our template all set up, we have one thing to do. Open our .sln file in Notepad and delete the first line (not sure what happens here, but this works). Microsoft should be on the first line when we are done.
And now our template is all ready, so we check our changes into source. If we are using git, we need to push back to the repository we are looking at after we finish committing.
Now that we’ve seen our template, let’s run Warmup.
Let’s call the new project Alpha.
warmup.exe base Alpha
This is the output:
Hardcore git cloning action to: C:\code\warmup\code_drop\warmup\Alpha Running: cmd /c git clone git://github.com/ferventcoder/warmup-templates.git C:\code\warmup\code_drop\warmup\Alpha Initialized empty Git repository in C:/code/warmup/code_drop/warmup/Alpha/.git/ replacing tokens
Hardcore git cloning action to: C:\code\warmup\code_drop\warmup\Alpha Running: cmd /c git clone git://github.com/ferventcoder/warmup-templates.git C:\code\warmup\code_drop\warmup\Alpha Initialized empty Git repository in C:/code/warmup/code_drop/warmup/Alpha/.git/
replacing tokens
This project already has UppercuT, RoundhousE, and others already in it. Take a look at the lib folder:
I open a command line and run build.bat. I get a successful build with 31 passing tests! If I were to go to the code_drop folder, I’m all ready to deploy if I had my deployment framework already here.
I can already run RoundhousE and create my database from here:
I have an entire structure that allows me to just concentrate on the stories at hand. Warmup may not be the best thing since sliced bread, but it’s going to save you oodles of time! If you know someone else that has created a template you want to use and it’s shared publicly, you can just edit the config file to point there. We have been using warmup for close to 3 months now and it saves us quite a bit of time. Plus we find more and more things we can put back into the templates to save us time. I believe this aspect of learning and growing your templates over time is the intention of warmup. Plus your template may not be the same as mine and that’s completely cool!
Have you ever received the following exception while using MEF?
Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
if you have received this error did you scratch your head and wonder ‘what the F does this mean’? Well the long and short of it is this. This means that while trying to load an Export there was a dependency which could not be found during reflection.
The quick way to determine the cause of the error is to do as the image below shows:
What I would suggest is to wrap your MEF logic inside of a try-catch and explicitly catch ReflectionTypeLoadException. Inside of your catch add some logic as such (just a PoC, copy-paster beware).
catch (ReflectionTypeLoadException tLException) { var loaderMessages = new StringBuilder(); loaderMessages.AppendLine("While trying to load composable parts the follwing loader exceptions were found: "); foreach (var loaderException in tLException.LoaderExceptions) { loaderMessages.AppendLine(loaderException.Message); } // this is one of our custom exception types. throw new PluginLoadingException(loaderMessages.ToString(), tLException); }
As you can see when you look at the LoaderExcpetions collections from my image above I had forgotten to reference AutoMapper in my plug-in and all hell broke lose (btw, no idea how this even compiled, but that is a different post).
Hope this helps.
Till next time,
Disclaimer: This post is about the idea, not about the implementation. The implementation is crippled, not thread safe, will work only in few scenarios and only if used properly. Do not copy and blindly use this code.
This post is about the idea, not about the implementation. The implementation is crippled, not thread safe, will work only in few scenarios and only if used properly. Do not copy and blindly use this code.
One of unique features of Windsor is that it manages the lifecycle of objects it creates for you. What this means (among other things) is that it will dispose all disposable objects it instantiates. However to do this, it has to keep a reference to these components. Also that means that in order to properly release the components you have to get hold of the container and once you’re done using the components – explicitly release them with the container.
It may be not desirable from your perspective to do it like this. Ideally, you’d rather use your component, call Dispose and then have Windsor release the component. This is not quite possible, since there’s no way by which Windsor can be notified that your component was disposed. Or is there?
Since Dispose is an interface method and Windsor has quite powerful AOP capabilities, we can take advantage of that and intercept the call to Dispose and transparently release our component. Let’s build a disposable component first:
public interface IController : IDisposable
int DoSomething();
bool Disposed { get; set; }
public class Controller : IController
// notice it's not virtual!
public void Dispose()
// some clean up logic here
Disposed = true;
public int DoSomething()
return 42;
public bool Disposed
get; set;
One important thing to notice about this code – Dispose is not implemented virtually. This will make our sample simpler since we won’t have to deal with recursion.
Then we set up the stage:
[TestFixture]
public class TransparentReleasingTest
private WindsorContainer container;
[SetUp]
public void SetUp()
container = new WindsorContainer();
container.Register(Component.For<ReleaseComponentInterceptor>());
container.Register(Component.For<IController>().ImplementedBy<Controller>()
.LifeStyle.Transient
.Interceptors<ReleaseComponentInterceptor>());
[TearDown]
public void CleanUp()
container.Dispose();
We’ll discuss the ReleaseComponentInterceptor, which is the gist of this post, in a minute. Let’s first create a test:
[Test]
public void Dispose_releases_component()
IController item;
using (var controller = container.Resolve<IController>())
item = controller;
controller.DoSomething();
Assert.IsTrue(container.Kernel.ReleasePolicy.HasTrack(controller));
Assert.IsFalse(controller.Disposed);
Assert.IsFalse(container.Kernel.ReleasePolicy.HasTrack(item));
Assert.IsTrue(item.Disposed);
Notice that in order for the interceptor to intercept the call to Dispose we need to cast component to IDisposable before calling the method (‘using’ will do that for us). Notice important aspect of this test – it is completely container agnostic. It does not need any kind of explicit nor indirect reference to the container to work and to release the component properly. Let’s now see what happens behind the scenes.
The hero of the day, is the following interceptor:
[Transient]
public class ReleaseComponentInterceptor : IInterceptor
private static readonly MethodInfo dispose = typeof(IDisposable).GetMethods().Single();
private readonly IKernel kernel;
public ReleaseComponentInterceptor(IKernel kernel)
this.kernel = kernel;
public void Intercept(IInvocation invocation)
if (invocation.Method == dispose)
kernel.ReleaseComponent(invocation.Proxy);
else
invocation.Proceed();
Most of the time it just sits there and does nothing. However if it detects a call to Dispose, it releases the component from the container, which will in turn invoke the Dispose again (this time on the class, not interface, and since the interface is implemented non-virtually that second call won’t be intercepted), perform all other decommission job for the component as well as all its dependencies and it will release it, so that it can be garbage collected afterwards.
This is just another useful trick, worth knowing if you want to keep your design container agnostic while still reaping full benefits it provides.
Today I needed a way to identify ANSI (Windows-1252) and UTF-8 files in a directory filled with files of these two types. I was surprised to not find a simple way of doing this via a property of method somewhere under the System.IO namespace.
System.IO
Not that it's that hard to identify the encoding programmatically, but it's always better when you don't need to write a method yourself. Anyway, here's what I came up with. It detects UTF-8 encoding based on the encoding signature added to the beginning of the file.
The code below is specific to UTF-8 but shouldn't be too hard to extend the example to detect more encodings.
public static bool IsUtf8(string fname){ using(var f = File.Open(fname, FileMode.Open)){ var sig = new byte[Encoding.UTF8.GetPreamble().Length]; f.Read(sig, 0, sig.Length); return sig.SequenceEqual(Encoding.UTF8.GetPreamble()); } }
Maybe I just looked in the wrong places. Does anyone know a simpler way in the framework to accomplish this?
In June of 2006 I officially became a professional author when ASP.NET Pro published my article “Google Can You Hear Me?”. (So eager was I to be published I submitted my code in VB!). Now I’m proud to announce Wrox has published “XNA 3D Primer” as a Wrox Blox (aka eBook).
Why did I write this book? Truth be told, I’ve always admired technical book authors that can teach not only the how, but the why of a subject. It’s no secret I’m a fan of Charles Petzold; ever since I read “Programming Windows 95” (my first Windows version that I wrote applications for) I’ve wanted to join the ranks of great technical authors. I of course am not there yet – this is my first book and I kept it simple by writing a 40 page ebook.
Who did I write this book for? A book is nothing without an audience. This book is written for the line-of-business application developer who is curious about writing 3D games using XNA. XNA is Microsoft’s platform for game development and includes a .Net based managed framework that can be run on Windows PCs and the Xbox 360. You can even sell your games over Xbox Live Indie Games. Some experience with XNA is expected, but not more than working though the getting started beginner’s guides.
What does this book cover? The book is a surface tour of 3D programming. When I first started 3D programming I felt like all the documentation was in Latin. None of the lingo made sense, and I had trouble just figuring out what I needed to search on. The book covers the basics of 3D space and the core math you’ll use over and over again. It then moves on to handling the camera, working with 3D models, collision detection, and ends with methods to animate 3D models. None of these topics are covered in great depth, but I found touching each one gives the “full picture” to the parts of a 3D game. Think of this book more as a guided safari instead of a full expedition into the jungles of XNA 3D.
You said free copy? Yes, though this free copy isn’t without cost.
First, you can buy XNA 3D Primer from Wrox for $6.99 (this is cheaper than my “free” offer below).
Since this is my first published book I wanted to do something special. Initially I was going to donate the money I earned from the book (which isn’t much – this was written for pleasure not profit) to Child’s Play. What is Child’s Play? In their own words:
Since 2003, over 100,000 gamers worldwide have banded together through Child’s Play, a community based charity grown and nurtured from the game culture and industry. Over 5 million dollars in donations of toys, games, books and cash for sick kids in children’s hospitals across North America and the world have been collected since our inception. This year, we have continued expanding across the country and the globe. With almost 70 partner hospitals and more arriving every month, you can be sure to find one from the map above that needs your help! You can choose to purchase requested items from their online retailer wish lists, or make a cash donation that helps out Child’s Play hospitals everywhere. Any items purchased through Amazon will be shipped directly to your hospital of choice, so please be sure to select their shipping address rather than your own. When gamers give back, it makes a difference!
Since 2003, over 100,000 gamers worldwide have banded together through Child’s Play, a community based charity grown and nurtured from the game culture and industry. Over 5 million dollars in donations of toys, games, books and cash for sick kids in children’s hospitals across North America and the world have been collected since our inception.
This year, we have continued expanding across the country and the globe. With almost 70 partner hospitals and more arriving every month, you can be sure to find one from the map above that needs your help! You can choose to purchase requested items from their online retailer wish lists, or make a cash donation that helps out Child’s Play hospitals everywhere. Any items purchased through Amazon will be shipped directly to your hospital of choice, so please be sure to select their shipping address rather than your own.
When gamers give back, it makes a difference!
Then I stole got an idea from Seth. Instead of giving the money outright, I offer to anyone interested to make a $30.00 (or more) donation to Child’s Play. Then email me the receipt and I’ll send you a free copy of XNA 3D Primer. You’re making the donation, so you get to take the tax deduction and you get the good feeling that comes when giving a donation.
When I explained the idea to my publisher, Wrox, not only did they love it, they offered to pitch in. On my own I would have been able to give away 50 copies of my book; Wrox has doubled this so I can give away 100!
So, the steps are:
Once these 100 copies are gone, that’s it – I’ll update this post with the remaining copies and the total raised for Child’s Play.
I was trying to profile some NHibernate sql to see if we could make some improvements. One thing I noticed very shortly after starting this task was that one of my columns was being duplicated. Now this is NOT going to make a huge performance difference but was something I wanted to remove this as it felt ‘wrong’. Below is the original sql segment
resrole1_.PART_B_PROVIDER_NO as PART16_29_0_, resrole1_.ADMIN_SET_ID as ADMIN17_29_0_, resrole1_.NATIONAL_PROVIDER_ID as NATIONAL18_29_0_, resrole1_.Admin_Set_ID as Admin17_29_0_, resrole1_.Resource_Type as Resource19_29_0_,
The first thing I looked at when I started to look for the cause was the mapping for the entity (Role entity) to make sure the ‘Admin_Set_ID’ is not mapped multiple times. And of course it was not. After a few minutes of staring at the screen (everyone knows this is how all the hard problems are solved) it dawned on me that Admin_Set_ID was a FK from one of my associations. Below is my association that was using it.
References( x => x.ResourceType ) .Access.AsCamelCaseField( Prefix.Underscore ) .WithColumns( "Admin_Set_ID", "Resource_Type" ) .FetchType.Select() .LazyLoad();
Because Admin_Set_Id is a FK I should NOT have been mapping as part of my entity. When I removed this column from my entity so that I could get it as part of my association. When I did this my newly outputted sql looked like below:
resrole1_.PART_B_PROVIDER_NO as PART16_29_0_, resrole1_.ADMIN_SET_ID as ADMIN17_29_0_, resrole1_.NATIONAL_PROVIDER_ID as NATIONAL18_29_0_, resrole1_.Resource_Type as Resource19_29_0_,
Long story short is this. If you happen to see a column being duplicated in your generated sql make sure that you are not referencing/mapping a FK directly in your mappings.
Over the past week, I spent some time with Silverlight 4. I’m really impressed by how easy it is to incorporate video into an application. This post shows how to capture video from the camera and display the video on the screen. The post also handles grabbing single frames of video. You might use this type of arrangement to allow users to upload images, hold impromptu web casts, and to do video conferencing.
I’ve uploaded a barebones application that turns on video capture and displays the video (viewable here)
To understand how this works, you need to be familiar with the following objects:
CaptureSource
CaptureDeviceConfiguration
VideoBrush
The CaptureSource type encapsulates the methods needed to interact with audio and video devices.
CaptureDeviceConfiguration acts as gatekeeper to the webcam and microphone. CaptureDeviceConfiguration knows how to ask the user for permission to use the webcam or microphone. It also remembers what the user said about using the webcam or microphone during the current session.
VideoBrush knows how to paint using a video as the source.
In order to interact with the camera, you need to know whether the user gave you permission and if not, you have to ask for permission. Once you have permission, you just need to capture video from the default camera, create the brush, and start capturing the video.
private CaptureSource _captureSource = null; private void btnStartCapture_Click(object sender, RoutedEventArgs e) { if (_captureSource != null &&
_captureSource.State == CaptureState.Started) { return; } if(!CaptureDeviceConfiguration.AllowedDeviceAccess) { if(!CaptureDeviceConfiguration.RequestDeviceAccess()) { return; } } _captureSource = new CaptureSource { VideoCaptureDevice =
CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice() }; var brush = new VideoBrush(); brush.SetSource(_captureSource); _captureSource.Start(); rectVideo.Fill = brush; }
Later on, when the user wants to capture the current video as an image, you just do something like this to put the current frame into an image control:
private void btnGrabImage_Click(object sender, RoutedEventArgs e) { if(_captureSource == null || _captureSource.State != CaptureState.Started) { return; } _captureSource.AsyncCaptureImage( image => imgCapture.Source = image ); }
As someone who spent way too much time in the C++/MFC era writing code to capture video from devices, I am incredibly impressed with the brevity of the code. I think the SL4 team did an awesome job here. Way to go!
You can grab the sample project here.
I’ll be at the Alabama Code Camp this weekend in Mobile, AL, along our very own Alan Northam. Like all code camps, this is a free event. If you are anywhere in the area I recommend attending. The schedule is posted on the site and there are a lot of great sessions.
I’m doing two presentations: one on source control concepts that I did at the Jacksonville and Tallahassee code camps. The other is a brand new presentation about the basics of game development, with a primer for XNA and how to port the source to Silverlight using SilverSprite.
You can also follow the event on Twitter with the hashtag #ALCC.
Hope to see you there.
Today I was adding some new logic to some older code and I noticed there was very little test coverage on the logic so I decided to add some. As I was adding coverage I wanted to add some tests that proved out our Contract style bound checks. However there was one slight problem. The method(s) I wanted to add coverage to was overloaded as such:
public virtual IMessageTransporter GetTransporter( Destination destination ) { // The null transporter will allow for testing without hittng rhapsody or an engine if ( ConfigurationReader.UseNullTransporter ) { return new NullTransporter(); } Contracts.Requires( destination != null ); // for now we only have one transporter for now.... will add more later return new HttpTransporter( destination.Endpoint ); }
and
public virtual IMessageTransporter GetTransporter( Endpoint endpoint ) { // The null transporter will allow for testing without hittng rhapsody or an engine if ( ConfigurationReader.UseNullTransporter ) { return new NullTransporter(); } Contracts.Requires( endpoint != null ); // for now we only have one transporter for now.... will add more later return new HttpTransporter( endpoint ); }
As you can see from the 2 methods above they are the same with ONLY the provided parameter being different. When I started to write my tests I did this:
transporterFactory.GetTransporter( null );
But when I simply added NULL as the parameter the compiler provided me the following error
The call is ambiguous between the following methods or properties: ‘TransporterFactory.GetTransporter(Destination)' and TransporterFactory.GetTransporter(Endpoint)' C:\PathHere\TransporterFactoryTests.cs 13 45 Common.Tests
Now if you think about it for even 2 seconds the above error makes perfect sense. So the real question is how do you get around this….? Simple you do the following.
transporterFactory.GetTransporter( (Destination)null );
By providing a type on your call the compiler now will know which overload you are attempting to call and the world will be a happier place.
About The CodeBetter.Com Blog NetworkCodeBetter.Com FAQOur Mission Advertisers should contact Brendan
Subscribe Google Reader or Homepagedel.icio.us CodeBetter.com Latest ItemsAdd to My Yahoo!Subscribe with BloglinesSubscribe in NewsGator OnlineSubscribe with myFeedsterAdd to My AOLFurl CodeBetter.com Latest ItemsSubscribe in Rojo
Member ProjectsDimeCasts.Net - Derik Whittaker
Friends of Devlicio.usRed-Gate Tools For SQL and .NETNDependSlickEdit SmartInspect .NET Logging NGEDIT: ViEmu and Codekana LiteAccounting.Com DevExpressFixxNHibernate ProfilerUnfuddle Balsamiq MockupsScrumyJetBrains - ReSharper <-- NEW Friend!
Site Copyright © 2007 CodeBetter.Com Content Copyright Individual Bloggers