通过构造函数注入的对象上的注册事件(PropertyChanged始终为null)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过构造函数注入的对象上的注册事件(PropertyChanged始终为null)相关的知识,希望对你有一定的参考价值。

是否可以通过构造函数注入一个对象并在其上注册一个事件?我目前正在尝试实现这样的功能,并且我的PropertyChanged始终为null。我认为这与以下事实有关:我没有在我注册事件的类中实例化该对象,但我不太确定,因为我还是事件驱动代码的新手。

无论如何这里是相关代码:

通过依赖项注入:

public static class ServiceCollectionExtensions
{
    public static void ConfigureWritable<T>(
        this IServiceCollection services,
        IConfigurationSection section,
        string file = "appsettings.json") where T : class, new()
    {
        services.Configure<T>(section);
        services.AddTransient<IWritableOptions<T>>(provider =>
        {
            var environment = provider.GetService<IHostingEnvironment>();
            var options = provider.GetService<IOptionsMonitor<T>>();
            return new WritableOptions<T>(environment, options, section.Key, file);
        });
    }
}

public interface IWritableOptions<out T> : IOptionsSnapshot<T> where T : class, new()
{
    void Update(Action<T> applyChanges);
    event PropertyChangedEventHandler PropertyChanged;
}

public class WritableOptions<T> : INotifyPropertyChanged, IWritableOptions<T> where T : class, new()
{
    private readonly IHostingEnvironment _environment;
    private readonly IOptionsMonitor<T> _options;
    private readonly string _section;
    private readonly string _file;

    public WritableOptions(
        IHostingEnvironment environment,
        IOptionsMonitor<T> options,
        string section,
        string file)
    {
        _environment = environment;
        _options = options;
        _section = section;
        _file = file;
    }

    public T Value
    {
        get
        {
            var fileProvider = _environment.ContentRootFileProvider;
            var fileInfo = fileProvider.GetFileInfo(_file);
            var physicalPath = fileInfo.PhysicalPath;

            var jObject = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(physicalPath));
            var sectionObject = jObject.TryGetValue(_section, out JToken section) ?
                JsonConvert.DeserializeObject<T>(section.ToString()) : (Value ?? new T());

            return sectionObject;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, e);
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    public T Get(string name) => _options.Get(name);

    public void Update(Action<T> applyChanges)
    {
        var fileProvider = _environment.ContentRootFileProvider;
        var fileInfo = fileProvider.GetFileInfo(_file);
        var physicalPath = fileInfo.PhysicalPath;

        var jObject = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(physicalPath));
        var sectionObject = jObject.TryGetValue(_section, out JToken section) ?
            JsonConvert.DeserializeObject<T>(section.ToString()) : (Value ?? new T());

        applyChanges(sectionObject);

        jObject[_section] = JObject.Parse(JsonConvert.SerializeObject(sectionObject));
        File.WriteAllText(physicalPath, JsonConvert.SerializeObject(jObject, Formatting.Indented));

        OnPropertyChanged("Value");
    }
}

使用注入依赖项的类:

public class ClientService : HostedService
{
    public ClientService(IWritableOptions<ClientSettings> clients)
    {
        clients.PropertyChanged += PropertyChanged;
    }

    private void PropertyChanged(object sender, PropertyChangedEventArgs e)
    {

    }
}

编辑:为了进一步阐明,我都在.Net Core WebAPI项目中使用了它。我这样注册ClientService:

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureWritable<ClientSettings>(Configuration.GetSection("ClientSettings"));
    services.AddSingleton<IHostedService, ClientService>();

    services.AddMvc();
}

然后我有了一个控制器,可以用来更新这些设置:

private IWritableOptions<ClientSettings> _clients;

public ClientController(IWritableOptions<ClientSettings> clients)
{
    _clients = clients;
}

[HttpPost]
[Route(CoreConfigClientRoutes.UpdateRoute)]
public void UpdateClients([FromBody] IEnumerable<ModuleSetting> updatedClients)
{
    var clients = _clients.Value.Clients.ToList();
    foreach (var updatedClient in updatedClients)
    {
        var index = clients.IndexOf(clients.Where(c => c.Id == updatedClient.Id).First());
        clients[index] = updatedClient;
    }
    _clients.Update(c => c.Clients = clients.ToArray());
}

来源:IHostedServiceIWritableOptions

我在这里错过了什么吗?还是不可能?

正在运行的GitHub存储库:EventDI

答案

我正在WPF应用程序中超越。

如果您愿意,您可能不应该在下面使用代码。但是,为了快速进行“修补和继续”,我做了以下操作。

我放弃了上述解决方案,因为它因依赖项注入位而失败。只是手动描述了从何处获取appsettings.json并删除了无法绑定组件的问题。

public class WritableOptions<T> : IWritableOptions<T> where T : class, new()
{
    private readonly IOptionsMonitor<T> _options;
    private readonly string _section;

    public WritableOptions(
        IOptionsMonitor<T> options,
        string section,
        string file)
    {
        _options = options;
        _section = section;
    }

    public T Value => _options.CurrentValue;
    public T Get(string name) => _options.Get(name);

    public void Update(Action<T> applyChanges)
    {
        var physicalPath = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName+ "\appsettings.json";

        var jObject = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(physicalPath));
        var sectionObject = jObject.TryGetValue(_section, out JToken section) ?
            JsonConvert.DeserializeObject<T>(section.ToString()) : (Value ?? new T());

        applyChanges(sectionObject);

        jObject[_section] = JObject.Parse(JsonConvert.SerializeObject(sectionObject));
        File.SetAttributes(physicalPath, FileAttributes.Normal);
        File.WriteAllText(physicalPath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
    }
}

以上是关于通过构造函数注入的对象上的注册事件(PropertyChanged始终为null)的主要内容,如果未能解决你的问题,请参考以下文章

通过生成构造函数将超类注入原始对象

这是不是有太多依赖项无法通过构造函数注入到对象中?

IOC 控制反转Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 获取要注入事件的 View 对象 | 通过反射获取 View 组件的事件设置方法 )

Autofac--注册组件--3属性和方法注入

[Asp.Net Core]Autofac单抽象多实现构造函数注入

[Asp.Net Core]Autofac单抽象多实现构造函数注入