Creating a settings table that can handle almost any type of value

本文介绍了一种使用Entity Framework设计能够存储各种类型值的通用设置表的方法。通过将值存储为byte数组并使用BinaryFormatter进行序列化和反序列化,实现了灵活的数据存储方案。

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&lt;T&gt;/// <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

http://buildstarted.com/2010/08/09/creating-a-settings-table-that-can-handle-almost-any-type-of-value/

转载于:https://www.cnblogs.com/yezhi/p/3139509.html

<think>好的,用户问的是为什么CREATE TABLE...AS SELECT(CTAS)可能不是创建标准表的最佳选择,而推荐使用CREATE TABLE加INSERT的方式。首先我需要理解这两个方法的区别和各自的优缺点。 首先,CTAS确实很方便,一条语句就能创建表并插入数据,但可能存在哪些问题呢?可能是在列类型、约束、索引等方面不够精确。比如,当源表的数据类型不明确时,CTAS可能会自动选择不合适的数据类型,导致后续插入数据时出现问题。例如,如果源表中的某个字段是VARCHAR(100),但实际需要更长的长度,CTAS可能不会自动调整,而手动创建表可以明确指定这些细节。 然后,用户提到的“列修饰符和表约束”是关键点。比如,主键、外键、唯一约束、默认值、非空约束等,CTAS可能不会保留这些信息,除非在SELECT子句中显式处理。而手动CREATE TABLE可以明确定义这些约束,确保表结构的正确性。 接下来是INSERT INTO...SELECT的方式,这种方法允许在已有结构正确的表中批量插入数据,同时可以结合事务处理,保证数据的一致性。而CTAS可能在事务处理上不够灵活,或者在复制大量数据时效率不如预期。 另外,索引的问题。使用CTAS创建的新表通常不会继承源表的索引,需要手动重新创建,而手动建表时可以预先定义好索引,提高查询性能。 还需要考虑数据一致性和完整性。例如,如果源表有触发器或其他依赖对象,CTAS可能不会复制这些,导致新表的行为不一致。手动建表可以确保所有必要的约束和触发器都被正确设置。 可能用户的应用场景是需要精确控制表结构,比如在生产环境中迁移或复制数据,这时候结构准确性比方便更重要。因此,用户需要明白虽然CTAS快捷,但在需要严谨结构的情况下,分开操作更可靠。 最后,总结时应该分点说明CTAS的不足和手动创建的优势,帮助用户清晰理解两者的适用场景,并提供具体的步骤示例,让用户知道如何正确操作。</think>在数据库管理中,使用`CREATE TABLE...AS SELECT`(简称CTAS)直接复制表数据时需注意以下技术细节。以下分步骤说明原因及更优方案: --- ### **一、CTAS的局限性** 1. **数据类型推断问题** - 系统会根据`SELECT`结果自动推断列类型,可能导致精度丢失 - 例如:源表字段为`DECIMAL(10,2)`,若查询中包含计算`(price * quantity)`,目标字段可能被推断为`DOUBLE` 2. **约束与索引缺失** - 主键、外键、唯一约束、非空约束等不会自动复制 - 索引(INDEX)和注释(COMMENT)需要手动重建 3. **表属性不继承** - 存储引擎、字符集、分区规则等需显式定义 - 例如:从InnoDB表复制时,默认可能生成MyISAM表 --- ### **二、推荐方案:分步创建** #### **步骤1:显式定义表结构** ```sql -- 完整定义列类型、约束和表属性 CREATE TABLE new_table ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, CHECK (CHAR_LENGTH(name) > 3) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 PARTITION BY RANGE (YEAR(created_at)); ``` #### **步骤2:批量插入数据** ```sql -- 使用事务保证原子性 START TRANSACTION; INSERT INTO new_table (id, name, created_at) SELECT id, name, created_at FROM old_table WHERE created_at > '2023-01-01'; COMMIT; ``` --- ### **三、关键优势对比** | 特性 | CTAS方案 | 分步方案 | |---------------------|------------------------|------------------------| | **数据类型控制** | 自动推断,易出错 | 精准定义 | | **约束完整性** | 完全丢失 | 完整保留 | | **事务支持** | 单语句不可回滚 | 支持事务块 | | **性能优化** | 无索引,查询效率低 | 可预建索引优化查询 | | **表属性继承** | 默认值可能不符需求 | 完全自定义 | --- ### **四、特殊场景例外** 以下情况可考虑CTAS: 1. 快速创建临时表用于中间计算 2. 不需要持久化结构的探索性分析 3. 兼容性要求低的一次性数据导出 建议优先采用分步方案以确保生产环境的健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值