我可以制作包含父键名称的Fluent NHibernate外键约定吗?

时间:2010-02-18 20:42:44

标签: fluent-nhibernate naming-conventions

我有一个数据库模式,其中外键名称的约定是:

ForeignTable.Name + ForeignTable.PrimaryKeyName

因此,对于引用具有名为Child的主键列的Parent表的Key表,外键将类似于ParentKey

有没有办法在我的Fluent NHibernate映射中创建这个约定?

目前我正在使用ForeignKeyConvention这样的实现:

public class ForeignKeyNamingConvention : ForeignKeyConvention
{
    protected override string GetKeyName(PropertyInfo property, Type type)
    {
        if (property == null)
        {
            // Relationship is many-to-many, one-to-many or join.
            if (type == null)
                throw new ArgumentNullException("type");

            return type.Name + "ID";
        }

        // Relationship is many-to-one.
        return property.Name + "ID";
    }
}

对于所有以“ID”作为主键的类型,这完全符合我的要求。我想要做的是将常量“ID”替换为所引用类型的主键名称。

如果使用Fluent NHibernate目前无法做到这一点,我很乐意接受这个答案。

3 个答案:

答案 0 :(得分:7)

查看conventions,特别是实现自定义外键约定。


更新:

这是一个例子。假设以下域:

public class Parent
{
    public virtual int Id { get; set; }
}

public class Child
{
    public virtual string Id { get; set; }
    public virtual Parent Parent { get; set; }
}

需要映射到此架构:

create table Child(
    Id integer primary key, 
    ParentId integer
)

create table Parent(
    Id integer primary key
)

你可以使用这个约定:

public class CustomForeignKeyConvention : IReferenceConvention
{
    public void Apply(IManyToOneInstance instance)
    {
        instance.Column(instance.Class.Name + "Id");
    }
}

并创建会话工厂:

var sf = Fluently
    .Configure()
    .Database(
        SQLiteConfiguration.Standard.UsingFile("data.db3").ShowSql()
    )
    .Mappings(
        m => m.AutoMappings.Add(AutoMap
            .AssemblyOf<Parent>()
            .Where(t => t.Namespace == "Entities")
            .Conventions.Add<CustomForeignKeyConvention>()
        )
    )
    .BuildSessionFactory();

答案 1 :(得分:3)

如果您可以获得某个课程的Mapping<T>,则可以获取其ID列的名称。

public class MyForeignKeyConvention: ForeignKeyConvention
{
    public static IList<IMappingProvider> Mappings = new List<IMappingProvider>();

    protected override string GetKeyName( System.Reflection.PropertyInfo property, Type type )
    {
        var pk = "Id";

        var model = new PersistenceModel();
        foreach( var map in Mappings ) {
            model.Add( map );
        }

        try {
            var mymodel = (IdMapping) model.BuildMappings()
                .First( x => x.Classes.FirstOrDefault( c => c.Type == type ) != null )
                .Classes.First().Id;

            Func<IdMapping, string> getname = x => x.Columns.First().Name;
            pk = getname( mymodel );
        } catch {
        }

        if (property == null) {
            return type.Name + pk;
        }
        return type.Name + property.Name;
    }
}

我们可以通过一些管道来获取Mapping对象。

ClassMap<T>的构造函数可以将this传递到我们的Mappers集合中。

对于AutoMapping<T>,我们可以按如下方式使用覆盖。

.Mappings( m => m.AutoMappings.Add( AutoMap.AssemblyOf<FOO>()
    .Override<User>( u => {
        u.Id( x => x.Id ).Column( "UID" );
        MyForeignKeyConvention.Mappings.Add( u );
    }
)

答案 2 :(得分:1)

对于系统范围的惯例,我相信这最能达到目的。 (我不确定是否要包括整篇文章或仅包含一部分,因为我已经回答了here

这是解决方案,链接到当前的Fluent NHibernate&amp;自动化文档。

问题(一个简单示例):

假设你有一个简单的例子(来自流利的维基)和一个实体,它是一个列表中的值对象:

public class Product
{
  public virtual int Id { get; set; }
  //..
  public virtual Shelf { get; set; }
}

public class Shelf
{
  public virtual int Id { get;  set; }
  public virtual IList<Product> Products { get; set; }

  public Shelf()
  {
    Products = new List<Product>();
  }
}

使用例如表格

Shelf 
id int identity

Product 
id int identity 
shelfid int

用于货架的外键 - &gt; Shelf.Id


你会得到错误: 无效的列名... shelf_id


解决方案:

添加约定,可以是系统范围的,也可以是更受限制的。

ForeignKey.EndsWith("Id")

代码示例:

var cfg = new StoreConfiguration();
var sessionFactory = Fluently.Configure()
  .Database(/* database config */)
  .Mappings(m =>
    m.AutoMappings.Add(
      AutoMap.AssemblyOf<Product>(cfg)
          .Conventions.Setup(c =>
              {
                  c.Add(ForeignKey.EndsWith("Id"));
              }
    )
  .BuildSessionFactory();

现在,它会将ShelfId列自动化为Shelf中的Product属性。


更多信息

Wiki for Automapping

Table.Is(x => x.EntityType.Name + "Table")
PrimaryKey.Name.Is(x => "ID")
AutoImport.Never()
DefaultAccess.Field()
DefaultCascade.All()
DefaultLazy.Always()
DynamicInsert.AlwaysTrue()
DynamicUpdate.AlwaysTrue()
OptimisticLock.Is(x => x.Dirty())
Cache.Is(x => x.AsReadOnly())
ForeignKey.EndsWith("ID")

See more about Fluent NHibernate automapping conventions