NHibernate

NHibernate.Mapping.Attributes如何实现复合主键的映射?

有一个数据表,主键是两列组成的复合主键,根据NHibernate关于复合组件的文档,需要将所有复合主键构成一个主键类,然后使用xml文件映射,这样才能用ISessionFactory.Session.Get(object obj)方法获取实体对象,但是我现在想用NHibernate.Mapping.Attributes库,利用C#的Attribute修饰我的主键类和目标类,应该怎么做?我的类已经写好了,主要是不知道两个类的Attribute应该怎么处理。

使用 NHibernate.Mapping.Attributes 库可以使用特性来映射复合主键类和目标类。

首先,你需要在复合主键类上使用 [CompositeId] 特性,然后在每个复合主键的属性上使用 [KeyProperty] 特性标注。

接下来,在目标类上使用 [Class] 特性,并在目标类中的复合主键类型的属性上使用 [Id] 特性。

这是一个示例:

[CompositeId]
public class CompositeKey
{
    [KeyProperty]
    public int Id1 { get; set; }

    [KeyProperty]
    public int Id2 { get; set; }
}

[Class]
public class TargetClass
{
    [Id]
    public CompositeKey CompositeKey { get; set; }

    // other properties and methods
}

然后你就可以使用 ISession.Get(object id) 方法来获取目标类的实例,其中 id 参数是复合主键类的实例。

using (var session = sessionFactory.OpenSession())
{
    var compositeKey = new CompositeKey { Id1 = 1, Id2 = 2 };
    var target = session.Get<TargetClass>(compositeKey);
}

在使用 NHibernate.Mapping.Attributes 库时,如果在实体类上使用了 [CompositeId] 特性,那么这个实体类必须要继承自 ICompositeUserType 接口,并实现其中的方法。如果你的实体类没有继承自这个接口,会提示 [CompositeId] 不能是 [Class] 的特性。可以尝试将你的实体类改为继承自 ICompositeUserType 接口,然后再次尝试使用 [CompositeId] 特性进行复合主键映射。
在继承自 ICompositeUserType 接口时,还需要注意几点:

在实现 ICompositeUserType.GetPropertyNames() 方法时,需要返回复合主键中所有属性的名称。
在实现 ICompositeUserType.GetPropertyTypes() 方法时,需要返回复合主键中所有属性的类型。
在实现 ICompositeUserType.NullSafeSet() 和 ICompositeUserType.NullSafeGet() 方法时,需要注意将复合主键的值转换为相应的数据库类型。
这些方法的具体实现可以参考下面的示例代码:

public class CompositeKey : ICompositeUserType
{
    public string[] PropertyNames => new[] { "Id1", "Id2" };
 
    public System.Type[] PropertyTypes => new[] { typeof(int), typeof(string) };
 
    public object GetPropertyValue(object component, int property)
    {
        var key = (CompositeKey)component;
 
        return property == 0 ? key.Id1 : key.Id2;
    }
 
    public void SetPropertyValue(object component, int property, object value)
    {
        var key = (CompositeKey)component;
 
        if (property == 0)
        {
            key.Id1 = (int)value;
        }
        else
        {
            key.Id2 = (string)value;
        }
    }
 
    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }
 
        if (x == null || y == null)
        {
            return false;
        }
 
        var xKey = (CompositeKey)x;
        var yKey = (CompositeKey)y;
 
        return xKey.Id1 == yKey.Id1 && xKey.Id2 == yKey.Id2;
    }
 
    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }
 
    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        var id1 = (int)NHibernateUtil.Int32.NullSafeGet(rs, names[0]);
        var id2 = (string)NHibernateUtil.String.NullSafeGet(rs,names[1]);return new CompositeKey { Id1 = id1, Id2 = id2 };
}

public void NullSafeSet(IDbCommand cmd, object value, int index)
{
    var key = (CompositeKey)value;

    NHibernateUtil.Int32.NullSafeSet(cmd, key.Id1, index);
    NHibernateUtil.String.NullSafeSet(cmd, key.Id2, index + 1);
}

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 System.Type ReturnedType => typeof(CompositeKey);

public bool IsMutable => false;}

在使用 [CompositeId] 特性进行复合主键映射时,还需要注意:

  • 在实体类的复合主键属性上使用 [KeyProperty] 特性进行标注。
  • 在实体类的复合主键属性上使用 [KeyType] 特性指定复合主键的类型。

示例代码如下:

[CompositeId]
[KeyType(typeof(CompositeKey))]
public class Entity
{
[KeyProperty]
public int Id1 { get; set; }

[KeyProperty]
public string Id2 { get; set; }

// 其他属性}