halloween, maker, costumes comments edit

Due to some challenges with home remodeling issues we didn’t end up handing out candy this year.

We discovered a slow leak in one of the walls in our kitchen that caused some of our hardwood floor to warp, maybe a little more than a square meter. Since this was a very slow leak over time, insurance couldn’t say “here’s the event that caused it” and, thus, chalked it up to “normal wear and tear” which isn’t covered.

You can’t fix just a small section of a hardwood floor and we’ve got like 800 square feet of contiguous hardwood, so… all 800 square feet needed to be fully sanded and refinished. All out of pocket. We packed the entire first floor of the house into the garage and took a much-needed vacation to Universal Studios California and Disneyland for a week while the floor was getting refinished.

I had planned on putting the house back together, decorating, and getting right into Halloween when we came back. Unfortunately, when we got back we saw the floor was not done too well. Lots of flaws and issues in the work. It’s getting fixed, but it means we didn’t get to empty out the garage, which means I couldn’t get to the Halloween decorations. Between work and stress and everything else… candy just wasn’t in the cards. Sorry kids. Next year.

But we did make costumes - and we wore them in 90 degree heat in California for the Disney “Oogie Boogie Bash” party. So hot, but still very fun.

I used this Julie-Chantal pattern for a Jedi costume and it is really good. I’m decent at working with and customizing patterns, I’m not so great with drafting things from scratch.

I used a cotton gauze for the tunic, tabard, and sash. The robe is a heavy-weave upholstery fabric that has a really nice feel to it.

Texture of the robe fabric up close

I added some magnet closures to it so it would stick together a bit nicer as well as some snaps to stick things in place. I definitely found while wearing it that it was required. All the belts and everything have a tendency to move a lot as you walk, sit, and stand. I think it turned out nicely, though.

The Jedi costume on a dress form

The whole family went in Star Wars garb. I don’t have a picture of Phoenix, but here’s me and Jenn at a Halloween party. Phoenix and Jenn were both Rey, but from different movies. You can’t really tell, but Jenn’s vest is also upholstery fabric with an amazing, rich texture. She did a great job on her costume, too.

Trav and Jenn in Star Wars costumes

mac comments edit

Rosetta is used to enable a Mac with Apple silicon to use apps built for Intel. Most of the time, you’ll get prompted to install it the first time you need it and after that the automatic software update process will take over. However, in some environments the automatic mechanisms don’t work - maybe it’s incorrectly blocked or the update isn’t detecting things right. Here’s how to update Rosetta manually.

First, get your OS build number: 🍎 -> About This Mac -> More Info.

The 'About This Mac' window - click the 'More Info' button

Click on the Version XX.X field and it should expand to show you the build number. It will be something like 22A380.

The 'More Info' window showing the build number

Go to the software catalog for Rosetta and search for your build number. You should see your build-specific package. The build number is in ExtendedMetaInfo:

<dict>
  <key>ServerMetadataURL</key>
  <string>https://swcdn.apple.com/content/downloads/38/00/012-92132-A_1NEH9AKCK9/k8s821iao7kplkdvqsovfzi49oi54ljrar/RosettaUpdateAuto.smd</string>
  <key>Packages</key>
  <array>
    <dict>
      <key>Digest</key>
      <string>dac241ee3db55ea602540dac036fd1ddc096bc06</string>
      <key>Size</key>
      <integer>331046</integer>
      <key>MetadataURL</key>
      <string>https://swdist.apple.com/content/downloads/38/00/012-92132-A_1NEH9AKCK9/k8s821iao7kplkdvqsovfzi49oi54ljrar/RosettaUpdateAuto.pkm</string>
      <key>URL</key>
      <string>https://swcdn.apple.com/content/downloads/38/00/012-92132-A_1NEH9AKCK9/k8s821iao7kplkdvqsovfzi49oi54ljrar/RosettaUpdateAuto.pkg</string>
    </dict>
  </array>
  <key>ExtendedMetaInfo</key>
  <dict>
    <key>ProductType</key>
    <string>otherArchitectureHandlerOS</string>
    <key>BuildVersion</key>
    <string>22A380</string>
  </dict>
