Entity Framework (EF)

by forcing iteration by invoking the ToList(), FirstOrDefault() or similar method, or looping through a foreach structure.

•••• Shawn Wildermuth dispels The Fable of the Perfect ORM on 8/30/2008. He points out that at least the following factors enter into the selection of the object-relational mapping tool:

Functional Requirements

System Requirements

Skill-set of the Development Team

Business Factors

Time-to-Market

Business Culture

Lifetime of Project

Volatility of Schema

Shawn goes on to give examples of criteria for using LINQ to SQL, NHibernate, LLBLGen Pro, or Entity Framework.

•••• Julie Lerman’s More on Foreign Keys in EF post of 8/30/2008 describes a partial class to return the scalar value of a foreign key from an EntityReference. Foreign key values are useful for specifying associations with dropdown lists. (LINQ to SQL delivers foreign key values by default.)

•••• Matthieu Mezil demonstrates in his EF and WPF post of 8/29/2008 part of the code required for a WPF master-details application that enables using a combo box to choose the master for the details window.

•• Jacob Proffitt’s Changing Table Names in an OR/M post of 8/27/2008 explains how to change EF and LINQ to SQL table names to suit a particular naming convention in both O/RMs’ XML mapping files. Jacob finds changing table names in EF is more difficult than LINQ to SQL.

What is cool about this approach is that you can issue LINQ queries on the client (in Silverlight 2) that will communicate with Data Services via the REST interface and execute queries and update data on the server. The substantial difference that you will have to get used to is the use of Asynchronous LINQ queries in Silverlight 2. Check out the article for all the details.

Symmetry with on-premises instances will be outside-in rather than inside-out.

SSDS have the capability to hand on-premises data to SQL Server Integration Services (SSIS) and pump it up to the cloud or vice-versa.

Microsoft Sync Services will make SSDS into a “data hub.”

SQL Server Reporting Services and Analysis Services will be used to deliver reports on uptime, performance and the like.

SSDS will grow to 5,000 nodes next year.

Open beta will occur shortly after PDC 2008 and SSDS will go live in the first half of 2008.

SSDS will be the preferred store of compliance data for large businesses.

••••Dave Robinson says in SSDS Tech-Ed Online Video Posted of 8/29/2008 that the interview was conducted at Tech•Ed North America 2008, which was held in early June, so the “very near future” appears not so near after all.

Microsoft platforms have not historically encompassed the continuum of access styles. Happily, the emerging cloud does. And while the novelty of “just coding to a URL” on a Microsoft platform will undoubtedly attract some tirekickers who otherwise wouldn’t show up, the real draw will be the ability to exercise choice along the whole continuum.

That choice, by the way, is not only relevant to developers accessing hosted, web-facing services. It will matter as much — and with the SOAP option, even more — to developers accessing on-premises services behind the corporate firewall and across corporate boundaries. Facing outward or inward, you’re most productive when you can choose the right access style for the job at hand. Between the realm of the 100% Microsoft coder and the 0% Microsoft coder, a wide and fruitful middle ground is opening up.

The SSDS team is to be commended for keeping testers up to date on the progress of bringing the service back up. Their response contrasted with Amazon’s failure to alert customers of their February outage, as reported in Amazon Web Services Outage: Causes And Remedies of 2/16/2008.

•• The preceding post has been updated for subsequent events and blog posts and was moved to a separate post due to its increasing length.

ASP.NET Dynamic Data (DD)

I have my tables organized into 4 categories: Sales, People, Products and Reports. And the cool thing is that this menu is completely dynamic. You can add, remove or reorganize the categories without touching the UI. And depending where you are keeping your metadata you could even do this without recompiling your app. The grouping is automatically discovered from the metadata and the menu is built solely off the it so everything 'just works'.

Besides adding the grouping information, I also tagged each of my tables with a custom description that I am displaying under the grids title. Nothing too complicated, but still interesting.

LINQ to SQL

•••• Damien Guard’s T4 template update for LINQ to SQL in his original post of 8/29/2008 was premature. The latest version will add:

Inheritance support

VB.NET generation

DataContract serialization

But it won’t support stored procedures or composite primary keys.

An updated link in future issue of this post will be provided when the template is fully cooked.

•••Rob Conery’s Working With Linq's Expression Trees Visually post of 8/29/2008 shows you how to extract and install the Expression Tree Viewer add-in from the CSharpSamples.zip file and use it to view LINQ to SQL expression trees.

