# Saturday, 15 March 2008

anug_logo_200x85 Saturday 15th marks the date for the first ever code camp held by Aarhus .NET user group. We went with ASP.NET for beginners as the theme building a small blog application with Visual Studio 2008 and ASP.NET 3.5. Due to space constraints we'd set the upper limit for attendees to 12 and I'm happy to report that we had a full house. The skills levels of the attendees varied from people who'd never opened Visual Studio before to people with some knowledge about ASP.NET.

Brian put together a nice program which took the attendees through creating various new features for the blog application like adding comments and membership support for login. To get the attendees coding some of the interesting parts of the application Brian provided a nice starting point for the code camp with a blog application laid out nicely in a Visual Studio solution along with a database. Due to differences in how we usually work with the stuff we encountered some interesting problems with getting the database up and running in SQL Server Express. Turns out that the SQL Server engine is prohibited from accessing user folders on the machine it's running on.

Coaches were in place to help out with questions the attendees might have, Søren Lauritsen who signed up to help at the last minute provided valuable help during the day, as did Brian himself between the short tech briefings, and myself. Every single attendee came well prepared and had all the prerequisites installed before showing for the code camp, thank you all for being so well-prepared.

 

 IMG_2234 IMG_2228 IMG_2235

 

During the day we had nice discussions on various aspects of ASP.NET, .NET in general, and we provided a number of tips and tricks like looking at compiled assemblies with Reflector and taking that a bit further with TestDriven.NET which enables you to simply right-click on a referenced assembly in Visual Studio and open it up in Reflector; no more digging around the file system to find that pesky assembly.

URLRewriting.NET was discussed along with ASP.NET MVC for creating friendly URLs. And I should mention that I'm doing a presentation on ASP.NET MVC in April If you want to know more about that in general. I'll post more information about that meeting once I know more about the particulars. As always the date and time is set so make sure to mark your calendar for April 30th 18:00 if you wish to attend.

Thanks to all who attended the code camp my impressions are of a successful day during which the attendees learned a lot. With that I'll leave you with some more pictures from the day. As you can see people were deeply focused but still very eager to help each other out. Nice work everybody!

Be sure and grab Brian's source code and presentations. Also you can check out all the pictures taken at the meeting.

IMG_2238 IMG_2243 IMG_2241 IMG_2231 IMG_2240 IMG_2237

posted on Saturday, 15 March 2008 12:35:50 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback
# Thursday, 13 March 2008

Commerce-Server-2007-Logo In my mini series about the Commerce Server development experience I did a piece called Magic Strings Galore which describes the general tendency to have all data in the various CS objects accessible via strings. Imagine a product with a rich description. You would access that like a hashtable, e.g. product["RichDescription"]. No way of knowing the return type , no discoverability via intellisense, poor refactoring support. Sure ReSharper takes care of some of that by looking at string literals when doing refactoring but surely there must be a better way to fix this. It turns out there is and I'm going to let you in on the secret :)

In my previous post .NET Framework 3.5 and Microsoft Commerce Server 2007 A Match Made in Heaven I discussed some options for using extension methods to add missing functionality to the built-in Commerce Server classes. Using this method you can augment the existing interface of Commerce Server but it doesn't really provide you with a nice place to put all the domain logic that is bound to turn up eventually. To solve this problem I came out with an automatic mapping layer sitting on top of the Commerce Server Profile System which translates the stock Profile into rich domain entities filling in the gap and giving you that place to put your custom logic and at the same time doing away with all the problem with magic strings that I described above. I call it the ProfileRepository.

The ProfileRepository is not a true object relational mapper in the sense that I'm not really converting from the relational model. Luckily all that is taken care of for me by the profile system of Commerce Server so I pretty much just have to provide the type safe abstraction.

Requirements

ProfileRepository My requirements for the ProfileRepository is the following. I want the developer using the framework to easily be able to map an entity, say User, to a profile, say UserObject.

Additionally I want to abstract the actual implementation of the entity by only working with interfaces so the consumer of the framework has the freedom to switch implementations, e.g. for unit testing or later in the development phase of the application.

Finally I want the consumer of the entity to be blissfully unaware of Commerce Server sitting underneath the repository; basically a full implementation of the repository pattern as outlined by Martin Fowler.

