'What is the proper way of passing part of configuration to base class?
Let's say we have a base service class:
public abstract class BaseService
{
private readonly _user;
private readonly _password;
public BaseService(IOptions<ServiceConfig> serviceConfig)
{
if (serviceConfig is null)
throw new ArgumentNullException(nameof(serviceConfig));
_user = serviceConfig.Value.User ?? throw new ArgumentNullException(nameof(serviceConfig.Value.User));
_password = serviceConfig.Value.Password ?? throw new ArgumentNullException((serviceConfig.Value.Password));
}
}
and the concrete service:
public class ConcreteService
{
private readonly _url;
public ConcreteService(IOptions<ServiceConfig> serviceConfig) : base(serviceConfig)
{
_url = serviceConfig.Value.Url ?? throw new ArgumentNullException(nameof(serviceConfig.Value.Url));
}
}
public class ServiceConfig
{
public string Url { get; set; }
public string User { get; set; }
public string Password { get; set; }
}
The problem in this implementation is passing the whole serviceConfig to base class even though It needs only the part of It. Also I'm not sure if relying on the base class to check if the serviceConfig is not a null is a good practice.
I can see a workaround to divide configuration into two parameters:
public ConcreteService(string url, IOptions<UserConfig> userConfig) : base(userConfig)
{
_url = url ?? throw new ArgumentNullException(nameof(url));
}
public class UserConfig
{
public string User { get; set; }
public string Password { get; set; }
}
But now related configuration is splitted and additionally url needs to be manually injected because DI container handles only injecting of IOptions<>.
I could also construct a config class like that:
public class ServiceConfig
{
public string Url { get; set; }
public UserConfig UserConfig { get; set; }
}
public ServiceConfig(IOptions<ServiceConfig> serviceConfig) : base(serviceConfig.Value?.UserConfig)
{
if (serviceConfig is null)
throw new ArgumentNullException(nameof(serviceConfig));
_url = serviceConfig.Value.Url ?? throw new ArgumentNullException(serviceConfig.Value.Url);
}
Don't know why but i feel that invoking base(serviceConfig.Value?.UserConfig) with nullable operator looks kind of weird. Does it violates any best pratices?
Is there a better way of implementing this?
Solution 1:[1]
Having access to more things than needed might not be ideal, but might also not be the most important thing to worry about. If it becomes a problem, like having to insert dummy values that you know are not needed, the problem becomes more important to address.
But now related configuration is splitted
Is address and username/password necessarily related? they might be, but if one is used without the other it is probably a hint that they might not be.
url needs to be manually injected because DI container handles only injecting of IOptions<>
You can use the type system to describe what the value actually means. So you could just declare a new class with a single value I.e. something like
public class ServerAddress{
public string Url {get; set;}
}
In a regular application I would consider just composing objects, i.e. something like
public class ServiceConfig
{
public string Url { get; set; }
public UserConfig User { get; set; }
}
This way you can just forward some part the configuration to the base class. But there might be cases where this approach does not play well with the Options system.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | JonasH |
