目标
我正在尝试迁移到 .net 标准,因此需要将 RealProxy 迁移到 Dispatch Proxy。
当前设置
我正在使用 Autofac 进行依赖注入。
配置绑定到实现 ISettings 的“设置”类。
我使用构造函数注入将设置注入我的类并检索设置的值,如下所示:
private readonly IDatabaseSettings _databaseSettings;
public MyService(IDatabaseSettings databaseSettings)
{
_databaseSettings = databaseSettings;
}
public string GetConnectionString() {
return _databaseSettings.ConnectionString;
}
通过从 MySettingsProxy 实例注册透明代理,向 Autofac 注册设置:
private static void RegisterConfiguration(ContainerBuilder builder)
{
foreach (var settingsContainer in MyConfigurationManager.GetSettings())
{
builder.Register(r => new MySettingsProxy(settingsContainer.Key).Build())
.As(settingsContainer.Key);
}
}
MySettingsProxy 是 RealProxy 的一个实现。
public class MySettingsProxy : RealProxy
{
private readonly Type settings;
public MySettingsProxy(Type settingsType)
: base(settingsType)
{
settings = settingsType;
}
public object Build()
{
return GetTransparentProxy();
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = (IMethodCallMessage)msg;
string prop = methodCall.MethodName;
return new ReturnMessage(settings.GetProperty(prop.Split('_')[1])
.GetValue(MyCachedSettings.CachedSettings[settings]), null, 0, null, null);
}
}
public static class MyCachedSettings
{
public static Dictionary<Type, ISettings> CachedSettings { get; set; } = new Dictionary<Type, ISettings>();
}
IConfiguration _configuration 中的部分绑定到 ISettings 类。
public static class MyConfigurationManager
{
private static IConfiguration _configuration = null;
private static IConfigurationRefresher _configurationRefresher = null;
private static Dictionary<Type, ISettings> _settings = new Dictionary<Type, ISettings>();
private static string _environment;
private static bool _isStagingSlot;
private static string _overwrittenLocalhostConfigKey = null;
public static async Task<bool> RefreshConfiguration()
{
var success = await _configurationRefresher.TryRefreshAsync();
BindAndCacheConfiguration();
return success;
}
public static void BuildConfiguration()
{
var configStore = ConfigureEnvironment();
ConnectToAppConfigService(configStore);
BindAndCacheConfiguration();
}
internal static Dictionary<Type, ISettings> GetSettings()
{
return _settings;
}
internal static ISettings GetSettings(Type settingsInterface)
{
return _settings[settingsInterface];
}
internal static void BindAndCacheConfiguration()
{
BindConfiguration();
CacheConfiguration();
}
private static void BindConfiguration()
{
_settings.Clear();
var settings = Assembly.GetAssembly(typeof(ISettings)).GetTypes()
.Where(x => typeof(ISettings).IsAssignableFrom(x)
&& !x.IsAbstract
&& !x.IsInterface);
List<string> missingSettings = new List<string>();
foreach (var settingsContainer in settings)
{
try
{
var settingsInterface = settingsContainer.GetInterface("I" + settingsContainer.Name);
var settingsPrefix = SettingsPrefixes.Prefixes[settingsInterface];
var configurationSection = _configuration.GetSection(settingsPrefix);
var boundSettingsContainer = (ISettings)configurationSection.Get(settingsContainer);
if (boundSettingsContainer is null)
{
throw new MissingConfigurationException($"{settingsInterface} was null, likely because no matching configs were found for that section. Environment: {_environment}");
}
var individualSettings = boundSettingsContainer.GetType().GetProperties()
.Where(x => x.CanRead && x.CanWrite)
.Select(x => new { Name = x.Name, Value = x.GetValue(boundSettingsContainer) });
foreach (var setting in individualSettings)
{
if (setting.Value == null || string.IsNullOrEmpty(setting.Value.ToString()) || !configurationSection.AsEnumerable().Any(x => x.Key == $"{settingsPrefix}:{setting.Name}"))
{
missingSettings.Add($"{settingsPrefix}:{setting.Name}");
}
}
_settings.Add(settingsInterface, boundSettingsContainer);
}
catch (KeyNotFoundException)
{
throw new MissingConfigurationException($"The prefix for {settingsContainer.Name} has not been configured in {nameof(SettingsPrefixes)}");
}
}
if (missingSettings.Count != 0)
{
throw new MissingConfigurationException($"The following settings are missing from the Azure App Configuration Service for {_environment}: {string.Join(",", missingSettings)}");
}
}
private static void CacheConfiguration()
{
MyCachedSettings.CachedSettings = _settings;
}
}
现在,当我在应用程序运行时更改 _configuration 的内容并将 _configuration 的部分重新绑定到 ISettings 类时,它反映了从构造函数注入访问设置时的新值。
迁移到 Dispatch Proxy 后的更改
我的 SettingsProxy 现在看起来像这样:
public class MySettingsProxy
{
private readonly object _target;
private readonly MethodInfo _methodInfo;
private readonly ISettings _settingsInterface;
private readonly Type _type;
public MySettingsProxy(Type type, ISettings settingsInterface)
{
_type = type;
_settingsInterface = settingsInterface;
var proxyType = typeof(MySettingsProxy<>).MakeGenericType(_type);
_target = Activator.CreateInstance(proxyType);
var methodName = nameof(MySettingsProxy<ISettings>.Decorate);
_methodInfo = proxyType.GetMethod(methodName);
}
public object Build()
{
return _methodInfo.Invoke(_target, new object[] { _settingsInterface });
}
}
public class MySettingsProxy<T> : DispatchProxy
where T : ISettings
{
public T Target { get; private set; }
public MySettingsProxy() : base()
{
}
public static object Decorate(T target)
{
var proxy = Create<T, MySettingsProxy<T>>()
as MySettingsProxy<T>;
proxy.Target = target;
return proxy;
}
protected override object Invoke(MethodInfo targetMethod, object[] args)
{
try
{
var result = targetMethod.Invoke(Target, args);
return result;
}
catch (TargetInvocationException exc)
{
throw exc.InnerException;
}
}
}
并且代理注册到 Autofac 容器,如下所示:
private static void RegisterConfiguration(ContainerBuilder builder)
{
var settings = RevokeConfigurationManager.GetSettings();
foreach (var setting in settings)
{
var settingsProxy = new MySettingsProxy(setting.Key, setting.Value);
var builtSettingsProxy = MySettingsProxy.Build();
builder.Register(r => builtMySettingsProxy)
.As(setting.Key);
}
}
其他一切仍然相同,但是在访问构造函数注入设置时,它们没有来自 _configuration 的新值
我的 Dispatch Proxy 设置有什么不同的想法吗?