I’ve been looking into the issue where EFTools.msi does not drop the 3 DLLs on disk (EntityFramework.dll + 2 providers). Using the logs from the two cases, I’ve found the lines where this is happening. (Highlighted at the bottom of this email).
The short summary of what happens is as follows:
- The “CostFinalize” step determines that the EF.dll on disk is “higher versioned”*, so it sets the action to “Null”, meaning that it won’t attempt to change those files.
- The “RemoveExistingProducts” step detects a “major upgrade” because WiX generated a new random product ID (the recommended behavior), and fires-off the uninstall for the previous MSI, which removes the original files from disk.
- The Install step follows the steps it determined in #1 above and does not install those 3 files, not knowing that in #2 the files were actually removed.
*The ”higher versioned” error also includes the case where the assemblies are the same version. Which is why we see this failure on the CI server every other build/install, because Glenn has it using the nightly signed EF6 builds, whereas in our local builds
we rebuild EF each time. Therefore, it seems that we would encounter this issue only if we ship the same version of the signed EF.dll’s with a new version of the designer. (As in, we perhaps ship a fix for the designer without changing the EF.dll’s and use
ones that had previously shipped.)
Using some quick internet research, it seems the commonly proposed solutions to the problem (of “major upgrades of an MSI which don’t actually upgrade one or more components”) are:
- Move the “RemoveExistingProducts” step so that it happens earlier.
a. This is not the recommended order, based on the SDK & docs for MSIs, and could have some unintended consequences.
- Uninstall the old product as a separate step, prior to “CostFinalize.”
a. This is similar to #1 but could be used as a one-time workaround in cases where you actually know that you are going to hit this issue. Normally you wouldn’t want to do this as the “RemoveExistingProducts” step takes care of it for you.
- Use a <RemoveFile On="install" /> step inside of the component definition, to explicitly remove the files before installing them. (This step should then get scheduled prior to “InstallFiles”, which means prior to “CostFinalize”)
a. This is also similar to #2, but works with rollback, so if the new version is ultimately not installed (or cancelled by the user) the old files will be put back into place.
For the CI machine, we can fix this issue by explicitly uninstalling the old MSI before installing the new one. (Thus when the new one checks for the files, they are not on disk.) However, this reduces awareness of the underlying issue and makes it so that
we might inadvertently ship something that breaks our customers. It depends on how high risk we think this is.