避免在双链接场景中指定键列

时间:2023-01-09 04:19:22

Assume I have the following two classes:

假设我有以下两个类:

public class User : Entity
{
    public virtual IList<Item> Items { get; set; }
}

public class Item : Entity
{
    public virtual User Owner { get; set; }
}

I created two mapping classes:

我创建了两个映射类:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Id(x => x.Id);
        HasMany(x => x.Items);
    }
}

public class ItemMap : ClassMap<Item>
{
    public ItemMap()
    {
        Id(x => x.Id);
        References(x => x.Owner);
    }
}

This will result in a table Item that has a column UserId and a column OwnerId. When I use KeyColumn("OwnerId") on the HasMany mapping, it works with only the OwnerId column, but I would like to avoid that. Is there a way to tell NHibernate, to use the column created by the mapping in ItemMap?

这将导致具有列UserId和列OwnerId的表项。当我在HasMany映射上使用KeyColumn(“OwnerId”)时,它只对OwnerId列起作用,但我希望避免这种情况。有办法告诉NHibernate,使用ItemMap中映射创建的列吗?

Why I want to avoid specifying the column explicitly:
The column name OwnerId is automatically being generated based on the name of the property and some rules. If I change either the rules or the property name, I need to remember to change that KeyColumn, too. So, basically, it is not refactoring save.

为什么要避免显式地指定列:根据属性名和一些规则自动生成列名OwnerId。如果我更改了规则或属性名,我还需要记住更改KeyColumn。所以,基本上,它不是重构保存。

1 个解决方案

#1


2  

Updated: fixed bug

更新:固定错误

if you apply the rules in the maps then

如果你在地图上应用这些规则

public static void KeyColumnFromReference<TChild>(
    this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map, Expression<Func<TChild, object>> referenceprop)
{
    string propertyname = GetPropertyName(referenceprop);
    var column = ((IMappingProvider)map).GetClassMapping()
        .References.First(m => m.Name == propertyname)
        .Columns.First().Name;

    collectionmap.KeyColumn(column);
}

public static void KeyColumnFromReference<T, TChild>(
    this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map)
{
    var column = ((IMappingProvider)map).GetClassMapping()
        .References.First(m => m.Type == typeof(TChild))
        .Columns.First().Name;

    collectionmap.KeyColumn(column);
}

public UserMap()
{
    HasMany(x => x.Items)
        .KeyColumnFromReference<User, Item>(new ItemMap());

    // or

    HasMany(x => x.Items)
        .KeyColumnFromReference(new ItemMap(), u => u.Owner);
}

if you apply the rules as conventions then you need to implement IHasManyConvention and apply the same rules on the EntityType and propertyname (which you have to get through reflection from the ChildType)

如果您将规则应用为惯例,那么您需要实现IHasManyConvention并对EntityType和propertyname应用相同的规则(您必须通过子类型的反射获得这些规则)

Update:

更新:

class ForeignKeyConvention : IHasManyConvention
{
    public void Apply(IOneToManyCollectionInstance instance)
    {
        // to force the compiler to take the Name property and not the Name method
        string propertyName = ((ICollectionInspector)instance).Name;

        // should be equal to the convention for the reference key column
        instance.Key.Column(propertyName + instance.EntityType.Name + "id");
    }
}

#1


2  

Updated: fixed bug

更新:固定错误

if you apply the rules in the maps then

如果你在地图上应用这些规则

public static void KeyColumnFromReference<TChild>(
    this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map, Expression<Func<TChild, object>> referenceprop)
{
    string propertyname = GetPropertyName(referenceprop);
    var column = ((IMappingProvider)map).GetClassMapping()
        .References.First(m => m.Name == propertyname)
        .Columns.First().Name;

    collectionmap.KeyColumn(column);
}

public static void KeyColumnFromReference<T, TChild>(
    this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map)
{
    var column = ((IMappingProvider)map).GetClassMapping()
        .References.First(m => m.Type == typeof(TChild))
        .Columns.First().Name;

    collectionmap.KeyColumn(column);
}

public UserMap()
{
    HasMany(x => x.Items)
        .KeyColumnFromReference<User, Item>(new ItemMap());

    // or

    HasMany(x => x.Items)
        .KeyColumnFromReference(new ItemMap(), u => u.Owner);
}

if you apply the rules as conventions then you need to implement IHasManyConvention and apply the same rules on the EntityType and propertyname (which you have to get through reflection from the ChildType)

如果您将规则应用为惯例,那么您需要实现IHasManyConvention并对EntityType和propertyname应用相同的规则(您必须通过子类型的反射获得这些规则)

Update:

更新:

class ForeignKeyConvention : IHasManyConvention
{
    public void Apply(IOneToManyCollectionInstance instance)
    {
        // to force the compiler to take the Name property and not the Name method
        string propertyName = ((ICollectionInspector)instance).Name;

        // should be equal to the convention for the reference key column
        instance.Key.Column(propertyName + instance.EntityType.Name + "id");
    }
}