views:

556

answers:

1

I am new to NHibernate and am running into some issues getting the Automap functionality to work properly. Here are a couple of issues I am having.

The getting started wiki for Fluent NHibernate (http://wiki.fluentnhibernate.org/Getting_started) defines a sample with store, product, and employee classes--as well as the mapping for those classes. I replaced the manual mapping with AutoMapping and used Fluent NHibernate to generate the schema. Every thing generated properly. However, when the application attempted to save sample store, product, and employee objects, I received an error "TransientObjectException was Unhandled: object references an unsaved transient instance - save the transient instance before flushing. Type: FluentExample.Entities.Employee, Entity: FluentExample.Entities.Employee".

The automap looks like:

.Mappings(m=>
  m.AutoMappings.Add(
    AutoMap.AssemblyOf<FluentExample.Entities.Employee>(type => type.Namespace == "FluentExample.Entities")))

The object creation code (straight from the wiki) looks like the following. I should mention that the object creation works fine when using the manual fluent mapping.

// create a couple of Stores each with some Products and Employees
var barginBasin = new Store { Name = "Bargin Basin" };
var superMart = new Store { Name = "SuperMart" };

var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
var fish = new Product { Name = "Fish", Price = 4.49 };
var milk = new Product { Name = "Milk", Price = 0.79 };
var bread = new Product { Name = "Bread", Price = 1.29 };
var cheese = new Product { Name = "Cheese", Price = 2.10 };
var waffles = new Product { Name = "Waffles", Price = 2.41 };

var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
var joan = new Employee { FirstName = "Joan", LastName = "Pope" };

// add products to the stores, there's some crossover in the products in each
// store, because the store-product relationship is many-to-many
AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
AddProductsToStore(superMart, bread, cheese, waffles);

// add employees to the stores, this relationship is a one-to-many, so one
// employee can only work at one store at a time                     
AddEmployeesToStore(barginBasin, daisy, jack, sue);
AddEmployeesToStore(superMart, bill, joan);

// save both stores, this saves everything else via cascading
session.SaveOrUpdate(barginBasin);
session.SaveOrUpdate(superMart);

transaction.Commit();

When attempting to use the AutoMap functionality on one of my own classes, a class is created, but for some reason I get errors when I attempt to actually insert a record. The main error message says "AssertionFailure was unhandled: null value". Here is a sample of my class, the config/ mapping, the error, and the create table script. (Note: The attributes in the class are for use with ASP.NET MVC and have nothing to do with NH.)

namespace Credit.Data.Entities
{
  [Serializable]
  public class EthnicityType
  {
    public EthnicityType()
    {
    }

    [DisplayName("Id")]
    [Required(ErrorMessage = "Id is required.")]
    public virtual Guid Id { get; private set; }

    [DisplayName("Title")]
    [Required(ErrorMessage = "Title is required.")]
    [StringLength(80, ErrorMessage = "Title must be less than 80 characters.")]
    public virtual string Title { get; set; }

    [DisplayName("Description")]
    [StringLength(255, ErrorMessage = "Description must be less than 255 characters.")]
    public virtual string Description { get; set; }

    [DisplayName("Is Active")]
    [Required(ErrorMessage = "Is Active is required.")]
    public virtual bool IsActive { get; set; }
  }
}

Here is the Fluent NHibernate configuration.

private static ISessionFactory CreateSessionFactory()
{
  return Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008
      .ConnectionString(c => c.FromConnectionStringWithKey ("CreditConnectionString"))
      .UseReflectionOptimizer()
      .AdoNetBatchSize(25)
      .DefaultSchema("dbo")
      .Cache(c => c
        .UseQueryCache()
        .ProviderClass<HashtableCacheProvider>())
      .ShowSql())
    .Mappings(m =>                    
      m.AutoMappings.Add(AutoMap.AssemblyOf<Credit.Data.Entities.EthnicityType>(type => type.Namespace == "Credit.Data.Entities")))
    .ExposeConfiguration(BuildSchema)
    .BuildSessionFactory();
}