With that in mind we end up with a basic class hierarchy in place as depicted in the class diagram in the picture to the right.

Mapping Engine

ProfileMapper With the basic class hierarchy in place lets take a look at how the actual mapping of a Commerce Server profile to an entity happens.

Interestingly the Profile System operates with a type system completely separate from .NET and indeed COM making mapping interesting. The type system is pretty weak and doesn't express everything needed to perform mapping of all data types. For example there's no way of telling the difference between an association between two profiles and a string; they both turn up as a string.

To work around this limitation I went with assumptions based on the target of the mapping. So from the type of the actual target property on the entity I can deduce that we're dealing with an association because the actual type is IProfile and not string. Same thing goes for GUIDs and strings which also show up as the same thing; a string. Now I love the string type as much as the next guy but this is borderline ridiculous :)

To perform the mapping I employ a mapping engine which knows about all the mapping rules supported by the engine like rules for handling primary keys, one-to-one relationships, one-to-many relationships, value types, DateTimes, Guids, etc..

Each rule is an implementation of the specification pattern meaning that the engine will evaluate against each mapped property of the target entity and determine whether a particular rules is applicable to current property. Each rule employs reflection to determine whether that is case so the GuidMappingRule would use reflection to determine whether the type of a property on the entity is in fact a Guid.

Creating a Mapped Entity

To create a mapped entity you need to perform three simple steps: Create the interface which will expose the entity, e.g. the IUser interface. Second create the actual implementation of that interface, e.g. the UserObject class. The third and final step is to decorate the properties of the implementation with mapping information. Simple and easy. The code for IUser and UserObject might looks like this:

 

public interface IUser : IProfile

{

    string FirstName { get; set; }

    string LastName { get; set; }

    IAddress PreferredAddress { get; set; }

}

 

[Profile("UserObject")]

internal class UserObect : IUser

{

    [ProfileProperty("first_name")]

    public string FirstName

    {

        get { ... } set { ... }

    }

 

    [ProfileProperty("last_name")]

    public string LastName

    {

        get { ... } set { ... }

    }

 

    [ProfileProperty("PreferredAddress")]

    public IAddress PreferredAddress

    {

        get { ... } set { ... }

    }

}

 

Loading an Entity

With the mapping complete loading an entity is pretty straightforward: You new up the profile repository and call the generic method Get<T> with the key of the profile you want and presto you get an instance of the IUser interface returned to you complete with associated entities, the preferred address in the case. The Key class might seem superfluous but there's a point to it as it enables support for multiple key types like Guid, int, etc..

 

IProfileRepository profileRepository = new ProfileRepository();

IUser user = profileRepository.Get<IUser>(new Key("{EEDA89C9-E231-4002-AC24-7FD7FAB2F2FD}"));

 

All the Rest

Your spider sense is probably tingling by now. How's the ProfileRepository able to figure out which implementation of the IUser interface to instantiate? The answer to that is a piece that I omitted in my previous description: Sitting inside the ProfileRepository is an inversion of control container (IoC), in this case Windsor from the Castle project, which dynamically instantiates the correct type based on a configuration file.

Interestingly Windsor will be a key component in coming features in the ProfileRepository. As it stands today there are a number of improvements that can be made to it. Most prominently is implementation of lazy loading. All associations are eager loaded today which means that if you ask for a any one profile entity you'll get a complete object graph back which might not be suitable for all scenarios especially if we're dealing with many associated profiles.

With Windsor in place I intend to employ dynamic proxies to instantiate modified types with the lazy loading pattern injected into the relevant properties. Thanks goes out to Søren Skovbøll who came up with the idea for this and even provided me with POC code. His general knowledge on ORMs came in handy for a couple of things on this too :)

There are several opportunities for other performance improvements. The ProfileRepository uses reflection quite extensively to perform the automatic mapping which as you know is a costly operation. For a future release of this guy I'd like to throw in some caching for the rules which employ the reflection routines. The net result here would be that the rule is evaluated once per property and entity and from that point on reflection is only used for actually initializing the values of the properties.

Finally the ProfileRepository is load-only at this point and naturally I'd like to get create and update functionality in there as well. A customer self-service module would definitely need this feature in place to enable users to edit their user profiles, signing up for newsletters, etc..