I will shortly be posting a framework that makes working in a POCO and DDD style much simpler. The Framework includes implemented Specification Patterns, Fetching Strategies and Extensible Repositories for your LINQ To SQL pleasure.

Jeff Atwood recommends adding the NOLOCK statement to SELECT queries to solve a mysterious deadlock problem with LINQ to SQL in his Deadlocked! post of 8/25/2008.

In this post I want to point out that you can actually live without that visualizer and go the hard-core way using SOS, … the WinDbg extension for managed code debugging that ships with the .NET Framework (sos.dll). A little-known fact is that SOS can be loaded directly in Visual Studio through the Immediate Window.

•• Martin Hinshelwood says LINQ to XSD is “Absolutely brilliant” in his LINQ to XSD post of 8/28/2008. I’d say the LINQ to XML team would be “absolutely brilliant” if they posted a LINQ to XSD release version instead of Alpha 0.2.

• Bart De Smet continues his dissertation on dynamic expression trees with To Bind or Not To Bind – Dynamic Expression Trees – Part 1 of 8/27/2008 by creating a DynamicExpression abstract class with MethodCallDynamicExpression, LambdaDynamicExpression and ParameterDynamicExpression factory classes.

• Jared Parsons uses a LINQ to XML-powered word-wheel application to demonstrate how to “Increase LINQ Query Performance” in his “Basic Instincts” column for MSDN Magazine’s August 2008 issue.

Visual Studio 2008 Service Pack 1 (General)

A simple demo of building a "Hello World" application with Visual Studio 2008 Service Pack 1 and then deploying it via ClickOnce to a clean Windows XP machine using the .NET Client Profile rather than the full .NET Framework.

If you're building something like a .NET WPF client application then one of the major bugbears (for both you and for Microsoft - let's be honest here :-)) is trying to get the .NET Framework on to the client machine.

We're trying to make this easier but if you were to look at the recent .NET Framework packages you'd perhaps not be able to tell that :-)

Miscellaneous (WPF, WCF, Silverlight, etc.)

•••• Phil Haack announces ASP.NET MVC CodePlex Preview 5 Released on 8/29/2008 and says the next release will be the official beta version. The post contains links to other important MVC-related articles.

•• Roger Jennings takes Jaiku’s new Google App Engine implementation to task in Jaiku + Google App Engine = Fail of 8/28/2008. The malformed home page shown in the post, which has lost its CSS content, took over a minute to load.

• The IE Team posted IE 8 Beta 2 for download on 8/27/2008. I installed it to try Web Slices on LINQ and Entity Framework Posts for M/D/YYYY+. The first item in this category recounts my lack of success with implementing Web Slices.

Saturday, August 30, 2008

Ayende Rahien (a.k.a. Israeli .NET developer Oren Eini) contends in his Does you application has a blog? post of August 29, 2008 that applications should have blogs that report on “interesting events” that occur during their operation. Not blogs by developers or operations staffers about applications, but blogs authored by the application itself.

Ayende offers an Order Management example blog and says:

From the point of view of the system as a whole, now business users have a way to watch what the system is doing, check on status updates, etc. More than that, you can now use this as a way to post reports (weekly summary, for example) and in general vastly increase the visibility of the system. …

Now, this is explicitly not a place where you want to put technical details. This should be reserved to some other system, this is a high level overview on what the system is doing. Posts are built to be human readable and human sounding, to avoid boring the readers and to ensure that people actually use this.

Soumitra Sengupta mentioned during his Tech*Ed 2008 North America interview by Greg Hughes that the SQL Server Data Services (SSDS) team planned to use SQL Server Analysis Services and Reporting Services to keep users up to date on the services’ overall performance as well as usage and performance statistics for individual authorities.

Such reports seem to me to be ideally suited for transmission in the form of public and private blog posts with the appropriate Atom/RSS feeds. Adding a bit of Business Intelligence (BI) in the form of trend analysis and anomaly detection would also benefit users.

Comment: “A normalization error has occurred” without further explanation or pointer to a page with a solution for the problem is another example of an Error Message from Hell.

The FeedValidator site says the Atom.xml file is OK, as shown here:

Atom.xml opens as expected in IE 8 Beta 2 and Firefox 3, as well as Visual Studio 2008’s XML Editor. Firefox has no problem with opening pages to the two pages with the …/#oakleaf anchor and positioning at the Web Slice’s location.

