net, build, aspnet comments edit

MSDeploy is a pretty cool installer technology, but it’s not what everyone uses, and my personal experience so far is it’s not flexible enough for me. I’m not the biggest fan of Windows Installer, either, but you can’t say it isn’t flexible.

As part of a recent project, I was working on a web site installer. I wanted Wix to take my web site - including the transformed web.config files and such - and package it into an installer. I’m no Wix expert, but this didn’t appear to be as straightforward as you might think.

The usual way you consume project output (when using the Visual Studio integration and .wixproj files) is to add a project reference to the application project, set up the output you’re interested in grabbing, and telling Wix to “Harvest”:

Normally 'Harvest' is 'True' and 'Project Output Group' is set to
something.

If you do that for a web project, though, the installer ends up dropping the web project assemblies right in the root of the app instead of keeping them in the bin folder like they’re supposed to be. I understand why it’s doing that (because other app types would actually need it that way) but for web projects it’s wrong.

Not only that, but you know that whole fancy web.config transformation thing you can do when you use MSDeploy? Wix doesn’t do that transformation. I want that, so I figured out how to do it:

You can get Wix to automatically consume MSDeploy-staged output from a web project. It just takes a little manual hacking to get it to work. Not much, but it is manual.

First, add a project reference from your Wix .wixproj to your web project. In Visual Studio, set the properties on that project reference

  • set “Harvest” to False and “Project Output Group” to “None.” If you look at the .wixproj source, that reference will look something like this:

    UI {YOURGUID-HERE-abcd-abcd-abcdabcdabcd} True True INSTALLLOCATION

Once you have that, it’s time to do a little manual hacking.

Open up the .wixproj in a text editor. Find the ProjectReference you just added. Just before the end of the ProjectReference, add a metadata property called “WebProject” and set it to “True” like this:

<ItemGroup>
  <ProjectReference Include="..\..\UI\UI.csproj">
    <Name>UI</Name>
    <Project>{e3362c3d-45c4-47d3-ad61-9f6fbcad9b02}</Project>
    <Private>True</Private>
    <DoNotHarvest>True</DoNotHarvest>
    <RefProjectOutputGroups>
    </RefProjectOutputGroups>
    <RefTargetDir>INSTALLLOCATION</RefTargetDir>
    <WebProject>True</WebProject>
  </ProjectReference>
</ItemGroup>

This will set web projects apart from other projects - in case you have multiple project references going. This is how you can identify which ones are web projects that needs to be staged.

Now look in the .wixproj file and find a Target node with the Name “BeforeBuild” - there may or may not be one, depending on your project, so if there isn’t one, create one just above the closing </Project> node at the bottom of the file. You may even see a little comment that is there telling you where to place the Target. The empty Target will look something like this:

    <!-- ...here's your project reference... -->
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(WixTargetsPath)" />
  <Target Name="BeforeBuild">
  </Target>
</Project>

The magic goes inside the BeforeBuild target. What you want to do there is:

  • Use the MSDeploy stuff to “stage” the web site with all of its transformed configuration files and everything.
  • Tell Wix where the staged output is.
  • Tell Wix to generate a Wix source file that includes the entire staged web site contents.
  • Do this for all of the project references that are marked as WebProject = True.

That’s done with just a few lines of MSBuild script:

<Target Name="BeforeBuild">
  <MSBuild
    Projects="%(ProjectReference.FullPath)"
    Targets="Package"
    Properties="Configuration=$(Configuration);Platform=AnyCPU"
    Condition="'%(ProjectReference.WebProject)'=='True'" />
  <ItemGroup>
    <LinkerBindInputPaths Include="%(ProjectReference.RootDir)%(ProjectReference.Directory)obj\$(Configuration)\Package\PackageTmp\" />
  </ItemGroup>
  <HeatDirectory
    OutputFile="%(ProjectReference.Filename).wxs"
    Directory="%(ProjectReference.RootDir)%(ProjectReference.Directory)obj\$(Configuration)\Package\PackageTmp\"
    DirectoryRefId="INSTALLLOCATION"
    ComponentGroupName="%(ProjectReference.Filename)_Project"
    AutogenerateGuids="true"
    SuppressCom="true"
    SuppressFragments="true"
    SuppressRegistry="true"
    SuppressRootDirectory="true"
    ToolPath="$(WixToolPath)"
    Condition="'%(ProjectReference.WebProject)'=='True'" />
