C#标识符中的非ASCII字符由NHibernate引用

时间:2011-03-23 19:35:31

标签: c# nhibernate enums

我试图在C#标识符中输入非ASCII字符,程序编译并运行得很好(至少乍一看)。更确切地说,我在枚举中使用克罗地亚变音字符(čćđšž)。我真的需要使用这些特殊字符,因为我将字符串枚举映射为NHibernate中的值对象。在将它们显示为对用户的查找时,必须避免使用这些标准字符真的很难看。在我以这种方式大规模开始使用枚举之前,我真的需要知道这种编程技术是否存在任何影响(隐藏的陷阱)(特别是关于NHibernate)?或者,如果您有更好的处理方式,请告诉我。

此外,用于重构的工具,SVN等是否存在任何问题。

4 个答案:

答案 0 :(得分:6)

C#语言使用unicode编码,这意味着您的特殊字符不会出现问题。但是,我喜欢在没有特殊字符的情况下保留我的英文代码。我还没有找到一个合理使用特定文化命名的问题。

来自C#语言规范:

  

C#程序由一个或多个组成   源文件,正式称为   编制单位(§9.1)。来源   file是Unicode的有序序列   字符。通常是源文件   与...一一对应   文件系统中的文件,但是这个   不需要通信。对于   最大可移植性,建议使用   编码文件系统中的文件   使用UTF-8编码。

第2.1节(Microsoft C# Language Specification

答案 1 :(得分:3)

我将这个自定义用户类型安全地从enum转换为int for NHibernate。你应该将函数更改为使用string而不是int。然后,您可以更改方法“NullSafeGet”以进行从数据库值到枚举值的转换。这样,您可以在数据库中使用普通字符作为枚举值和用户可读名称。 编辑:我自己做了改动。您可以将此类用于自定义用户类型。你应该实现“SpecialConversion”方法,从你的特殊字符串转换为枚举,反之亦然。

public class EnumToSpecialStringType<TEnum> : IUserType
{
    #region IUserType Members

    /// <summary>
    /// Reconstruct an object from the cacheable representation. At the very least this
    /// method should perform a deep copy if the type is mutable. (optional operation)
    /// </summary>
    /// <param name="cached">the object to be cached</param>
    /// <param name="owner">the owner of the cached object</param>
    /// <returns>a reconstructed object from the cachable representation</returns>
    public object Assemble(object cached, object owner)
    {
        return DeepCopy(cached);
    }

    /// <summary>
    /// Return a deep copy of the persistent state, stopping at entities and at collections.
    /// </summary>
    /// <param name="value">generally a collection element or entity field</param>
    /// <returns>a copy</returns>
    public object DeepCopy(object value)
    {
        return value;
    }

    /// <summary>
    /// Transform the object into its cacheable representation. At the very least this
    /// method should perform a deep copy if the type is mutable. That may not be enough
    /// for some implementations, however; for example, associations must be cached as
    /// identifier values. (optional operation)
    /// </summary>
    /// <param name="value">the object to be cached</param>
    /// <returns>a cacheable representation of the object</returns>
    public object Disassemble(object value)
    {
        return DeepCopy(value);
    }

    /// <summary>
    /// Compare two instances of the class mapped by this type for persistent "equality"
    /// ie. equality of persistent state
    /// </summary>
    /// <param name="x"/>
    /// <param name="y"/>
    /// <returns/>
    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        return x.Equals(y);
    }

    /// <summary>
    /// Get a hashcode for the instance, consistent with persistence "equality"
    /// </summary>
    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    /// <summary>
    /// Are objects of this type mutable?
    /// </summary>
    public bool IsMutable
    {
        get { return false; }
    }

    /// <summary>
    /// Retrieve an instance of the mapped class from a ADO.NET resultset.
    /// Implementors should handle possibility of null values.
    /// </summary>
    /// <param name="rs">a IDataReader</param>
    /// <param name="names">column names</param>
    /// <param name="owner">the containing entity</param>
    /// <returns/>
    /// <exception cref="HibernateException">HibernateException</exception>
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var value = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;

        // Put you special conversion here (string -> enum)
        var converted = SpecialConversion(value);

        return converted;
    }

    /// <summary>
    /// Write an instance of the mapped class to a prepared statement.
    /// Implementors should handle possibility of null values.
    /// A multi-column type should be written to parameters starting from index.
    /// </summary>
    /// <param name="cmd">a IDbCommand</param>
    /// <param name="value">the object to write</param>
    /// <param name="index">command parameter index</param>
    /// <exception cref="HibernateException">HibernateException</exception>
    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        var parameter = (IDataParameter)cmd.Parameters[index];

        // Do conversion from your enum to database string value
        var converted = SpecialConversion(value);

        parameter.Value = converted;
    }

    /// <summary>
    /// During merge, replace the existing (<paramref name="target"/>) value in the entity
    /// we are merging to with a new (<paramref name="original"/>) value from the detached
    /// entity we are merging. For immutable objects, or null values, it is safe to simply
    /// return the first parameter. For mutable objects, it is safe to return a copy of the
    /// first parameter. For objects with component values, it might make sense to
    /// recursively replace component values.
    /// </summary>
    /// <param name="original">the value from the detached entity being merged</param>
    /// <param name="target">the value in the managed entity</param>
    /// <param name="owner">the managed entity</param>
    /// <returns>the value to be merged</returns>
    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    /// <summary>
    /// The type returned by <c>NullSafeGet()</c>
    /// </summary>
    public Type ReturnedType
    {
        get
        {
            return typeof(TEnum);
        }
    }

    /// <summary>
    /// The SQL types for the columns mapped by this type. 
    /// </summary>
    public SqlType[] SqlTypes
    {
        get
        {
            return new[] { new SqlType(DbType.String) };
        }
    }

    #endregion
}