So the question is: Where did I go wrong?

Update 8/31/2008: The problem appears to be related to IE 8 Beta 2’s reaction to the URL’s /# separator for positioning to a <div id=”oakleaf”> or <a name=”oakleaf”> anchor as in http://oakleafblog.blogspot.com/#oakleaf. The separator should not include /, but IE 8 inserts the / if #name is typed directly after the base URL. This URL works from the address text box in IE 8 Beta 2 and Firefox 3, but not from the OakLeaf LINQ and Entit… Web Slice dropdown in the favorites toolbar.

Thursday, August 28, 2008

Michelle Neylon announced Jaiku Back Online at 1:22 AM Thursday morning (8/28/2008) after being offline since last Saturday for a move from the Google acquisition’s single Finnish server to Google App Engine’s cloud infrastructure. Michelle concludes:

Twitter, even if it has a horrible UI, is still a lot more popular.

After waiting more than a minute for the home page to open, here’s the even worse UI that appeared for me:

Given the choice, I’ll stick with Twitter.

Full disclosure: After about 30 minutes I was able to load a more respectable version of Jaiku’s front page, but it still took more than 25 seconds.

08/26/2008 07:04 PDT: The SQL Server Data Services (SSDS) team reported by e-mail what appeared to be unscheduled downtime. The message consisted of two instances of “Maintenance Notification” and nothing else.

08/26/2008 07:16 PDT: SSDS was reported to be recovering and functioning.

08/26/2008 07:54 PDT: SSDS was reported to be healthy. (The delay between the start of the downtime and the message timestamp isn’t known.)The message said “Details to follow.”

08/26/2008 08:14 PDT: The preceding message appears to be a false alarm because a subsequent message arrived at 08:14 PDT:

We are experiencing an unplanned downtime of the service. We are working with utmost urgency to correct the problem and to bring the service back up.

"As a distributed system, the different components of S3 need to be aware of the state of each other. For example, this awareness makes it possible for the system to decide which redundant physical storage server to route a request to. We experienced a problem with those internal system communications, leaving the components unable to interact properly, and customers unable to successfully process requests. After exploring several alternatives, the team determined it had to take the service offline to restore proper communication and then bring service online again. These are sophisticated systems and it generally takes a while to get to root cause in such a situation," Amazon said. "We will be providing our customers with more information when we've fully investigated the incident."

The SSDS team is to be commended for keeping testers up to date on the progress of bringing the service back up. Their response contrasted with Amazon’s failure to alert customers of their February outage, as reported in Amazon Web Services Outage: Causes And Remedies of 2/16/2008.

The team is hard at work figuring out definitively what the root cause was and we will report back our findings and provide more details soon. Trust can only be built through transparency and consistently delivering on your promise.

••Muhammad Mosa discovers EF’s LINQ to Entities doesn’t support many constructs that LINQ to SQL handles with no problem. His LINQ to Entities, what is not supported? post of 8/24/2008 describes the following problems when migrating the data source of Rob Conery’s sample MVC Storefront project from LINQ to SQL to Entity Framework:

•• Matthieu Mezil’sTPH limitation which should disappear with v2 post of 8/23/2008 describes an exception when using Is Null and IsNot Null conditions on a table-per-hierarchy inheritence model (see below) that shouldn’t happen and needs fixing in EF v2.

• Matthieu Mezil discusses the use of NULL values in the type discriminator column of a table-per-hierarchy inheritance model in his TPH is more than a selection! post of 8/22/2008.

Entity Framework can't replace working within a well thought through Domain Model but it sure helps me in my everyday work. I just finished a drafting a part of our application that took month and a half to get up and running the first time (I was not involved in the first version). It took me 2 days to come up with a pretty much solid version 2 of that same part.

Mikael claims using EF reduced the 60 developer*day project to 4 developer*days (including 2 days for adding business validation rules)

• Ido Flatow shows you how to change CSDL and MSL files programmatically in his Change Entity Framework storage DB schema in runtime of 2/22/2008. Changing files at runtime enables working with experimental, developmental, and production databases without the need to change and recompile your EF code.

David Yack finds that adding a type declaration for the from clause’s parameter in a LINQ to Entities expression adds a Cast<T> operator to the translated expression in his Entity Framework - Cast Affects Include post of 8/21/2008. The spurious Cast<T> operator caused his Include() method to fail. Removing the type declaration solves the problem.

