首先,这就是为什么这个问题不重复的原因:
我知道有关将enum
转换为另一个ADONETType
的多个问题已被问到,我甚至自己回答one of them,但我在这个主题上发现的所有问题都有某种方法比较不同的枚举值(无论是名称还是值)。在我的特定情况下,我不知道价值观,而且名称也不匹配。
作为我工作的GitHub项目的一部分,名为ADONETHelper,旨在最大限度地减少与Ado.Net合作时的代码重复,我面对需求在不相关的枚举之间转换值。这是为了允许相同的代码与OleDb,Odbc和SqlClient一起使用(并且希望将来也可以使用OracleClient和MySqlClient)。
我尝试做的是创建不同枚举的统一 - 特别是 - 描述sql参数数据类型的枚举。
好吧,我支持4个枚举 -
System.Data.DbType
,
System.Data.SqlClient.SqlDbType
,
System.Data.OleDb.OleDbType
,
System.Data.Odbc.OdbcType
但如果我想添加对OracleClient或MySqlClient的支持,我必须非常努力地添加System.Data.OracleClient.OracleType
或MySql.Data.MySqlClient.MySqlDbType
。
所以我想找到一个更优雅的方式,然后我想出来了。
这是我目前的代码(它的效果很好,但正如我写的那样,添加对新枚举的支持很难):
首先,我已经定义了自己的枚举Boolean
。它包含Byte
,Binary
,Char
,DBTypeConverter
等条目'。
然后我创建了一个名为internal static class DBTypeConverter
{
#region private members
private static List<DbTypeMap> _Map;
#endregion private members
#region static constructor
static DBTypeConverter()
{
_Map = new List<DbTypeMap>()
{
new DbTypeMap(ADONETType.Boolean, DbType.Boolean, SqlDbType.Bit, OleDbType.Boolean, OdbcType.Bit),
new DbTypeMap(ADONETType.Byte, DbType.Byte, SqlDbType.TinyInt, OleDbType.UnsignedTinyInt , OdbcType.TinyInt),
new DbTypeMap(ADONETType.Binary, DbType.Binary, SqlDbType.Binary, OleDbType.Binary, OdbcType.Binary),
// ... more of the same here ...
new DbTypeMap(ADONETType.Xml, DbType.Xml, SqlDbType.Xml, null, null)
};
}
#endregion static constructor
#region methods
internal static DbType ToDbType(this ADONETType type)
{
return type.ConvertTo<DbType>();
}
internal static SqlDbType ToSqlType(this ADONETType type)
{
return type.ConvertTo<SqlDbType>();
}
internal static OleDbType ToOleDbType(this ADONETType type)
{
return type.ConvertTo<OleDbType>();
}
internal static OdbcType ToOdbcType(this ADONETType type)
{
return type.ConvertTo<OdbcType>();
}
private static dynamic ConvertTo<T>(this ADONETType type)
{
var returnValue = _Map.First(m => m.ADONETType == type).GetValueByType(typeof(T));
if(returnValue != null)
{
return returnValue;
}
throw new NotSupportedException(string.Format("ADONETType {0} is not supported for {1}", type, typeof(T)));
}
#endregion methods
#region private struct
private struct DbTypeMap
{
#region ctor
public DbTypeMap(ADONETType adonetType, DbType? dbType, SqlDbType? sqlDbType, OleDbType? oleDbType, OdbcType? odbcType)
: this()
{
ADONETType = adonetType;
DbType = dbType;
SqlDbType = sqlDbType;
OleDbType = oleDbType;
OdbcType = odbcType;
}
#endregion ctor
#region properties
internal ADONETType ADONETType { get; private set; }
internal DbType? DbType { get; private set; }
internal SqlDbType? SqlDbType { get; private set; }
internal OleDbType? OleDbType { get; private set; }
internal OdbcType? OdbcType { get; private set; }
#endregion properties
#region methods
internal dynamic GetValueByType(Type type)
{
if (type == typeof(ADONETType))
{
return this.ADONETType;
}
if(type == typeof(DbType))
{
return this.DbType;
}
if (type == typeof(SqlDbType))
{
return this.SqlDbType;
}
if (type == typeof(OleDbType))
{
return this.OleDbType;
}
if (type == typeof(OdbcType))
{
return this.OdbcType;
}
return null;
}
#endregion methods
}
#endregion private struct
}
的静态类,为此枚举提供扩展方法,以便它的值可以转换为其他枚举。
这就是这个类的样子:
OracleType
现在,正如您所看到的,为了提供对OracleClient的支持,我必须执行以下操作:
DbTypeMap
私有结构中添加DbTypeMap
的属性。GetValueByType
构造函数更改为也接受oracle类型。DBTypeConverter
方法中为交换机添加另一个案例。internal static OracleType ToOracleType(this ADONETType type)
。DBTypeConverter
)添加到{{1}}。很明显,这是一项很多工作,我宁愿找到另一种方法来统一这些枚举,因此增加对新客户的支持将更容易。
这是你的专家进场的时候。
答案 0 :(得分:4)
假设你真的需要这个(考虑Jeroen's comment,如果没有,那么你可以将其重用于其他东西......)你可以使用等价列表来简化事情。它只是一个数组列表,其中数组项是等效的。我正在使用数组而不是类,因为在添加新等价时我不需要添加属性和ctor参数。要存储等价,我使用特殊的Enum
基类,但object
也很好(不需要,AFAIK,用于dynamic
)。
找到转换就是这些列表中的搜索问题(这里的代码比性能更明确):
public static TTo ConvertTo<TTo>(Enum value)
{
var set = Mapping.FirstOrDefault(values => Array.IndexOf(values, value) != -1);
if (set == null)
throw new InvalidOperationException($"Value {value} is unknown");
return (TTo)(object)set.First(x => x.GetType() == typeof(TTo));
}
根据需要填充Mapping
列表,例如,可以将其定义为:
private static List<Enum[]> Mapping = new List<Enum[]>
{
new Enum[] { ADONETType.Byte, DbType.Byte, SqlDbType.TinyInt },
new Enum[] { ADONETType.Boolean, DbType.Boolean, SqlDbType.Bit },
new Enum[] { ADONETType.Binary, DbType.Binary, SqlDbType.Binary },
new Enum[] { ADONETType.Xml, DbType.Xml },
};
请注意,丑陋的双重演员(TTo)(object)
...啊啊.NET Generics ...欢迎更好的解决方案。为了支持新的等价,您仍然需要将所有枚举值映射到此表中,令人烦恼但在一个地方本地化。如果无法转换(未在任何地方定义值value
),或者没有已知的TTo
转换(例如最后DbType.Xml
),则InvalidOperationException
为抛出。