</dict>

Look for the URL value (the .pkg file). Download and install that. Rosetta will be updated.

net, csharp, vs comments edit

UPDATE OCT 25 2022: I filed an issue about some of the challenges here and the weird <Compile Remove> solution I had to do to get around the CS2002 warning. I got a good comment that explained some of the things I didn’t catch from the original issue about strongly-typed resource generation (which is a very long issue). I’ve updated the code/article to include the fixes and have a complete example.


In the not-too-distant past I switched from using Visual Studio for my full-time .NET IDE to using VS Code. No, it doesn’t give me quite as much fancy stuff, but it feels a lot faster and it’s nice to not have to switch to different editors for different languages.

Something I noticed, though, was that if I updated my *.resx files in VS Code, the associated *.Designer.cs was not getting auto-generated. There is a GitHub issue for this and it includes some different solutions to the issue involving some .csproj hackery, but it’s sort of hard to parse through and find the thing that works.

Here’s how you can get this to work for both Visual Studio and VS Code.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!--
        Target framework doesn't matter, but this solution is tested with
        .NET 6 SDK and above.
    -->
    <TargetFrameworks>net6.0</TargetFrameworks>

    <!--
        This is required because OmniSharp (VSCode) calls the build in a way
        that will skip resource generation. Without this line, OmniSharp won't
        find the generated .cs files and analysis will fail.
    -->
    <CoreCompileDependsOn>PrepareResources;$(CompileDependsOn)</CoreCompileDependsOn>
  </PropertyGroup>

  <ItemGroup>
    <!--
        Here's the magic. You need to specify everything for the generated
        designer file - the filename, the language, the namespace, and the
        class name.
    -->
    <EmbeddedResource Update="MyResources.resx">
      <!-- Tell Visual Studio that MSBuild will do the generation. -->
      <Generator>MSBuild:Compile</Generator>
      <LastGenOutput>MyResources.Designer.cs</LastGenOutput>
      <!-- Put generated files in the 'obj' folder. -->
      <StronglyTypedFileName>$(IntermediateOutputPath)\MyResources.Designer.cs</StronglyTypedFileName>
      <StronglyTypedLanguage>CSharp</StronglyTypedLanguage>
      <StronglyTypedNamespace>Your.Project.Namespace</StronglyTypedNamespace>
      <StronglyTypedClassName>MyResources</StronglyTypedClassName>
    </EmbeddedResource>

    <!--
        If you have resources in a child folder it still works, but you need to
        make sure you update the StronglyTypedFileName AND the
        StronglyTypedNamespace.
    -->
    <EmbeddedResource Update="Some\Sub\Folder\OtherResources.resx">
      <Generator>MSBuild:Compile</Generator>
      <LastGenOutput>OtherResources.Designer.cs</LastGenOutput>
      <!-- Make sure this won't clash with other generated files! -->
      <StronglyTypedFileName>$(IntermediateOutputPath)\OtherResources.Designer.cs</StronglyTypedFileName>
      <StronglyTypedLanguage>CSharp</StronglyTypedLanguage>
      <StronglyTypedNamespace>Your.Project.Namespace.Some.Sub.Folder</StronglyTypedNamespace>
      <StronglyTypedClassName>OtherResources</StronglyTypedClassName>
    </EmbeddedResource>
  </ItemGroup>
</Project>

Additional tips:

Once you have this in place, you can .gitignore any *.Designer.cs files and remove them from source. They’ll be regenerated by the build, but if you leave them checked in then the version of the generator that Visual Studio uses will fight with the version of the generator that the CLI build uses and you’ll get constant changes. The substance of the generated code is the same, but file headers may be different.

You can use VS Code file nesting to nest localized *.resx files under the main *.resx files with this config. Note you won’t see the *.Designer.cs files in there because they’re going into the obj folder.