我使用流畅的NHibernate和我的方式使用这个自定义usertype映射我的列:

Map(x => x.PropertyName, "ColumnName").CustomType<EnumToSpecialStringType<EnumType>>();

编辑:这是一篇关于如何使用xml映射文件映射用户类型的帖子:

nHibernate mapping to custom types

答案 2 :(得分:1)

我只是在这里编写示例来说明如何进行映射。

考虑所有值包含非ASCII字符的枚举:

public enum MyEnum
{
    NonAsciiName1,
    NonAsciiName2,
    NonAsciiName3,
}

您可以将其更改为在所有值都包含ASCII字符的情况下执行此操作:

public enum MyEnum
{
    AsciiName1,
    AsciiName2,
    AsciiName3,
}

public static class MyEnumExtensions
{
    static readonly Dictionary<MyEnum, string> map = new Dictionary<MyEnum, string>
    {
        { MyEnum.AsciiName1, "NonAsciiName1" },
        { MyEnum.AsciiName2, "NonAsciiName2" },
        { MyEnum.AsciiName3, "NonAsciiName3" },
    };

    public static string GetValue(this MyEnum key)
    {
        return map[key];
    }
}

然后您的代码需要进行小的更改才能使用它:

// you probably had something like:
var myEnumValue = MyEnum.NonAsciiName1;
var value = myEnumValue.ToString();

// now becomes:
var myEnumValue = MyEnum.AsciiName1;
var value = myEnumValue.GetValue();

或者有DEHAAS建议,使用以类似方式工作的属性。但是你必须使用反射来获得具有一点性能损失的值。

using System.ComponentModel;

public enum MyEnum
{
    [DescriptionAttribute("NonAsciiName1")] AsciiName1,
    [DescriptionAttribute("NonAsciiName2")] AsciiName2,
    [DescriptionAttribute("NonAsciiName3")] AsciiName3,
}

public static class MyEnumExtensions
{
    public static string GetValue(this MyEnum key)
    {
        return typeof(MyEnum).GetField(key.ToString())
                             .GetCustomAttributes(typeof(DescriptionAttribute), false)
                             .Cast<DescriptionAttribute>()
                             .Single()
                             .Description;
    }
}

答案 3 :(得分:1)

是的,我认为这是一个陷阱。

向用户显示查找并保留数据库中可能(唯一)项目的列表可以更好地单独保存。特别是如果您的应用程序将来支持其他语言。

不是非常面向对象你可以在你的代码中有一个类,它是你的枚举和相应语言的值之间的直接映射,就像这样的nhibernate映射:

<class name="DropDown" table="DropDown_Language">
    <!-- map onto the ENUMeration -->
    <id name="UniqueID" column="Id" type="Int32" >
        <generator class="identity" />
    </id>
    <property name="DropDownType" column="DropDownTypeId" type="DropDownType, Domain"/>
    <property name="Language" column="LanguageId" type="LanguageReferenceType,  "/>
    <property name="DropDownTypeDescription" column="DropDownTypeDescription" type="string"/>
</class>

然后在DropDownTypeDescription列中输入下拉列表的值。

希望我理解你的问题: - )