I had a similar problem.
I was developing a Xamarin mobile app with an ASP.Net backend. I had a settings class that contains the backend server URL:
namespace Company.Mobile
{
public static class Settings
{
#if DEBUG
const string WebApplicationBaseUrl = "https://local-pc:44335/";
#else
const string WebApplicationBaseUrl = "https://company.com/";
#endif
}
}
It has different values for debug and release configurations. But this didn't work when several developers started working on the project. Every dev machine had its IP address, and mobile phones need to connect using unique IP addresses.
I needed to set the constant value from a file or an environment variable on each dev machine. This is where Fody fits in. I used it to create an in solution weaver. Here are the details.
I place my Settings
class in the Xamarin app project. This project has to include the Fody Nuget package:
<ItemGroup>
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Fody" Version="6.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<WeaverFiles Include="$(SolutionDir)Company.Mobile.Models\bin\Debug\netstandard2.0\Company.Mobile.Models.dll" WeaverClassNames="SetDevServerUrlWeaver" />
</ItemGroup>
I make my setup work on Debug configuration only, because I don't want the substitution to happen on Release builds.
The weaver class is placed in a class library project (Company.Mobile.Models) that the mobile project depends on (you needn't and shouldn't have this dependency, but Fody docs says clearly that the project that contains the weaver must be built before the project that emits the weaved assembly). This library project includes the FodyHelpers Nuget package:
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="FodyHelpers" Version="6.2.0" />
</ItemGroup>
The weaver class is defined as follows:
#if DEBUG
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Fody;
namespace Company.Mobile.Models
{
public class SetDevServerUrlWeaver : BaseModuleWeaver
{
private const string SettingsClassName = "Settings",
DevServerUrlFieldName = "WebApplicationBaseUrl",
DevServerUrlSettingFileName = "devServerUrl.txt";
public override void Execute()
{
var target = this.ModuleDefinition.Types.SingleOrDefault(t => t.IsClass && t.Name == SettingsClassName);
var targetField = target.Fields.Single(f => f.Name == DevServerUrlFieldName);
try
{
targetField.Constant = File.ReadAllText(Path.Combine(this.ProjectDirectoryPath, DevServerUrlSettingFileName));
}
catch
{
this.WriteError($"Place a file named {DevServerUrlSettingFileName} and place in it the dev server URL");
throw;
}
}
public override IEnumerable<string> GetAssembliesForScanning()
{
yield return "Company.Mobile";
}
}
}
#endif
And here's the FodyWeavers.xml file placed in the Mobile app project:
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<SetDevServerUrlWeaver />
</Weavers>
The devServerUrl.txt simply contains my local IP:
https://192.168.1.111:44335/
. This file must not be added to source control. Add it to your source control ignore file so that each developer have his version.
You may easily read the substituted value from an environment variable (System.Environment.GetEnvironmentVariable
) or whatever place instead of a file.
I hoped there had been a better way to do this, like Roslyn, or this attribute that seems to do the job, but it doesn't.