And the error. Yuk!

NHibernate.AssertionFailure was unhandled
  Message="null identifier"
  Source="NHibernate"
  StackTrace:
       at NHibernate.Engine.EntityKey..ctor(Object identifier, String
rootEntityName, String entityName, IType identifierType, Boolean
batchLoadable, ISessionFactoryImplementor factory, EntityMode
entityMode)
       at NHibernate.Engine.EntityKey..ctor(Object id,
IEntityPersister persister, EntityMode entityMode)
       at
NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate
(Object entity, EntityKey key, IEntityPersister persister, Boolean
useIdentityColumn, Object anything, IEventSource source, Boolean
requiresImmediateIdAccess)
       at
NHibernate.Event.Default.AbstractSaveEventListener.PerformSave(Object
entity, Object id, IEntityPersister persister, Boolean
useIdentityColumn, Object anything, IEventSource source, Boolean
requiresImmediateIdAccess)
       at
NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId
(Object entity, String entityName, Object anything, IEventSource
source, Boolean requiresImmediateIdAccess)
       at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId
(SaveOrUpdateEvent event)
       at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient
(SaveOrUpdateEvent event)
       at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate
(SaveOrUpdateEvent event)
       at
NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate
(SaveOrUpdateEvent event)
       at NHibernate.Impl.SessionImpl.FireSaveOrUpdate
(SaveOrUpdateEvent event)
       at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
       at FluentExample.Program.PopulateRecordTest(ISessionFactory
sessionFactory) in C:\Code\FluentExample\FluentExample\Program.cs:line
52
       at FluentExample.Program.BootstrapNH() in C:\Code\FluentExample
\FluentExample\Program.cs:line 32
       at FluentExample.Program.Main() in C:\Code\FluentExample
\FluentExample\Program.cs:line 24
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String
[] args)
       at
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

And the table schema--just for kicks.

USE [Credit]
GO

/****** Object:  Table [dbo].[EthnicityType]    Script Date:
08/30/2009 04:59:31 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[EthnicityType](
  [ID] [uniqueidentifier] NOT NULL,
  [Title] [nvarchar](80) NOT NULL,
  [Description] [nvarchar](255) NULL,
  [IsActive] [bit] NOT NULL        
  CONSTRAINT [PK_EthnicityType_Title] PRIMARY KEY NONCLUSTERED
  (
    [ID] ASC
  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY
    = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[EthnicityType] ADD  CONSTRAINT
  [DF_EthnicityType_ID]  DEFAULT (newid()) FOR [ID]
GO

ALTER TABLE [dbo].[EthnicityType] ADD  CONSTRAINT
  [DF_EthnicityType_IsActive]  DEFAULT ((1)) FOR [IsActive]
GO

I've tried a number of things to get automapping working in my environment but just have not yet been fully successful. Some variations I have tried include

  • Using a static map and allowing Fluent Nhibernate to recreate my table.
  • Changing the private set on Id to public
  • Creating a primarykeyconvention to try and set the Guid in case that was the issue and adding this to my mapping.

Primary Key Convention:

public class PrimaryKeyConvention : IIdConvention
{
    public void Apply(IIdentityInstance instance)
    {
        instance.GeneratedBy.GuidComb();
        //instance.GeneratedBy.Native();
    }
}

Any advice or feedback is very much appreciated.

A: 

The Fluent NHibernate example entities are in the Examples.FirstProject.Entities namespace, while you're limiting the AutoMap to FluentExample.Entities (in type => type.Namespace == "FluentExample.Entities").

The first error message points to that by stating "references an unsaved transient instance [...] FluentExample.Entities.Employee"

You should update the AutoMap code to point to the correct interface. If you have classes in multiple interfaces, you can modify the criteria to include an or.

James Gregory