</Target>

(Note that it’s line-wrapped for readability, but in the .wixproj file it’s only five lines in the Target.)

Let’s walk through that:

  • The MSBuild task calls the “Package” target on each of the project references where WebProject = True. The property it passes in for “Platform” is AnyCPU because it’s a platform target that pretty much everything knows about by default. It’s important to specify a platform that all of the projects in your solution know about because that way project-to-project references will work. If you specify a platform that not every project knows about, you have to specify an OutputPath folder so the task can locate project-to-project build output locations… and that just gets messy. Stick with AnyCPU and make your life easy. (It also means output will be in expected locations like “bin\Debug” or “bin\Release.”) The “Configuration” value is used not only in figuring out where to build the staged output but also in your web.config transforms - that’s how it knows which transform to use. Finally, you’ll see the Condition on the MSBuild task - that’s how we make sure to only run this for web projects.
  • The ItemGroup sets up an item for Wix. Wix needs to know where the staged output base path is. The Package target builds its staged output by convention. It goes in a folder called: obj\[Configuration]\Package\PackageTmp under your web application. (Again, sticking with “AnyCPU” as the platform value means you’ll have a predictable output location.) “Configuration” gets substituted in with the “Configuration” variable you passed into the MSBuild task. So if you were building the Debug configuration of an application in C:\MyWebApp, the Package target would build to: C:\MyWebApp\obj\Debug\Package\PackageTmp The crazy long MSBuild-script-variable string you see there builds up the location of the Package output using that convention.
  • The HeatDirectory task builds the Wix source from the Package output. The file it builds will match the name of the ProjectReference, so if your web application is called “MyWebApp.csproj” then the Wix script it builds will be “MyWebApp.wxs.” The Wix component it builds (which you’ll need to know so it can be included in the main install script) is also named after your web application by adding a “_Project” suffix (e.g., “MyWebApp_Project”). You’ll see, again, the Directory we specify is that big long package output folder. And, finally, you also see that the Condition on the task makes sure we’re still only running this for project references where WebProject = True.

When that target runs, you’ll see a .wxs file pop out in the .wixproj project folder. Add the generated .wxs to your .wixproj project so it knows to include it in the build.

Add the .wxs file to the
.wixproj.

Finally, in your main Product.wxs file, add a ComponentGroupRef to the Feature that should install your web site and use the generated “ProjectName_Project” (e.g., “MyWebApp_Project”) component ID, like in this fragment:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="YOURGUID-HERE-abcd-abcd-abcdabcdabcd" Name="YourProduct" ...>
    <Feature Id="YourFeature" Title="Your Feature" Level="1">
      <!-- Include your generated components here. -->
      <ComponentGroupRef Id="UI_Project" />
    </Feature>
  </Product>
</Wix>

That’s it! (Actually, that wasn’t a small amount of work, but it’s not as hard as some things in Wix.)

Now don’t forget to set up the web site in IIS (that’s not done for you by the above - only getting the files into the installer). Good luck!

UPDATE 8/6/2010: I ran into an issue where a web project that has a project-to-project reference to a non-web project (e.g., a web project that references a class library as a .csproj rather than a .dll) may look in the wrong spot for the class library. I updated the article to accommodate for this. The change is thatif you specify AnyCPU as the platform, you don’t have to specify an output folder and things magically fall together.

UPDATE 3/8/2013: Updated code snippets for Wix 3.8, which brought some breaking changes. The MSBuild “BeforeBuild” snippet uses an ItemGroup with an item called LinkerBindInputPaths for the project package location instead of a PropertyGroup with a property called LinkerBaseInputPaths. Also, in the Product.wxs snippet, the reference to the Product.Generated component is removed since that is off by default in Wix 3.8.

gaming, xbox comments edit

My Xbox 360 profile was on a 512MB memory unit and with the USB flash drive update to the system, I figured it was time to move on up.

Xbox 360 has some sort of “test” it runs USB drives through to determine if the drive “meets recommendations” or is “below recommended performance.” If it’s below the recommended performance, you’ll supposedly get degraded experiences in loading/saving games, reading your profile information from the drive, etc. Unfortunately, the Xbox 360 storage page really doesn’t tell you which drives work other than their Xbox 360 branded drives.