{
  "explorer.fileNesting.enabled": true,
  "explorer.fileNesting.patterns": {
    "*.resx": "$(capture).*.resx, $(capture).designer.cs, $(capture).designer.vb"
  }
}

azure, tfs, build comments edit

Using the Sonatype Nexus IQ for Azure DevOps task in your build, you may see some warnings that look like this:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/agent/_work/_tasks/NexusIqPipelineTask_4f40d1a2-83b0-4ddc-9a77-e7f279eb1802/1.4.0/resources/nexus-iq-cli-1.143.0-01.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

The task, internally, just runs java to execute the Sonatype scanner JAR/CLI. The warnings here are because that JAR assumes JDK 8 and the default JDK on an Azure DevOps agent is later than that.

The answer is to set JDK 8 before running the scan.

# Install JDK 8
- task: JavaToolInstaller@0
  inputs:
    versionSpec: '8'
    jdkArchitectureOption: x64
    jdkSourceOption: PreInstalled

# Then run the scan
- task: NexusIqPipelineTask@1
  inputs:
    nexusIqService: my-service-connection
    applicationId: my-application-id
    stage: "Release"
    scanTargets: my-scan-targets

csharp comments edit

I was doing some AutoMapper-ing the other day, converting my data object…

public class Source
{
  public Source();
  public string Description { get; set; }
  public DateTimeOffset? ExpireDateTime { get; set; }
  public string Value { get; set; }
}

…into an object needed for a system we’re integrating with.

public class Destination
{
  public Destination();
  public Destination(string value, DateTime? expiration = null);
  public Destination(string value, string description, DateTime? expiration = null);
  public string Description { get; set; }
  public DateTime? Expiration { get; set; }
  public string Value { get; set; }
}

It appeared to me that the most difficult thing here was going to be mapping ExpireDateTime to Expiration. Unfortunately, this was more like a three-hour tour.

I started out creating the mapping like this (in a mapping Profile):

// This is not the answer.
this.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Expiration, opt.MapFrom(src => src.ExpireDateTime));

This didn’t work because there’s no mapping from DateTimeOffset? to DateTime?. I next made a mistake that I think I make every time I run into this and have to relearn it, which is that I created that mapping, too.

// Still not right.
this.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Expiration, opt.MapFrom(src => src.ExpireDateTime));
this.CreateMap<DateTimeOffset?, DateTime?>()
    .ConvertUsing(input => input.HasValue ? input.Value.DateTime : null);

It took a few tests to realize that AutoMapper handles nullable for you, so I was able to simplify a bit.

// Getting closer - don't map nullable, map the base type.
this.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Expiration, opt.MapFrom(src => src.ExpireDateTime));
this.CreateMap<DateTimeOffset, DateTime>()
    .ConvertUsing(input => input.DateTime);

However, it seemed that no matter what I did, the Destination.Expiration was always null. For the life of me, I couldn’t figure it out.

Then I had one of those “eureka” moments when I was thinking about how Autofac handles constructors: It chooses the constructor with the most parameters that it can fulfill from the set of registered services.

I looked again at that Destination object and realized there were three constructors, two of which default the Expiration value to null. AutoMapper also handles constructors in a way similar to Autofac. From the docs about ConstructUsing:

AutoMapper will automatically match up destination constructor parameters to source members based on matching names, so only use this method if AutoMapper can’t match up the destination constructor properly, or if you need extra customization during construction.

That’s it! The answer is to pick the zero-parameter constructor so the mapping isn’t skipped.

// This is the answer!
this.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Expiration, opt.MapFrom(src => src.ExpireDateTime))
    .ConstructUsing((input, context) => new Destination());
this.CreateMap<DateTimeOffset, DateTime>()
    .ConvertUsing(input => input.DateTime);

Hopefully that will save you some time if you run into it. Also, hopefully it will save me some time next time I’m stumped because I can search and find my own blog… which happens more often than you might think.