If you put together the notion of service documents with using JSON as the payload format for a service endpoint, you're close to getting the touted programmer friendliness of RPC technologies like XML-RPC & SOAP/WSDL while still building a RESTful service which works with the Web instead of against it.

The only problem is how to deal with statically typed languages like C# and Java. These languages need the types of the objects that application will consume from a Web service defined up front. So if we could just figure out how to come up with service documents for JSON services that included the notion of a class definition, we could pretty much have our cake and eat it to with regards to getting the ease of use of an RPC system while building a RESTful service.

Sounds to me like an interesting development that might apply to Astoria v2 and SSDS.

• Marcelo Lopez Ruiz adds another reason why you’ll probably want to use ES as Astoria’s O/RM data source, despite the performance hit when you first spin it up with a large number of objects. His EntityState problems in ADO.NET Data Service post of 8/22/2008 reports:

[I]f you are using a 'T' that's not the ObjectContext-derived type in your DataService<T>, the service won't know you're using an ADO.NET Entity Framework types, and will try to discover types and properties by following references, which eventually leads to the EntityState property problem. Using the ObjectContext type as the 'T' should then solve the problem.

So what is the potential of ADO.NET Data Services? In a RIA … or data driven application it can help to provide a cleaner solution. The ability of Data Services to return responses in JSON or in URI makes them flexible and usable by Ajax or Silverlight. Also, if the data service is consumed by a .Net client you can use the LINQ to [REST] feature. The Data Services come with other features like concurrency, batch operations over HTTP, full query operation support and more. These features, when used right, can help to provide good solutions for a data driven application.

can be accessed by serving appropriate warrants or other legal processes to the cloud provider. In some cases, this can be done without notifying the owner of the information (such as cases where they think that by knowing, the owner could/would/might destroy information). …

Perhaps we need a system where the information stored in the cloud is done so encrypted, with keys that are owned by the information owner only. That way the cloud provider returns to being just that, a service provider, without having responsibility (or access) for/to the data on their systems.

I believe that this is a topic that the SSDS Team should consider for implementation in v1 RTM but probably requires SQL Server 2008. (The present “CloudDB” runs on modified SQL Server 2005.)

ASP.NET Dynamic Data (DD)

• Scott Hunter’s Image Generation is released to CodePlex post of 8/21/2008 describes the long-awaited ASP.NET GeneratedImage replacement for Whidbey’s (!) DynamicImage feature that got cut from VS 2005. GeneratedImage is an ASP.NET control that automatically generates an ImageHandler and wires the control to its URL. The new control is ideal for use in DD projects.

ImageHandler is an implementation of IHttpHandler that, according to Scott, supports:

I am pretty impressed and I am looking forward to exploring this further - stay tuned.

Matt Berseth being “pretty impressed” with DD bodes well for its future.

Josh Heyse uploaded Dynamic Data Filtering v1.01, which eliminates private reflection, to CodePlex on 8/20/2008. Josh says he “worked with the Dynamic Data team to open up several internal method calls to eliminate the need for the reflection.”

Justin Etheredge’s Exploring System.Web.Routing post of 8/20/2008 is a detailed tutorial on the new URL mapping feature introduced by VS 2008 SP1 for ASP.NET MVC and DD.

LINQ to SQL

••• David DeWinter requests potential users to suggest features for his LINQ to SQL utility that supports updating the object model from the database schema and property-name fixup in his DBML Fixup Preview post of 8/24/2008. David’s current feature list appears to duplicate the LINQ to SQL capabilties of Kris Andersson’s Huagati DBML/EDMX Tools. David has scheduled DBML Fixup for a year-end release.

•• Andrew Davey suggests a set of rules for creating “convention-based mapping” with LINQ to SQL’s XML mapping files to minimize manual authoring in his LINQ-to-SQL Convention Mapping Source post of 8/24/2008

• Beth Massi’s Data on the Smart Client Talk at Bay.NET UG post of 8/21/2008 provides a link to her databinding to WinForms and WPF clients presentation to the Bay.NET user group and its sample code. (The OMS.mdf database file required for the demos is in the …\LINQSQLDemo\OMSDataLayer folder.)