To that end, I tried a couple of drives and found one that worked (the Lexar JumpDrive Firefly) and one that didn’t (SanDisk Cruzer). I figure I’ll put that info here, and if there are people who have experiences they want to contribute, I’ll add it. Send me a link to the drive on Amazon or whatever and whether or not it “met the recommendations.”

Prices listed are current as of the day I added the drive to the list.

Meets Recommendations:

Size Drive Price Link Date Added
16GB SanDisk Cruzer Fit 16 GB USB Flash Drive SDCZ33-016G-B35 $12.19 Amazon 7/8/2013
16GB Lexar JumpDrive TwistTurn 16 GB USB 2.0 Flash Drive LJDTT16GASBNA $12.98 Amazon 11/1/2012
16GB Corsair Flash Voyager 16 GB USB 2.0 Flash Drive CMFUSB2.0-16GB $15.00 Amazon 5/10/2012
16GB PNY Mini Attaché 16GB USB 2.0 Flash Drive P-FD16G/MINI-EF $17.00 Amazon 2/21/2012
16GB Lexar JumpDrive TwistTurn 16 GB USB 2.0 Flash Drive LJDTT16GASBNA $22.99 Amazon 8/8/2011
16GB Kingston DataTraveler C10 $30.99 Amazon / NewEgg 7/29/2010
16GB Lexar JumpDrive Firefly LJDFF16GASBNA $39.22 Amazon 7/29/2010
16GB Patriot Xporter XT Boost PEF16GUSB $42.99 Amazon / NewEgg 7/29/2010
16GB SanDisk Xbox 360 Drive $57.85 Amazon 7/29/2010
16GB Sony USM-16W/B 16GB MicroVault Compact Metal USB Flash Drive     8/18/2014
8GB SanDisk Xbox 360 Drive $31.45 Amazon 7/29/2010
8GB Kingston Digital DataTraveler 100 Generation2 DT100G2/8GBZ $7.99 Amazon 2/6/2012
4GB Kingston DataTraveler 101 DT101Y/4GB $9.99 Amazon / NewEgg 7/29/2010
2GB Sandisk Cruzer Micro SDCZ4-2048-A11 $25.00 Amazon 5/20/2011

Fails Recommendations:

Size Drive Price Link Date Added
16GB 7dayshop Memory - USB 2.0 Flash / Key Drive £12.99 7dayshop 5/20/2011
16GB SanDisk Cruzer SDCZ36-016G-A11 $37.99 Amazon 7/29/2010
8GB LaCie iamaKey 8 GB USB 2.0 Flash Drive 130870 $26.99 Amazon 12/2/2011
8GB Sandisk Cruzer Micro SDCZ6-8192-A11 $9.99 Amazon 5/20/2011
8GB Dane-Elec zMate Pen USB Drive 8GB Ref DA-ZMP-8192T-R £16.98 Amazon 5/20/2011
4GB Sandisk Cruzer Micro SDCZ6-4096-A11 $6.99 Amazon 5/20/2011
4GB PNY Attache P-FD4GBATT03-EFM1 $7.99 Amazon 5/24/2011
4GB SanDisk Cruzer SDCZ36-004G-A11 $9.99 Amazon / NewEgg 7/29/2010
4GB Geek Squad GS-4GB10 $27.99 Best Buy 8/19/2010
4GB PNY P-FDU4GBSV-EF/GRY Micro Swivel 1 $31.20 Amazon 9/21/2011
? (not reported) Emtec S300 Thin Series $14.49 and up Amazon 1/19/2011

1 The drive failed the original configuration but on subsequent attempts succeeded.

Again, if you find one that works or one that doesn’t, or if you’re using one of the above and can confirm functionality, leave a comment or use the contact link on the site to send me a note and a link to the drive on Amazon or whatever. In the event where someone finds a drive that works and someone else finds that same drive doesn’t work, I’ll probably put it on the “fails” list. Better a drive that works for everyone than one that works for “some people.”

