Is it possible to have an object injected through the constructor and register an event on it? I am currently trying to implement something like this and my PropertyChanged even is always null. I think it has to do with the fact that I am not instantiating the object in my class where I register the event, but I am not quite sure as I am still new to event driven code.
Anyway here is the relevant code:
Injected via dependency:
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");
}
}
Class consuming the injected dependency:
public class ClientService : HostedService
{
public ClientService(IWritableOptions<ClientSettings> clients)
{
clients.PropertyChanged += PropertyChanged;
}
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
}
EDIT: To further clarify, I all use this in a .Net Core WebAPI project. I register ClientService like this:
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureWritable<ClientSettings>(Configuration.GetSection("ClientSettings"));
services.AddSingleton<IHostedService, ClientService>();
services.AddMvc();
}
I then have a controller with which I can update these settings:
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());
}
Sources: IHostedService, IWritableOptions
Am I missing something here or is this not possible?
Working GitHub Repo: EventDI