Creating a settings table that can handle almost any type of value
Update: Updated article here.
Today I wanted to be able to have a table store any type of value as a way to store some settings for an application. The table needed to be able to store basically a name/value pair.
I designed the object using a code-first approach
Initial Idea
publicclassSetting{[Key]publicstringName{get;set;}publicobjectValue{get;set;}publicstringType{get;set;}}
Simple and to the point.
Here is a snippet from my DataContext
publicclassDataContext:DbContext,IDataContext{publicDbSet<Setting>Settings{get;set;}}
To access the data I would simply cast Value to whatever type I expected it to be.
var x =(string)Settings.Single(s => s.Name=="sitename").Value;
Problems
However, with EF and probably Linq (I didn’t test that) the Value was always null after I calledSaveChanges. At first I couldn’t understand why but then it hit me. There’s no direct relationship between object and SQL. So I switched the Value to a byte[]. Here is my new class.
publicclassSetting{[Key]publicstringName{get;set;}publicbyte[]Value{get;set;}publicstringType{get;set;}}
This caused a few problems with the first method since it’s not possible to typecast from a byte[] to a DateTime, int, string…and so on. So I added a few helper methods to my DataContext.
Solution
public T Setting<T>(stringName){//grab the the setting if it exists otherwisevar value =Settings.SingleOrDefault(s => s.Name==Name);//return the default value for typeof(T)if(value ==null)returndefault(T);//If they're trying to cast to a different type we should throw an error//They can to another type conversion after grabbing the valueif(Type!=typeof(T).FullName)thrownewInvalidCastException(string.Format("Unable to cast: {0} to {1}",typeof(T).FullName,Type));//Using the BinaryFormatter, return a typecast valuereturn(T)(newBinaryFormatter().Deserialize(newSystem.IO.MemoryStream(value.Value)));}publicvoidSetting<T>(stringName, T Value){//check for an existing valuevar setting =Settings.SingleOrDefault(s => s.Name==Name);//serialize the new valueSystem.IO.MemoryStream ms =newSystem.IO.MemoryStream();newBinaryFormatter().Serialize(ms,Value);if(setting !=null){//for consistency let's make sure we're assigning the same typeif(Type!=typeof(T).FullName)thrownewInvalidCastException(string.Format("Unable to cast: {0} to {1}",typeof(T).FullName,Type));
setting.Value= ms.ToArray();}else{//add a new value to the database
value =newModels.Setting(){Name=Name,Type=typeof(T).Fullname,Value= ms.ToArray()};Settings.Add(value);}}
Now instead of calling the above cast we can now use the following
var x =DataContext.Setting<string>("sitename");//getDataContext.Setting<string>("sitename","buildstarted.com");//set
Hope this is helpful and inspires someone. Please comment if you have a better method. I’m not too keen on serializing all the time but it’s the best method I’ve come up with so far.
One of the problems with having values stored in byte[] form is lack of searchability of values. However, you’re unlikely to select all settings with values of “true”.
So after working with the Settings table from my previous post a bit, it’s clear to me that creating some extension methods would make working with settings easier. To do this though we have to modify the Model a bit first and rename “Value” to lowercase “value”. The summary xml documentation is there to inform the user there’s an Extension method
publicclassSetting{publicintSettingID{get;set;}publicstringName{get;set;}/// <summary>/// Use the extension methods instead of this value; ex: Value<T>/// <summary>publicbyte[] value {get;set;}publicstringType{get;set;}}
Creating the Extension Method
[Extension]publicstatic T Value<T>(thisSetting setting){if(setting ==null)returndefault(T);if(setting.Type!=typeof(T).FullName)thrownewInvalidCastException(string.Format("Unable to cast: {0} to {1}",typeof(T).FullName,
setting.Type));return(T)(newBinaryFormatter().Deserialize(newSystem.IO.MemoryStream(setting.value)));}[Extension]publicstaticvoidValue<T>(thisSetting setting, T value){if(setting ==null)thrownewArgumentNullException("setting");System.IO.MemoryStream ms =newSystem.IO.MemoryStream();newBinaryFormatter().Serialize(ms, value);if(setting.Type!=typeof(T).FullName)thrownewInvalidCastException(string.Format("Unable to cast: {0} to {1}",typeof(T).FullName,
setting.Type));
setting.value = ms.ToArray();}
Doing this also has the added benefit of making the Generic methods in the DataContext cleaner
public T Setting<T>(stringName){var setting =Settings.SingleOrDefault(s => s.Name==Name);return setting.Value<T>();}publicvoidSetting<T>(stringName, T Value){var setting =Settings.SingleOrDefault(s => s.Name==Name);
setting.Value<T>(Value);}
As you can see the extension methods are very easy to create. This gives us the benefit of accessing the values in a cleaner way
var setting = db.Settings.First();
setting.Value<string>("this is a new value");var theValue = setting.Value<string>();
The generic methods are what make the settings Model powerful and (almost) seamless.
– Ben
Creating a settings table that can handle almost any type of value的更多相关文章
- Fun with dynamicobject dynamic and the settings table
What came before In my previous post I discussed ways of making the settings table using Generics to ...
- webpack 3.8 使用 extract-text-webpack-plugin 3.0 抽取css失败:You may need an appropriate loader to handle this file type.
webpack 3.8.1 使用 extract-text-webpack-plugin 3.0.2 抽取css时失败,报错: ERROR in ./src/static/style/localTim ...
- Column 1 of table 'xxx' cannot be converted from type 'varchar(33)' to type 'varchar(11)'
mysql主从同步失败.错误日志如下. Column 1 of table 'xxx' cannot be converted from type 'varchar(33)' to type 'var ...
- RoR - Creating and Modifying Table and Columns
自动生成的id 被当作primary key来使用 timestamp method生成 created_at 与 updated_at columns create_table 和 drop_tab ...
- vue-cli使用vux时报错处理,“You may need an appropriate loader to handle this file type”
先说解决方案: 在项目中找到build,找到webpack.base.conf.js 将vux给出的解决方案代码拷贝出来 const vuxLoader = require('vux-loader') ...
- “You may need an appropriate loader to handle this file type”
这里不能为空!!!!!!!!!!!!!!!!!!!!
- vue Module parse failed: Unexpected token You may need an appropriate loader to handle this file type
1.错误截图: 2.错误原因:webpack 原生只支持 js 文件类型,及 es5 的语法 3.解决方法:在webpack.config.js中,增加以下配置 module: { rules: [ ...
- centos linux系统日常管理复习 CPU物理数逻辑核数,iftop ,iotop ,sar ,ps,netstat ,一网卡多IP,mii-tool 连接,ethtool速率,一个网卡配置多个IP,mii-tool 连接,ethtool速率 ,crontab备份, 第十八节课
centos linux系统日常管理复习 物理CPU和每颗CPU的逻辑核数,uptime ,w,vmstat,iftop ,iotop ,sar ,ps,netstat ,一个网卡配置多个IP,mii ...
- Linux系统入门命令100条 转
https://www.howtoforge.com/linux-commands/ 2017-04-27 RiboseYim 睿哥杂货铺 Author : Himanshu Arora 原文:htt ...
随机推荐
- dede轮播图
DEDEcms 里面有个自带的轮播图调用: 模版 ------> 广告管理 --------> 增加一个新广告 ----进入后的界面 填写后 点击 代码获取相应的JS调用代码:或者织梦CM ...
- DEDE数据库修改
dede数据库配置文件所在路径为:/data/common.inc.php 修改方法: 下面是该dede数据库配置文件的内容: <?php //数据库连接信息 $cfg_dbhost = 'lo ...
- 兼容IE的渐变
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=, startColorstr=#1471da, endColorstr ...
- mongodb spring
可参考 http://blog.csdn.net/cuiran/article/details/8287204 我修改后的代码 http://pan.baidu.com/s/1mgJYbaC
- Hex文件
那么什么是Hex文件呢?Intel Hex文件是由一行行符合Intel Hex文件格式的文本所构成的ASCII文本文件.在Intel Hex文件中,每一行包含一个Hex记录.这些记录由对应机器语言码和 ...
- Teach Yourself Scheme in Fixnum Days 6 recursion递归
A procedure body can contain calls to other procedures, not least itself: (define factorial (lambda ...
- ubuntu记录
1. gleboneblack OMAPES=4.x ANDROID_ROOT_DIR=$HOME/aosp W=1 install /bin/sh: 5: ./install.sh: Permiss ...
- 关于bootstrap--表单(按钮<button>效果、大小、禁用)
1.各种标签实现按钮效果: <button class="btn btn-default" type="button">button标签按钮< ...
- phpcms:八、show.html
标题:{$title}来源:{$copyfrom}评论:<a href="#comment_iframe" id="comment">0</a ...
- js基础例子
创建变量 var obj=value; 其中obj是变量名; value表示可能是数字,数组,函数之类的 多变量进行计算 var a1=200,b1='hello',c1=400; var d1=c1 ...