Note: Some folks have found that the first time you plug the drive in and test it, Xbox 360 says the drive passes, but on subsequent tests it fails. I’ll move those drives from the “pass” to “fail” tables as they’re discovered.

autofac, net comments edit

It’s taken some time and a couple of long Google Groups threads, but with a kick in the right direction from Nick Blumhardt, I’ve got multitenant dependency injection working with Autofac and available as a contributed library.

The basic usage pattern is:

  • Determine a strategy by which tenants are identified. That is, which tenant is making a given request? This might come from an environment variable, a request parameter, a role on the user’s principal, or wherever.
  • Build up your application container with the default dependency set. For tenants that don’t override anything, they’ll use these defaults.
  • Create a multitenant-aware container based on the application defaults. This is where the magic of AutofacContrib.Multitenant comes into play.
  • Configure tenant-specific overrides with the multitenant-aware container. Tenants can override things, add new dependencies, or whatever they need to do.
  • Resolve everything out of the multitenant-aware container. All of the resolutions will automatically use your tenant identification strategy so you’ll always get a tenant-specific result.

In practice, it looks like something this:

// First, create your application-level defaults using a standard // ContainerBuilder, just as you are used to. var builder = new ContainerBuilder(); builder.RegisterType<Consumer>().As<IDependencyConsumer>().InstancePerDependency(); builder.RegisterType<BaseDependency>().As<IDependency>().SingleInstance(); var appContainer = builder.Build();  // Once you've built the application-level default container, you // need to create a tenant identification strategy. var tenantIdentifier = new MyTenantIdentificationStrategy();  // Now create the multitenant container using the application // container and the tenant identification strategy. var mtc = new MultitenantContainer(tenantIdentifier, appContainer);  // Configure the overrides for each tenant by passing in the tenant ID // and a lambda that takes a ContainerBuilder. mtc.ConfigureTenant('1', b => b.RegisterType<Tenant1Dependency>().As<IDependency>().InstancePerDependency()); mtc.ConfigureTenant('2', b => b.RegisterType<Tenant2Dependency>().As<IDependency>().SingleInstance());  // Now you can use the multitenant container to resolve instances. // Resolutions will be tenant-specific. var dependency = mtc.Resolve<IDependency>();

The usage is very similar to the existing Autofac usage you know and love.

The multitenancy support works for standard non-web/service apps (e.g., console apps or Windows services), ASP.NET web forms and MVC apps, and WCF service apps.

There’s a ton of documentation about how to use the multitenancy that includes sample code snippets.There are also somesample console, WCF, and ASP.NET MVC applications in the source treeso you can see it in action.

It’s not currently out there as a binary, so you’ll have to do a source checkout and compile it manually, but I’m sure it’ll show up soon. It was released as part of AutofacContrib 2.5.1 and is available for download.

We’re looking for early adopters to give us feedback on it. If you try it out, please let us know what you think in the Autofac Google Group. What did you like? What were the pain points? Did you find a scenario that didn’t work? How was the documentation? The more feedback we get, the better we can make it! Thanks!

It’s my birthday today and I’m 34 years old. A hearty thanks to the folks who have sent me good birthday wishes via the various communication mechanisms at their disposal including, but not limited to: Twitter, Facebook, email, snail mail, text message, BlackBerry messenger, Google Chat, and telephone. I do appreciate it.

The day ain’t over yet, but it’s been a good birthday so far.

Last night my parents came by for a little while to visit. It was good to see them and they left a funny “Dadcat” shirt (because my girl cat lays on my stomach just like the cat on the shirt), some cool Hawaiian boxer shorts made from bamboo, and a pin from the Maui Hard Rock Cafe (I have a small collection).

One of our cat boys, Stan, started the day out by wishing me a happy birthday very loudly from outside the bedroom door. He provided an enthusiastic serenade about the day from 5:00a all the way until 6:00a when Jenn got up to get ready for work. He is outside the office door right now wishing me a happy birthday still. Still loud. Still enthusiastic. Lovely.

Jenn gave me her gifts before she left:

I’ve got the day off from work, which means I can do whatever I want, including not putting any gel in my hair. My head looks like a dandelion puff, but I’m cool with that.

After I’m done here, I’m going to go to McDonald’s and treat myself to a bacon, egg, and cheese McGriddle, my favorite. Then I’ll probably do some more mundane things like shopping at Costco, but I’ll be savoring the memory of that McGriddle, man.