With ProfileRepository I've tried to bring the full power of the profile system as a general purpose data access to bear in the sense that what we've got with the profile system is very cool and flexible but needs just that little bit extra to provide a nice development experience as well as something that supports the overall maintainability of the system.

posted on Thursday, 13 March 2008 21:46:16 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback
# Wednesday, 12 March 2008

Commerce-Server-2007-Logo Imagine this scenario: The year is 2003 and you're a Commerce Server developer who is setting out on the first Commerce Server 2002 and .NET project. Exited? You bet I was :) Way back we were asked to subcontract on a large ticket booking system developing the user profiling piece for the solution. With my background in OO naturally I wanted to go ahead a create business entities from the profiles in Commerce Server by inheriting them building entities specific to my needs.

Now you know what I ran into by trying to do that right? Pretty much a brick wall behind yet another brick brick wall. What I ran into was the fact that the Profile class is sealed and the brick wall behind it was the ProfileContext which is also sealed. Pretty much my idea was dead in the water at that point. So to return to the point of this post. Today several years after the fact it dawns on me that I have the perfect new tool to actually get some of what I wanted back then: Extension Methods.

As you probably know extension methods is a way of spot welding new functionality onto existing classes whether they are sealed or not. Basically a handy way of making external functionality available where you need it. Lots and lots of articles about extension methods have been written about the topic so suffice it to say that you put your extension methods in a particular namespace and to make them available on the target types you simply import them into your current context via a using or Imports statement depending on your particular language fetish :) With that out of the way lets skip right ahead to how you might use them in Commerce Server.

You can envision a scenario where you augment the profiles with specific functionality based on what you're doing. Say you're creating a product review profile in which you store the customer's opinion about a particular product. Use extension methods on that guy to include rendering a star rating, setting the customer review description, or administrative methods like approve/reject review.

For the product classes like ProductFamily, Category, etc. which are also sealed you could opt for something else entirely. From time to time we utilize the concept of search category, i.e. a category where the child categories and/or products are determined by a search criterion. Instead of putting this functionality in a helper somewhere you could go for a extension method to load the child objects of your current search category.

But why go for the individual entities themselves, why not go for the services published by Commerce Server like ProfileContext or CatalogContext to provide enhancements to the core functionality? I can certainly see some interesting scenarios enabled in the Marketing system, a subsystem which traditionally hasn't been very open to extensions.

To sum up extension methods is a brand new extension point for Commerce Server. Commerce Server has traditionally been very extendable but some areas are completely locked down. Until now that is. The scenarios I describe above is one way to do away with a lot of helpers floating around. Of course a much better way of accomplishing this is fashioning a facade layer on top of Commerce Server which will accommodate not only your custom logic but more importantly support your automated test suited (you do have one, don't you? :)). The facade layer will be the subject of a future post as I feel very strongly that architecturally this is a great thing to have in place to support you now and in the future.

Also let this post be a cry to the powers that be at Cactus to open up the APIs for inheritance. I certainly understand the need for sealing stuff especially with COM lurking under the covers but please, pretty please don't let that baggage spill over in future versions when COM is completely removed from the product. Finally sprinkle some interfaces on top and I'll promise to be a good boy from here on in :)

posted on Wednesday, 12 March 2008 16:28:20 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback
# Tuesday, 11 March 2008

Final proof that spammers are the root of all evil? Should I be worried at all that diabolical powers are taking over my inbox? Or even more worrisome that I took the time to actually make a post out of this? :)

Spam-666

posted on Tuesday, 11 March 2008 19:44:38 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback
# Monday, 10 March 2008

aspnet A quick note to let you know that this Saturday we open the doors for the first code camp in the history of Aarhus .NET user group. The fun starts at 9:00 and we're digging into ASP.NET and building a blog application, just because we can :) The goal of this code camp is to give you a sense of what's available in ASP.NET and how to use some of it.

During the day you'll be able to ask the experts for help and meet some of your fellow aspiring ASP.NET developers.

Please note that the number of attendees is capped at 12 as we can't seat any more than that.

Read more and sign up.

posted on Monday, 10 March 2008 21:35:43 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback

CarboniteLogo For a while now I've had a nice file server sitting under my desk at home for storing everything in a nice central location. One problem with the solution though is the complete lack of backup on that particular box. I really wanted to run Mozy on the box to get everything backed up off site in a simple and cost effective manner. One problem though: Mozy Personal doesn't support Windows Server which is running on my file server.

In my post Online Backup: Carbonite vs. Mozy I declare Mozy the winner due to a number factors like features, performance, and configurability. What I failed to mention though is the fact that you cannot use Mozy Personal for backing up a Windows Server box. For that you have to spring for a professional plan where you're charged by the gigabyte instead of a flat rate. For me that's not really an option as I've got a look of stuff to back up. For a couple of gigabytes it's probably fine.

Carbonite however allows you to back up a Windows Server box with the personal edition which means that it's a more cost effective solution for the home. Since I did the comparison Carbonite has added the number one feature I felt was missing from the solution: Version history. Mozy had this and Carbonite now does too. One thing hasn't changed though, Carbonite is still pretty slow to the tuned of 100k/s when you back up. Mozy will complete the initial backup approximately four times faster than Carbonite. Not a big deal once you're over the initial backup and into regular running mode with only updated files are transferred but getting over that initial hump does take a long while.

So to sum it up: With Carbonite allowing you to run the client on a server box it's a compelling offer for those of us running home servers wanting offsite backup. For everything else I'd still say that Mozy is superior. Alternatives like Amazon S3, JungleDisk (using S3), etc. are still way more expensive to use since they too charge by the gigabyte.

Mozy allows you to pay by the month allowing you to opt out immediately should you find a better solution. With Carbonite you're locking in for a minimum of a year. Where. Network performance with Mozy is a lot better than what you get with Carbonite, though that might just be me being located far away from the Mozy data center. The Mozy client still offers oodles more configuration options than what you find in the Carbonite ditto.

posted on Monday, 10 March 2008 21:06:49 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback

SharedView-Start-SessionIt's been a while since I updated my toolbox so I thought I'd do a little post about my most recent addition: Microsoft SharedView.

What is SharedView? As the name implies it's a way of sharing what you see on your computer with others remotely. I've had to muck around with LiveMeeting a lot lately and boy is that a piece of work in the sense of me not being able to get anything done within a reasonable amount of time. Setting up a shared desktop experience with that stuff is like swimming in molasses: A lot of effort and very little reward. You have to pay to use it too.

Contrast this experience with SharedView where you're up and running in a matter of a couple of minutes. Did I mention that's completely free to use? To test out SharedView I tried it out with a partner abroad. I hadn't mentioned anything about it before the meeting but we literally had the program up and running within two minutes sharing the presentation that I needed him to see. Cool stuff! The experience has that Apple-feel to it: I got the job done, nothing more, nothing less.

SharedView allows you to share a single program window or the entire desktop and you can hand over control of the window to any participant in your current session. Setting up a new session is a simple matter of you logging in with your Live ID and clicking "Start new session" which will provide you with a link you can send to the participants of the meeting. That's it.

I definitely see this little gem coming in handy with customer meetings where we need to do sprint demos but can't come on site to do so. With a new delivery going down each month having the entire dev team on site is somewhat of a drain on the customer.

Download Microsoft SharedView

posted on Monday, 10 March 2008 20:44:54 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback
# Monday, 18 February 2008

Microsoft_Team_System_Logo I'm proud to announce a very exiting meeting for Februar: The guys at Systematic are going to tell us all about their experience with Team System. Topics for the meeting include:

  • General introduction
  • Configuration management
  • Continuous integration

Following that we'll some insight into the world of software engineering at Systematic working with CMMI level 5 and agile processes like SCRUM.

The meeting will take place February 27th 18:00 at:

Systematic Software Engineering A/S
Søren Frichs Vej 39
8000 Århus C.

Signup and more information

posted on Monday, 18 February 2008 11:15:49 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback
# Wednesday, 06 February 2008

anug_logo_200x85 With the release of Visual Studio 2008, C# 3.0, and VB 9 in November last year we felt that it would be nice to get some information out there on what to expect of the new language features available in the new versions.

As always I started out the meeting by summing up what the core group has done since the last time around; we've been quite busy too. I'm very proud to announce that we've booked meetings at various companies around Aarhus until May. We do have a gap in April but I expect to put on a little song and dance about the MVC framework for ASP.NET.

