So I have a table that I keep my settings of my mobile app in coming near the end of development I cant help but think their a neater way of doing this.
Instead of having the three individual calls how would I have one call that can account for the different data types being returned.
I am using ef core 6 and c# 7 in a web api project.
public class SettingsService
{
TheAppDBContext db;
public SettingsService(TheAppDBContext dBContext)
{
db = dBContext;
}
public List<Settings> GetALLSettings()
{
return db.Settings.Where(w => w.IsActive == true &&
w.IsDeleted == false).ToList();
}
public bool? GetBoolSettingValueByKey(string Key)
{
return db.Settings.Where(w => w.IsActive == true && w.IsDeleted == false
&& w.Key.ToLower() == Key.ToLower()).First().BoolValue;
}
public string? GetStringSettingValueByKey(string Key)
{
return db.Settings.Where(w => w.IsActive == true && w.IsDeleted == false &&
w.Key.ToLower() == Key.ToLower()).First().StringValue;
}
public int? GetIntSettingValueByKey(string Key)
{
return db.Settings.Where(w => w.IsActive == true && w.IsDeleted == false &&
w.Key.ToLower() == Key.ToLower()).First().IntValue;
}
}
My Table of settings are as follows there could be in future possibility of dates and other data types stored in the settings table.
CodePudding user response:
If you are willing to lose some type safety you can use make your method a generic one and analyze it's generic type parameter:
public T GetSettingValueByKey<T>(string key)
{
var setting = db.Settings
.Where(w => w.IsActive == true && w.IsDeleted == false && w.Key.ToLower() == Key.ToLower())
.First();
var type = typeof(T);
if (type == typeof(bool))
{
return (T)(object)setting.BoolValue;
}
if (type == typeof(string))
{
return (T)(object)setting.StringValue;
}
if (type == typeof(int))
{
return (T)(object)setting.IntValue;
}
...
throw new InvalidOperationException();
}
The issue here is that no one restricts user to call GetSettingValueByKey
with unsupported type and compiler can't enforce it (unless you want to dive into writing custom code analyzer).
To preserve compile time safety without custom analyzer you can try introducing type hierarchy to represent the possible setting types:
public interface ISettingValue{}
public interface ISettingValue<T> : ISettingValue
{
T Value { get; set; }
}
public struct IntSettingValue : ISettingValue<int?>
{
public int? Value { get; set; }
}
public struct BoolSettingValue : ISettingValue<bool?>
{
public bool? Value { get; set; }
}
...
public T GetSettingValueByKey<T>(string key) where T : ISettingValue
{
var setting = db.Settings
.Where(w => w.IsActive == true && w.IsDeleted == false && w.Key.ToLower() == Key.ToLower())
.First();
var type = typeof(T);
if (type == typeof(BoolSettingValue))
{
return (T)(object)new BoolSettingValue
{
Value = setting.BoolValue
};
}
if (type == typeof(int))
{
return (T)(object)new IntSettingValue
{
Value = setting.IntValue
};
}
...
throw new InvalidOperationException(); // write unit test with reflection to validate that this never happens
}
And usage:
SettingsService service = ...;
int? val = service.GetSettingValueByKey<IntSettingValue>("").Value;