I don’t know if we have some friends coming over tonight or not. If so, cool. Either way, I may end up rolling some Borderlands with my dad. We’re trying to crack level 40. Since the DLC for it is on sale next week, we’ll probably grab it then.

Tomorrow… well, there’s a family gathering, but it’s not for my birthday - it’s for my cousin who recently passed. Sort of a downer, so we’ll just move on. (Also, I anticipate a lot of “What have you been up to?” questions which I can’t answer because how do you explain “implementing multitenant dependency injection in Autofac” to a non-technical person?)

Sunday I do have some friends coming over and I anticipate there being some Rocking and Banding the likes of which have never been seen before. Or maybe we’ll just get some dinner. Either way, that’ll be fun.

So, again, thanks to those who’ve sent in birthday wishes. It’s time for me to go get my McGriddle!

For the second year in a row, Western Display Fireworks decided to trust Jenn and I to run our own show. This year we were in Happy Valley, OR.

The show was a little smaller than last year with around 300 shells and seven pre-fused boxes. It lasted about 18 minutes.

We had a good crew and both setup and cleanup went surprisingly quickly. Some of the crew are folks who had worked with me in previous years, some were new.

Here are the edited highlights:

One of my crewmembers, Alex, also took some great photos.

We had a lot of visits from city and state officials this year, way more than any other show I’ve ever been on. Fire crews checking things out, asking where to be stationed; police figuring out what the best way to secure things might be; the fire marshal running around with his checklist making sure we’re doing things to spec… it was pretty intimidating, to be honest. Shows how much they’re cracking down on this stuff. I guess they had something like 12 police units out patrolling for illegal fireworks that night.

From a retrospective standpoint there are a couple of things to mention:

  • I still hate driving the truck. Just thinking about driving a moving truck makes me tense up. Lighting explosives next to my head? No problem. Driving the moving truck? Problem. Jenn is a freaking truck-driving maniac so she took care of it most of the time, but I still had ample opportunity to tense up behind the wheel.
  • It is really, really hard to get a crew together. Really hard. Beyond hard. We had a really great crew this year, just as we did last year, but even giving people over a month’s notice it’s difficult to get folks to commit to being there… and you really need people you can count on to be there, or the whole thing will be a tough stunt to pull off. People cancel at the last minute, people don’t show up… and folks don’t realize that this isn’t like “Hey, I’m having a party, show up if you can” - it’s more “Hey, I truly need some help, so if you say you’re going to make it, I need to actually be able to count on you to be there 100% guaranteed.” Plus, it’s difficult work, lots of lifting and digging and such, and it’s not for everyone, so you sort of “burn through your contacts list” pretty quickly looking for folks. I was working on getting crew together until a week ago.
  • We probably could have gone just a tad faster. Looking back at the video, things felt a little slow overall. I don’t remember it being that way when we were there, and watching fireworks on video is sort of dumb and anticlimactic anyway, but there were a few dead spots where I feel like we should have had something in the sky. Granted, we couldn’t have gone too fast or the show would have finished in ten minutes or less… but I think this was maybe more of a 12 - 15 minute show than the 18 minutes we got out of it. Live and learn. You don’t really get to “practice” this stuff.

All in all, it was a great show.

As for next year, I’m not sure what the plan is. It was so hard to get a crew together this year, and there was so much work around coordinating the whole thing - getting the truck, scheduling people to be where they need to be, making sure people have their questions answered, following up to make sure everyone brought everything they were supposed to, etc.

  • that it’s really started to be much less fun than it used to be.

In some cases getting a show together is like getting a small wedding together. Is the caterer there? Did the cake arrive? How many people are showing up? Did you plan for enough food? Too much food? Where the hell is the groom? In the end, I’m questioning whether it might be time to take a break.

If I do, I’d either have to let my pyro license expire or do a make-up show (since you have to have a minimum number of shows to renew your license)… but the only reason I have my license is to do shows, so it’s sort of Catch-22-ish. The only reason to be licensed besides that is basically just to have the ability to say I’m licensed.

Anyway, something to think about. In the meantime, we had a great show this year, so thanks to the crew for all their hard work in making it happen!