我有一个数据库模式,其中外键名称的约定是:
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目前无法做到这一点,我很乐意接受这个答案。
答案 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
属性。
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")