I am trying to use FluentNHibernate AutoMappings to create an entity with reference to its parent. I was able to do this with help of ClassMap<>, but now I want to move everything to AutoMappings. I have a table with the following schema:
CREATE TABLE [dbo].[Job]
(
[Id] INT NOT NULL IDENTITY(1,1),
[ParentId] INT NULL,
PRIMARY KEY ([Id]),
CONSTRAINT [FK_Job_Parent] FOREIGN KEY ([ParentId]) REFERENCES [Job]([Id])
)
And the following C# code:
using FluentNHibernate;
using FluentNHibernate.Automapping;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;
using System;
using System.Configuration;
namespace NHibernateTest
{
public class Program
{
private static void Main(string[] args)
{
var sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(ConfigurationManager.ConnectionStrings["Default"].ConnectionString))
.Mappings(m =>
{
m.AutoMappings.Add(
AutoMap.AssemblyOf<Program>(new MappingConfig()).Conventions.Setup(conv =>
{
conv.Add<DefaultReferenceConvention>();
conv.Add<DefaultHasManyConvention>();
conv.Add<SimpleForeignKeyConvention>();
}));
})
.BuildSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (var tran = session.BeginTransaction())
{
var jobs = session
.QueryOver<Job>()
.List<Job>();
tran.Commit();
}
}
}
}
public class Job
{
public virtual int Id { get; set; }
public virtual Job Parent { get; set; }
}
public class MappingConfig : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type == typeof(Job);
}
}
public class SimpleForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
{
return type.Name + "Id";
}
return property.Name + "Id";
}
}
public class DefaultHasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.Column(string.Format("{0}{1}", instance.EntityType.Name, "Id"));
instance.LazyLoad();
}
}
public class DefaultReferenceConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
var col = string.Format("{0}Id", instance.Class.Name);
instance.Column(col);
instance.LazyLoad();
}
}
}
I am getting:
An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
Foreign key (FK7D5D63A6AE42E0BA:Job [JobId, ParentId])) must have same number of columns as the referenced primary key (Job [Id])
Is there a way to do this using auto mappings only?
Your DefaultReferenceConvention
appears to conflict with your SimpleForeignKeyConvention
. One of them uses the property name as the basis for the column name, the other uses the property type. It looks like for some reason Fluent NH is using both of these, and thinks you have a composite key.
Remove DefaultReferenceConvention
and see if that fixes it. It's unnecessary anyway because Fluent NH's default foreign key column naming is already {Class.Name}Id
.
Fluent NH conventions are excellent for painting your mappings with broad brush strokes, but they are not the right tool for changing the column name of a single property. In that case, you should define an IAutoMappingOverride<T>
instead.