IMG_1998 IMG_1999

IMG_2000 IMG_2001

IMG_2002 IMG_2003
IMG_2004

Future Meetings

So what have we got planned for you guys to enjoy? February will bring us a talk from the guys at Systematic where they'll tell us all about their experience with working with Team System with CMMI and SCRUM. Personally I'm looking forward to this one quite a bit as we're using Team System internally at Vertica as well; I know that many of you guys are too so I expect that a lot of very useful information will come from this one.

March brings us not one but two events: First there's our code camp for people who want to know more about ASP.NET where we'll continue building our blog application. I expect that we can accommodate approximately ten people and those attending will have to bring their own laptop. we'll do a full Saturday better our hands dirty in the code. More information on this will follow shortly so stay tuned.

Later in March we'll head back out to Scanvaegt where Henrik Kristensen will give a talk on his works with Workflow Foundation. He's been doing a number of POC applications and is very eager to share his experiences with the rest of us.

ASP.NET MVC Framework has garnered a lot of attention lately, but what is it and why should you care? I'll try and explain this with my talk on it in April. I'm doing an internal talk at Vertica this Friday and I figured that you guys would find it interesting as well. There are certainly some nifty techniques at play in the framework that I'm looking forward to sharing with you.

Finally Klaus Hebsgaard from Kristelig fagbevægelse will head up the April meeting with his talk about WCF. He's doing some very interesting work with WCF in conjunction with a large SOA project at KF and I know for certain that he'll have a lot to say about the topic.

Facebook

As an experiment we decided to do sign up for the meeting via Facebook. While it's been quite a success there are a couple of flies in the ointment: 1) Some people really dislike Facebook and outright refuses to use it. 2) Some companies actively block Facebook in their firewalls.

In the light of this information we'll not do exclusive sign ups on Facebook in future. Carrying forward we'll do Facebook sign ups primarily but also allow e-mail sign up as to allow everyone full access to the user group. Additionally we'll make sure that all relevant information will be available from the anug.dk web site.

Jobs

Interestingly we were contacted by a company owner who wanted to make our members aware of a job opening at his particular company. Our stance on this is that we won't advertise job openings in the interest of keeping our purpose clear and keeping a neutral position with respect to the companies kind enough to let us use their offices for the meetings.

Meetings Outside Aarhus

We've discussed the idea of holding meetings outside of Aarhus as a number of interesting companies exist in the vicinity. When asked though the members of the group indicated to me tat they weren't willing to travel too far outside of the city to attend our meetings. So we'll try and keep the meetings local as to not impose too much of a travel burden on the attendees.

Language Features in C# 3.0 and VB 9, Henrik Lykke Nielsen, Captator

The main attraction of the evening was of course .Henrik, Microsoft's RD for Denmark, and part owner of Captator.  I asked Henrik to give this particular talk because I know he's very fond of VB and I really wanted to see even attention given to both languages. It turned out though that when asked the attendees were interesting in C# 3.0 only so VB was mentioned in passing but the upshot of course was the fact that Henrik able to gauge the interest of the attendees and adapt his talk accordingly. Tip of the hat for that.

Henrik gave a very detailed talk on C# 3.0 and we even got into some IL discussions along the way which was a nice twist on the evening. I must say that I'm impressed with Henrik's deep knowledge on the subject having given a similar talk myself internally at Vertica I figured that I knew most of what he was going to say still I got a couple of nuggets of gold to take home from the meeting.

To understand many of the new features of C# 3.0 you need to understand what's already put in place in previous versions of the languages and again Henrik did an admirable job of getting everyone up to speed before moving on to the new features.

Slides are forthcoming as I'm still waiting to receive them from Henrik. While you're waiting for those why not head on over and take a look at his blog?

posted on Wednesday, 06 February 2008 21:32:18 (Romance Standard Time, UTC+01:00)  #    Comments [2] Trackback
# Tuesday, 05 February 2008

Commerce-Server-2007-Logo A while back a friend of mine posted a comment here asking me to describe what it's like developing with Commerce Server 2007. Initially I wanted to reply to him in comments but thinking more on it I really want to provide a different and real perspective on how Commerce Server is to work with as a product, a perspective in which I want to dig a deeper than the usual how-to and tutorials you see on the various Commerce Server blogs; mine included.

