NHibernate IUserType获取映射的属性名称

时间:2013-09-17 14:37:21

标签: c# .net nhibernate fluent-nhibernate

我们正在开发一个NHibernate项目,我们需要实现NHibernate IUserType接口。所以我们做了,到目前为止它工作正常(我们尚未开始测试NullSafeSet方法)。但是,我们有一个问题:我们需要知道我们映射的属性,以便能够返回正确的结果。

场合:

我们需要映射的值作为int存储在数据库中,我们需要根据此值创建代码对象。为了能够创建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 |
+----+------+-------------------+-----------+

正如您所看到的:如果我们想知道代码的描述,我们知道我们需要从代码表中获取什么值,方法是查看列名,以及旅行表中该列中的值。 / p>

c#c​​lass 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”,以便能够从代码表中获取正确的值? (注意:我无法更改数据库设计。)

2 个答案:

答案 0 :(得分: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") {}
}

答案 1 :(得分:0)

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

Here


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

Here

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

Mapping


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

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);
}