I would like to see MS give this product the support it deserves. I would like to see a commitment from the Data Platform team to stop its focus on talking LINQ to SQL down as a RAD tool and tallking up its advantages for use in the OO approaches to software development. For example I would like to see the Data Platform team talking about LINQ to SQL's support for POCO strategies, lazy loading, etc. and pointing out to customers who request those features. There needs to be more acknowledgement that if you want them you should consider LINQ to SQL.

Due to the nature of the access to this information, we are still working out the details for the open-source version we plan to offer soon. The world of the owners of this data has not caught up to the philosophy we have for transparency and shared value.

•• Jim Wooley says “LINQ to Reflection isn't really a provider, but just a specific implementation of LINQ to Objects which happens to query reflection information” in his LINQ to Reflection IsNot LinqProvider post of 8/23/2008.

Mike Ormond discovers in his LINQ to JSON post of 8/21/2008 that LINQ to JSON from Silverlight’s System.Json namespace saves a lot of code because he doesn’t need to define a .NET type to represent the JSON object.

SQL Server Compact (SSCE) 3.5 and Sync Services

No SSCE posts as of 11:00 PDT.

Visual Studio 2008 Service Pack 1 (General)

••Scott Hanselman’s SmallestDotNet: On the Size of the .NET Framework post of 8/23/2008 describes options for upgrading client PCs to .NET Framework 3.5 with installers that range in size from 10 MB to 56 MB, depending on the client’s current versions. Scott also describes the Client Profile:

The Client Profile is an even smaller install option for .NET 3.5 SP1 on XP. It's small 277k bootstrapper. When it's run on a Windows XP SP2 machines with no .NET Framework installed, it will download a 28 meg payload and give you a client-specific subset of .NET 3.5. If the Client Profile bootstrapper is run on a machine with any version of .NET on it, it'll act the same as the 3.5 SP1 web installer and detect what it needs to download, then go get it. There's more details in the Client Profile Deployment Guide.

Scott also announces his new SmallestDotNet page that executes a small chunk of JavaScript to inspect your browser’s UserAgent and determine what version of the .NET Fx is installed. The script reports the total size of the download to update to .NET Fx 3.5.

The Visual Studio 2008 Software Development Kit (SDK) 1.1 includes tools, documentation, and samples for developers to design, build, test and deploy extensions for Visual Studio 2008 Service Pack 1. You can also use the Visual Studio 2008 SDK 1.1 to create custom tools environments based on the Visual Studio 2008 Service Pack 1 Shell.

EBS is the final piece in the puzzle that had prevented Amazon's cloud computing platform from being comparable to traditional hosting solutions. With EBS Amazon is now superior to most traditional hosting solutions from a developer usability perspective as well as cost. Google App Engine now looks like a plaything in comparison. In fact, you could build GAE on top of Amazon's cloud computing platform now that the EBS has solved persistent custom storage problem.

Wednesday, August 20, 2008

Note: This post is updated daily or more frequently, depending on the availability of new articles.

• Updated 8/20/2008

Entity Framework (EF)

• Matthieu Mezil explains how to trick EF into accommodating a stored procedure for returning data from a SELECT statement that you don’t want to surface as an EntitySet on the ObjectContext in his EF: SELECT with a SP post of 8/20/2008.

Dan Simmons is back with Stephen Forte, a member of the EF Advisory Council, a group of outsiders who were brought in to help shape future direction for the EF. You might be surprised at who else was on the council, and what they are thinking about for the next version.

The action starts at 08:35, but Danny and Stephen don’t start talking about the first two-day EF Advisory Council meeting until 11:40. Danny gets into integrating the EDM with ADO.NET Sync Services starting at about 49:15 and a discussion of what’s coming in EF v2 at 52:20.

I believe that Stephen was referring to the EDM and its implementations for other scenarios, such as Reporting Services and Sync Services, not to Entity Framework, which is the O/RM implementation of the EDM. Microsoft’s Zlatko Michailov (in Entity Framework and Object/Relational Mapping of 12/14/2007) and others, notably Shawn Wildermuth (in “Introducing the Entity Framework” of May 2007), have muddied the water by stating explicitly that the “Entity Framework is not an object/relational mapping tool” or words to that effect.

