将不同的枚举映射到一起

时间:2017-09-28 08:36:06

标签: c# .net enums

首先,这就是为什么这个问题不重复的原因:

我知道有关将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.OracleTypeMySql.Data.MySqlClient.MySqlDbType。 所以我想找到一个更优雅的方式,然后我想出来了。

这是我目前的代码(它的效果很好,但正如我写的那样,添加对新枚举的支持很难):

首先,我已经定义了自己的枚举Boolean。它包含ByteBinaryCharDBTypeConverter等条目&#39;。 然后我创建了一个名为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的支持,我必须执行以下操作:

  1. DbTypeMap私有结构中添加DbTypeMap的属性。
  2. GetValueByType构造函数更改为也接受oracle类型。
  3. DBTypeConverter方法中为交换机添加另一个案例。
  4. 将oracle类型添加到internal static OracleType ToOracleType(this ADONETType type)
  5. 的静态构造函数中
  6. 将方法(DBTypeConverter)添加到{{1}}。
  7. 很明显,这是一项很多工作,我宁愿找到另一种方法来统一这些枚举,因此增加对新客户的支持将更容易。
    这是你的专家进场的时候。

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为抛出。