views:

118

answers:

2

I have a class, Document and several sub-classes (Invoice, PurchaseOrder, etc). I've added a discriminator to Document like so:

public class DocumentMapOverride : IAutoMappingOverride<Document>
{
    public void Override(AutoMapping<Document> mapping)
    {
        mapping.DiscriminateSubClassesOnColumn("DocumentType");
    }
}

My understanding is, if I create an Invoice, it will insert the type name into the DocumentType column. However, when I try to insert the Invoice, I get the following exception.

NHibernate.Exceptions.GenericADOException : could not insert: [MyNamespace.Invoice#101][SQL: INSERT INTO "Document" (Version, DocumentNumber, DocumentDate, DbDate, Sender_id, Receiver_id, SenderAlias_id, ReceiverAlias_id, Process_id, Id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]
  ----> System.Data.SQLite.SQLiteException : Abort due to constraint violation
Document.DocumentType may not be NULL

Any suggestions?

  • FluentNHibernate 1.0
  • SQLite
  • C# / .Net4.0
+1  A: 

This may be completely irrelevant, but when I was trying a similar mapping to this, I found that Nhibernate was trying to insert "ReallyVeryIncrediblyLongNameSpace.Invoice" into the discriminator column, rather than just the type name. Since my column didn't have the capacity for all those characters, it failed.

I can see that your error message doesn't appear to match this, but sometimes errors are misleading so I thought it was worth a shot.

Jonny Cundall
+1  A: 

I wasn't able to reproduce the problem. I've downloaded the latest version of FluentNHibernate 1.1 from here and the following program ran fine:

using System;
using System.Reflection;
using FluentNHibernate;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Conventions;
using FluentNHibernate.Conventions.Instances;
using NHibernate;
using NHibernate.Tool.hbm2ddl;

public interface IEntity
{
    int Id { get; set; }
}

public abstract class MyBaseClass : IEntity
{
    public virtual int Id { get; set; }

    public class MyBaseClassMap : IAutoMappingOverride<MyBaseClass>
    {
        public void Override(AutoMapping<MyBaseClass> mapping)
        {
            mapping.DiscriminateSubClassesOnColumn("ChildClassType", "MyBaseClassMap");
        }
    }
}

public class MyFirstChildClass : MyBaseClass
{
    public virtual string Child1 { get; set; }
}

public class MySecondChildClass : MyBaseClass
{
    public virtual string Child2 { get; set; }
}

public class PrimaryKeyConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        string table = string.Format("{0}_HiLo", instance.EntityType.Name);
        instance.GeneratedBy.HiLo(table, "next_hi", "100");
    }
}

public class MyMappingConfig : DefaultAutomappingConfiguration
{
    public override bool ShouldMap(Type type)
    {
        if (type.GetInterface(typeof(IEntity).FullName) != null)
            return true;

        return false;
    }

    public override bool AbstractClassIsLayerSupertype(Type type)
    {
        if (type == typeof(IEntity))
            return true;
        return false;
    }

    public override bool IsId(Member member)
    {
        return member.Name == "Id";
    }

    public override bool IsDiscriminated(Type type)
    {
        if (type.IsAssignableFrom(typeof(MyBaseClass)) || type.IsSubclassOf(typeof(MyBaseClass)))
            return true;

        return false;
    }
}


public class Program
{
    private static ISession InitializeNHibertnat(Assembly mapAssembly)
    {
        var automappingConfiguration = new MyMappingConfig();

        var fluentConfiguration =
            Fluently.Configure().Database(SQLiteConfiguration.Standard.InMemory());

        fluentConfiguration = fluentConfiguration
            .Mappings(m => m.AutoMappings
                               .Add(AutoMap.Assembly(mapAssembly, automappingConfiguration)
                                        .Conventions.Add<PrimaryKeyConvention>()
                                        .UseOverridesFromAssembly(mapAssembly)))
            .Mappings(m => m.FluentMappings
                               .AddFromAssembly(mapAssembly))
            .Mappings(m => m.HbmMappings
                               .AddFromAssembly(mapAssembly))
            .ExposeConfiguration(cfg => cfg.SetProperty("generate_statistics", "true"))
            .ExposeConfiguration(cfg => cfg.SetProperty("show_sql", "true"))
            .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "1"));


        var configuration = fluentConfiguration.BuildConfiguration();
        var sessionFactory = configuration.BuildSessionFactory();
        var session = sessionFactory.OpenSession();
        new SchemaExport(configuration).Execute(false, true, false, session.Connection, null);

        return session;
    }

    static void Main()
    {
        var mfcc = new MyFirstChildClass();
        mfcc.Id = 1;
        mfcc.Child1 = "Child One";

        var mscc = new MySecondChildClass();
        mscc.Id = 2;
        mscc.Child2 = "Child Two";

        var Session = InitializeNHibertnat(Assembly.GetExecutingAssembly());
        using (var tx = Session.BeginTransaction())
        {
            Session.Save(mfcc);
            Session.Save(mscc);
            tx.Commit();
        }
    }
}

And here's the SQL queries executed:

NHibernate: select next_hi from MyBaseClass_HiLo
NHibernate: update MyBaseClass_HiLo set next_hi = @p0 where next_hi = @p1;@p0 = 2, @p1 = 1
NHibernate: INSERT INTO "MyBaseClass" (Child1, ChildClassType, Id) VALUES (@p0, 'MyFirstChildClass', @p1);@p0 = 'Child One', @p1 = 101
NHibernate: INSERT INTO "MyBaseClass" (Child2, ChildClassType, Id) VALUES (@p0, 'MySecondChildClass', @p1);@p0 = 'Child Two', @p1 = 102

In my test I also used System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139 and targeted .NET 4.0 for this console application. Ran it on Windows 7 x64.

Darin Dimitrov
Thanks - I'll try upgrading to Fluent 1.1 today or tomorrow.
Matt Grande