Check out part 1 Secure By Default where I discuss the security aspects of Commerce Server, part 2 Three-way Data Access in which I write about the various ways of getting data into your applications, part 3 Testability which not surprisingly is all about how CS lends itself to unit testing, part 4 Magic Strings Galore where I take on low level aspects of the APIs, part 5 Pipelines where COM makes a guest appearance in our mini series, part 6 which is all about getting your solution into production, and part 7 where I rip into the reference shop implementation: The Starter Site.

The Good Stuff

In this the final part of my mini series about developing Commerce Server I'm going to cover the stuff that I love about working with Commerce Server 2007. While I didn't start out with a particular roadmap for this series of articles I've noticed a trend when I look back over the posts: They aren't very positive about Commerce Server. Why is that? Does it mean that Commerce Server is a bad product? The answer to that eluded me for a while until our salesman pointed out a particular fact about engineers: Our job is to know the weak spots of the technology we're working with in order to produce the best possible solutions. While this is great trait for an engineer it certainly doesn't make for a great sales person :). I guess the reason for my negative slant stems from this fact: For me and my team to deliver the very best Commerce Server solutions we have to be constantly aware of any and all weaknesses of the product which is why I naturally gravitate towards that mode of describing the development experience.

So to answer the question posed above: Is Commerce Server a bad product? Certainly not, actually I enjoy working with a very mature platform which provides a lot of great features out of the box. Actually I've found myself in the fortunate situation of being able to tell a customer that, "yes we can do that out of the box", more often than not. I truly enjoy that part of my job because I find that customers are used to not getting anything out of the box if they're coming from the traditional business which started out on the web on a custom solution.

Actually I come across two types of distinct businesses when I go out and do Commerce Server work in the field: The business which primarily grew out of the web with the webshop at the core and the traditional business with the ERP at the center. As I mentioned above Commerce Server is a very compelling offer for the webshop-centered business because it provides a much more sound foundation than the custom built solution. The benefits for the traditional business are of course the same but interestingly I've found that Commerce Server is aligned very well with the way ERP guys typically think about a business. A good example of this is the rich way in which we can express business data in Commerce Server, in the areas of the ERP which concern a webshop we're able to not only match the capabilities of ERP systems but in some cases even surpass them, e.g. richness of the order schema and the way shipping is handled, the flexibility of the catalog, etc..

Were I to use a single word to describe Commerce Server it would be "flexible". Flexible in every sense of word as you can customize every aspect of the product to the suit the needs of the customer. Pretty the only limitation you'll come across is your own knowledge about the platform. With the right knowledge you can shape Commerce Server to suit the particular requirements of your customer which is why getting the right people working on your Commerce Server-project is essential for the success of it. You might argue that this is the case for all types of projects but I've seen how bad I project can go if the people working on a Commerce Server project lacks the proper skills to do so. The sound foundation I wrote about previously suddenly starts to look pretty wobbly and you end up in a situation where the platform is working actively against your business instead of with it.

So what happens if you get the right people working on your project creating the right architecture? Something akin to magic that's what. With the projects we've got going on right now I see one particular trend: The architecture that we're putting in place on top of Commerce Server leveraging the platform without working against it is actually opening up new avenues of possibilities for us as the projects move forward. Instead of feeling barred in by the choices we make I increasingly find that our solutions just support new requirements from the customer either "automagically", with reconfiguration of the existing system, or with very little modification to the system because the features were built on the sound foundation that is Commerce Server. That and of course the fact that I've got the privilege of working with the best damn e-commerce team out there :)

posted on Tuesday, 05 February 2008 21:21:13 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback

Commerce-Server-2007-Logo In light of the success of the Aarhus .NET User Group on Facebook I went ahead and created a Facebook group for Microsoft Commerce Server for all of us working with the product. If you have an interest in getting in touch with people with deep Commerce Server knowledge please don't hesitate to join the group. Prominent people like Ryan Donovan and Max Akbar are already in there so why aren't you? ;)

Microsoft Commerce Server Facebook group

posted on Tuesday, 05 February 2008 19:42:14 (Romance Standard Time, UTC+01:00)  #    Comments [0] Trackback