1

我们正在开发一个 NHibernate 项目,我们需要在其中实现 NHibernateIUserType接口。所以我们做到了,到目前为止它运行良好(我们还没有开始测试该NullSafeSet方法)。但是,我们有 1 个问题:我们需要知道我们正在映射什么属性才能返回正确的结果。

情况:

我们需要映射的值作为 存储在数据库中int,我们需要从该值创建一个Code对象。为了能够创建 Code 对象,我们需要string与前面提到的int. 这string是我们正在映射的属性的名称。

我已经发现该names参数包含属性的 NHibernate SQL 别名(它的形式为:“IS69_6_”)但我不知道这个值是否保持不变(当我调试时它似乎仍然是相同的)。

需要澄清的一些代码: CodeTypeMapper.cs

public class CodeTypeMapper : IUserType
{
    public new bool Equals(object x, object y)
    {
        return x != null && x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    /********** Code relevant to the question: **********/
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var key = (int?)NHibernateUtil.Int32.NullSafeGet(rs, names[0]);
        if (key == null) return null;
        // So here we need to be able to get the correct code.
        // In order to get the correct code, we need the key (fetched above)
        // but we also need the name of the property that we are mapping
        // which has a dummy value in this example.
        Code code = MasterMetaProvider.MasterMeta.GetCode("PropertyName", (int)key);
        if (code == null) return null;
        // Now that we have the Code object, we can use it to return the proper result
        var result = new NetworkObjectEnum((int)key, code.CodeDescEn);
        return result;
    }
    /***************************************************/

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        if (value == null)
        {
            NHibernateUtil.Int32.NullSafeSet(cmd, null, index);
        }
        else
        {
            value = int.Parse(value.ToString());
            NHibernateUtil.Int32.NullSafeSet(cmd, value, index);
        }
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    public object Assemble(object cached, object owner)
    {
        return cached;
    }

    public object Disassemble(object value)
    {
        return value;
    }

    public SqlType[] SqlTypes
    {
        get
        {
            var types = new SqlType[1];
            types[0] = new SqlType(DbType.String);
            return types;
        }
    }
    public Type ReturnedType
    {
        get { return typeof(NetworkObjectEnum); }
    }
    public bool IsMutable
    {
        get { return false; }
    }
}

NHibernate 映射代码:

// We need this Property's name
Map(x => x.PropertyName).Column("PropertyNameInDB").CustomType<CodeTypeMapper>();


问题:

所以我想问的问题是:是否可以在CodeTypeMapper类中访问上述属性的名称?

提前致谢。

问候, Yanik Ceulemans


2013 年 9 月 23 日更新:

为了澄清我的问题,我将举一个例子:假设有一个代码表指定了城市、交通类型等......:

+----+-----------+------------+-----------+
| id | code_type | code_value | code_desc |
+----+-----------+------------+-----------+
|  1 | city      |          5 | Antwerp   |
|  2 | city      |          8 | Brussels  |
|  3 | city      |          1 | Ghent     |
|  4 | transport |          1 | boat      |
|  5 | transport |          8 | plane     |
|  6 | transport |          9 | car       |
+----+-----------+------------+-----------+

接下来,我们有一个旅行表,它以下列方式使用此代码表:

+----+------+-------------------+-----------+
| id | city |   other_column    | transport |
+----+------+-------------------+-----------+
|  1 |    5 | some random value |         1 |
|  2 |    8 | randomness        |         8 |
|  3 |    1 | a value           |         9 |
+----+------+-------------------+-----------+

如您所见:如果我们想知道代码的描述,我们可以通过查看列名来知道需要从代码表中获取什么值,以及从旅行表中该列中的值。

c# 类Travel可能看起来像这样:

public class Travel
{
    public int Id { get; set; }
    public int City { get; set; }
    public string OtherColumn { get; set; }
    public int Transport { get; set; }
}

所以我的问题是:在映射 Travel 类时,是否有可能IUserType在运行时在实现中获取属性名称“city”,以便能够从代码表中获取正确的值?(注意:我无法更改数据库设计。)

4

2 回答 2

2

这种设计被称为“神桌”,你有我的同情。当我开始时,我们的主要产品有一个,我花了一年的时间来使其正常化。

在重构之前,我使用 NHibernate 映射它,使用视图对其进行虚拟规范化。也就是说,为城市、交通等创建一个视图,并将它们映射为表格。我看到您无法更改架构,但如果您可以添加视图,这是一个很好的解决方案。

备选方案 1:为代码表中的每种类型创建一个 IUserType 实现。

备选方案 2:在使用视图之前,我使用 NHibernate 向映射添加 where 子句的能力。我记得这种方法有一些缺点,我认为其中一个缺点是 where 子句并不总是按预期应用在查询中。使用 Fluent NHibernate 映射将如下所示:

public abstract Class CodeTableBase
{
    public virtual int Id { get; protected set; }
    public virtual string CodeType { get; protected set; }
    public virtual int Value { get; protected set; }
    public virtual string Description { get; protected set; }
}

public class City : CodeTableBase {}

public abstract class CodeTableMap<T> : ClassMap<T> where T : CodeTableBase
{
    public CodeTableMap() 
    {
        Table("CodeTable");
        Id(x => Id, "id").your id generation strategy
        CodeType(x => x.CodeType, "code_type");
        Value(x => x.Value, "code_value");
        Description(x => x.Description, "code_description");
    }

    public CodeTableMap(string codeType) : this()
    {
        Where("code_type = '" + codeType + '"); // may need to use property name
    }
}

public class CityMap : CodeTableMap<City>
{
    public CityMap : base("city") {}
}
于 2013-09-25T13:17:59.070 回答
0

我不确定你的意思,但也许你需要这样的东西:

这里


实现 IParameterizedType 您可以发送映射属性的名称:

这里

Fluent NHibernate 不提供此功能,但您可以使用 xml 文件。

映射


其他不太优雅的解决方案:

var propertyName = GetPropertyName(owner.GetType(), owner, typeof(YourAttribute));

public static string GetPropertyName(Type entityType, object entity, Type attributeType)
{
    if (entity == null) return null;

    var propertyInfo = GetPropertyInfo(entityType, attributeType);

    return propertyInfo == null ? null : propertyInfo.Name;
}

public static PropertyInfo GetPropertyInfo(Type entityType, Type attributeType)
{
    return entityType.GetProperties().FirstOrDefault(prop => prop.GetCustomAttributes(false)
        .Count(x => x.GetType() == attributeType) > 0);
}
于 2013-09-20T14:59:50.127 回答