So I am working on an application (top-secret!) that I need settings for, these settings are pretty basic, but I don't want to make a single-row table with a bunch of serialized settings, or a single-row table with a bunch of columns, etc.
So I made a Setting
class that's pretty handy:
public class Setting
{
[Key]
[MaxLength(64)]
public string Name { get; set; }
public string Value { get; set; }
public string Type { get; set; }
public Setting() { }
public Setting(string name, string value, string type)
{
Name = name;
Value = value;
Type = type;
}
public static class Default
{
public static Setting RequireEmailValidation => new Setting("RequireEmailValidation", false.AsString(), nameof(Boolean));
}
}
So it's a bit hinky if you're not sure why I did things.
First: I have the parameterized constructor and the default constructor, which seem to contradict. I wanted the parameterized one so I can make absolutely sure to set the required properties for a setting when I instantiate them. The default one is for Entity Framework.
Second: I have this static class Default
which contains the default settings of the application. This is for two purposes:
First: seeding. It allows me to easily seed the default settings of the application:
protected override void Seed(Models.MasterDbContext context)
{
var settings = typeof(Models.Setting.Default).GetProperties();
foreach (var setting in settings)
{
context.Settings.AddOrUpdate(
x => x.Name,
(Models.Setting)setting.GetValue(null, null)
);
}
context.SaveChanges();
}
Obviously this is nice. It makes seeding a breeze.
Next: it makes it nice to locate a setting from the DB:
_context.Settings.First(x => x.Name == Setting.Default.RequireEmailValidation.Name)
You can't bork a string name this way (you can, but you don't need to). You can just say Setting.Default.SomeSetting.Name
to get it. Awesome! It also means if you want to change a setting value from the default you can just grab Setting.Default.SomeSetting
and manipulate the .Value
. Excellent!
This obviously creates the problem of all Setting
values being a string. So I have an extension for that:
public static class SettingExtensions
{
/// <summary>
/// Returns the <see cref="Setting.Value"/> value as the specified type.
/// </summary>
/// <typeparam name="T">The <see cref="IConvertible"/> type to convert the <see cref="Setting"/> to.</typeparam>
/// <param name="setting">The <see cref="Setting"/> to convert.</param>
/// <returns>The strong type of the <see cref="Setting.Value"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T As<T>(this Setting setting)
where T : IConvertible =>
setting.Value.As<T>();
}
And a string
extension as well:
public static class StringExtensions
{
/// <summary>
/// Converts a <see cref="string"/> to a strong type.
/// </summary>
/// <typeparam name="T">An <see cref="IConvertible"/> type to return from the <see cref="string"/>.</typeparam>
/// <param name="value">The <see cref="string"/> value to convert.</param>
/// <returns>The strong type value of the <see cref="string"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T As<T>(this string value)
where T : IConvertible =>
(T)Convert.ChangeType(value, typeof(T));
/// <summary>
/// Converts an <see cref="IConvertible"/> to a <see cref="string"/>.
/// </summary>
/// <typeparam name="T">The <see cref="IConvertible"/> to convert.</typeparam>
/// <param name="value">The <see cref="IConvertible"/> value to convert.</param>
/// <returns>The <see cref="string"/> representation of the <see cref="IConvertible"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string AsString<T>(this T value)
where T : IConvertible =>
(string)Convert.ChangeType(value, typeof(string));
}
So if you have a basic type (which all mine are at the moment) you can just do:
false.AsString(); SomeSetting.As<bool>();
Etc.
Actual usage:
EmailConfirmed = !_context.Settings.First(x => x.Name == Setting.Default.RequireEmailValidation.Name).As<bool>()
So, this is all fairly simple in the end, but it looks complex.
Please comment on anything I can improve. There will eventually be a crapton (that's metric) of Setting
values in the DB, and I want to make sure they're right from the beginning.
Also: due to the importance of this, I will be offering a (or more) bounty (when it's eligible) to the best answer(s).
Type
property ? And what's it's purpose ? As you can't directly convert string with valueBoolean
to boolean type. \$\endgroup\$Type
property to display that to the user. (So they know what valid values are.) \$\endgroup\$string
properties everywhere right ? \$\endgroup\$