• Julie Lerman’s Comparing times for loading models of different (exaggerated) sizes post of 8/20/2008 compares the times to cache the metadata of the AdventureWorksLT database (17 tables) and the 400-table database of her earlier Entity Framework Designer and Large Databases article. Julie’s 400-table example loads in about 4.4 seconds compared to about 9.5 seconds for my 120-table database. Her faster cache time might be due to a faster computer. I’m running Vista Ultimate on a Gateway desktop having a dual-core 2.8-GHz Pentium with 4 GB RAM (100+ MB free) and a 7,200-RPM SATA drive. Another possibility is a difference in the total number of columns and associations. My 120-table Huagati database has about 1,529 columns and 410 associations.

Matthieu Mezil’s SSDL View and CUD operations post of 8/19/2008 explains what happens when you “want to CUD an entity which maps on a table and (with Entity Splitting) a SSDL view?” He also includes the <Function> CUD elements you must add to the SSDL group and their mapping in the MSL group.

ADO.NET Data Services (Astoria)

• Aaron Skonnard’s new Wednesday Endpoint Screencasts series on Windows Communication Foundation (and Workflow) have started on Channel9. Here are links to the first two:

Configuring Services with Endpoints. This screencast introduces you to WCF adapters, bindings, and contracts, and shows you how to use the WCF Service Configuration Editor to modify these endpoints and add new endpoints. (8/20/2008)

Creating Your First WCF Service The screencast walks you through creating the service from scratch in VS2008 - defining a data contract, a service contract, and testing/hosting the service in VS2008. (8/13/2008)

One of the interesting items in this demo is a method GetMimeType(string filename) that uses the Registry to resolve the selected file's MIME type before sending it on to SSDS. The code is not very complex, but might have some security implications. I'd be curious to hear if anyone has trouble running the code in partial trust.

David Chappell’s “A Short Introduction to Cloud Services” white paper dated August 2008 provides “an enterprise-oriented view” of cloud computing as a whole, with the emphasis on cloud platforms:

As its name suggests, this kind of platform lets developers write applications that run in the cloud, or use services provided from the cloud, or both. Different names are used for this kind of platform today, including on-demand platform and platform as a service (PaaS). Whatever it’s called, this new way of supporting applications has great potential.

Microsoft sponsored the whitepaper but the document covers Amazon, Google and Microsoft platforms and/or services. There’s no mention of SSDS but it clearly conforms to Chappell’s definition of a cloud platform.

LINQ to Objects, XML, et al.

• Alex Thissen points out that “[t]he new XML API that comes with LINQ to XML has some peculiar ways of handling the XML declaration (aka the XML prolog)” in his XML declarations in LINQ to XML post of August 19, 2008, and then points out workarounds for the peculiarities.

Bart De Smet’s Probably The Most Powerful LINQ Operator: SelectMany post of 8/19/2008 tells you more than you might have wanted to know about the SelectMany() standard query operator. He analogizes SelectMany() with nested foreach statements that iterate over Cartesian products created by multiple from statements in LINQ queries.

According to Noam Ben-Ami who is on the team that works on the designer, the performance for the designer itself should have “typically reasonable” performance for up to “about 120 tables, after which things begin slowing down.”

Her test with a 400-table database took the EDM Wizard about 20 seconds to generate the visual model. “Saving the model took about 1/2 minute as it generated the classes.”

A 120-table database (from Huageti Systems), which is typical of those used by a reservation system for small airlines but contains no data, took 50 seconds to generate the model in the EDM Designer. The same operation with LINQ to SQL takes 22 seconds.

Update 9/30/2008: The entities have a total of 205 associations and 1190 scalar properties (9.9 properties/entity.)

What’s more important, in my opinion, is the time to generate the EDM at runtime. The following table lists the times to instantiate and cache the runtime EDM with LINQ to SQL and Entity Framework in a WinForm client.

Times to Instantiate a 120-Table Entity Data Model in a Windows Form Client

The dual Web role application has been running in Microsoft's South Central US (San Antonio) data center since September 2009. I believe it is the oldest continuously running Windows Azure application.

About Me

I'm a Windows Azure Insider, a retired Windows Azure MVP, the principal developer for OakLeaf Systems and the author of 30+ books on Microsoft software. The books have more than 1.25 million English copies in print and have been translated into 20+ languages.

Full disclosure: I make part of my livelihood by writing about Microsoft products in books and for magazines. I regularly receive free evaluation software from Microsoft and press credentials for Microsoft Tech•Ed and PDC. I'm also a member of the Microsoft Partner Network.