0

I have uploaded a gist the has a working sample, the mapping files, and the full debug log that shows the issue - https://gist.github.com/ravensorb/14193136002adbb3ec2fac07c026f921

Here is the actual exception that I am getting:

ERR: NHibernate.PropertyValueException: Error dehydrating property value for NHibernate.ConsoleTest.IOrderLine.Product ---> NHibernate.HibernateException: Unable to resolve property: Id
at NHibernate.Tuple.Entity.EntityMetamodel.GetPropertyIndex(String propertyName)
at NHibernate.Tuple.Entity.AbstractEntityTuplizer.GetPropertyValue(Object entity, String propertyPath)
at NHibernate.Persister.Entity.AbstractEntityPersister.GetPropertyValue(Object obj, String propertyName)
at NHibernate.Type.EntityType.GetReferenceValue(Object value, ISessionImplementor session)
at NHibernate.Type.ManyToOneType.NullSafeSet(DbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session)
at NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, Object[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColumns, Int32 table, DbCommand statement, ISessionImplementor session, Int32 index)

Here is the main part of the code that demonstrates the issue. It seems there may be an issue when working either inheritance with the Id property?

class Program
{
    static void Main(string[] args)
    {
        try
        {
            var c = new Catalog { Name = "Catalog 1" };
            var p = new Product { Name = "Sample Product", SKU = "123456789", MSRP = (decimal)1.0, Catalog = c };
            var ol = new OrderLine { Product = p, Price = p.MSRP, Qty = 1 };
            var o = new Order { CreatedOn = DateTime.Now, Lines = new List<IOrderLine> { ol } };

            if (System.IO.File.Exists("test.db"))
            {
                System.IO.File.Delete("test.db");
            }

            var sessionFactory = NHibernateUtils.CreateSessionFactory(SQLiteConfiguration.Standard.UsingFile("test.db"));

            using (var session = sessionFactory.OpenSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    session.Save(typeof(IOrder).FullName, o);

                    transaction.Commit();
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception: {0}", ex);
            if (ex.InnerException != null) Console.WriteLine("InnerException: {0}", ex.InnerException);
        }

        Console.WriteLine("Press <ENTER> to exit....");
        Console.ReadLine();
    }
}

#region Interfaces

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

public interface ICataglog : IEntity
{
    string Name { get; set; }
}

public interface IProduct : IEntity
{
    string Name { get; set; }
    string SKU { get; set; }
    decimal MSRP { get; set; }
    ICataglog Catalog { get; set; }
}

public interface IOrder : IEntity
{
    DateTime CreatedOn { get; set; }
    IList<IOrderLine> Lines { get; set; }
}

public interface IOrderLine : IEntity
{
    IProduct Product { get; set; }
    int Qty { get; set; }
    decimal Price { get; set; }
}
#endregion Interfaces

#region Class Maps

public class CatalogMap : ClassMap<ICataglog>
{
    public CatalogMap()
    {
        Table("tblCatalog");

        Id(x => x.Id)
            .Column("Id")
            .GeneratedBy.Assigned();

        Map(x => x.Name);
    }
}

public class ProductMap : ClassMap<IProduct>
{
    public ProductMap()
    {
        Table("tblProduct");

        Id(x => x.Id)
            .Column("Id")
            .GeneratedBy.Assigned();

        Map(x => x.Name);
        Map(x => x.SKU);
        Map(x => x.MSRP);

        References(x => x.Catalog)
            .Cascade.SaveUpdate();
    }
}

public class OrderMap : ClassMap<IOrder>
{
    public OrderMap()
    {
        Table("tblOrder");

        Id(x => x.Id)
            .Column("Id")
            .GeneratedBy.Assigned();

        Map(x => x.CreatedOn);

        HasMany(x => x.Lines)
            .Cascade.All()
            .KeyColumns.Add("Id");
    }
}

public class OrderLineMap : ClassMap<IOrderLine>
{
    public OrderLineMap()
    {
        Table("tbOrderLine");

        Id(x => x.Id)
            .Column("Id")
            .GeneratedBy.Assigned();

        Map(x => x.Qty);
        Map(x => x.Price);

        References(x => x.Product)
            .Cascade.SaveUpdate()
            .PropertyRef(x => x.Id)
            .ForeignKey("productId");
    }
}
#endregion Class Maps

public static class NHibernateUtils
{
    public static ISessionFactory CreateSessionFactory(IPersistenceConfigurer persistenceConfigurer)
    {
        return Fluently.Configure()
            .Database(persistenceConfigurer)
            .Mappings(m =>
            {
                m.FluentMappings.Add<CatalogMap>();
                m.FluentMappings.Add<ProductMap>();
                m.FluentMappings.Add<OrderMap>();
                m.FluentMappings.Add<OrderLineMap>();

                m.FluentMappings.ExportTo(System.Environment.CurrentDirectory);
            })
            .ExposeConfiguration(c => new SchemaExport(c).Create(false, true))
            .BuildConfiguration()
            .BuildSessionFactory();
    }
}

if you want to see the mapping files that are generated or want to repro the issue - check out the gist

  • *Map classes, not interfaces. All issues will be gone...* – Radim Köhler Feb 19 '18 at 06:53
  • Unfortunately I need to work with interfaces -- the actual solution (not the example above) uses an IoC/DI design pattern with the concrete objects manged by a different group. My question though -- why should it matter, everything that is needed for writting the data to a db layer is provided in by the interfaces and mapping. – Shawn Anderson Feb 20 '18 at 11:52

1 Answers1

0

I think you use the wrong id generator. Check this and Fluent NHibernate Generated AND Assigned ID Columns.

Assigned means that NHibernate will try to insert the entity with already initialized identifier(not generate it by itself) - kind of SET IDENTITY_INSERT [dbo].[TableName] ON.

Try to use this:

Id(x => x.Id).GeneratedBy.Guid();

//Id(x => x.Id).GeneratedBy.Increment();//for numbers
//Id(x => x.Id).GeneratedBy.Identity();//for numbers

Update:

If you want to use Id(x => x.Id).GeneratedBy.Assigned() then make sure you explicitly assigned identifiers for all entities:

 var c = new Catalog { Name = "Catalog 1", Id = Guid.NewGuid() };
 var p = new Product { Name = "Sample Product", SKU = "123456789", MSRP = (decimal)1.0, Catalog = c, Id = Guid.NewGuid() };
 var ol = new OrderLine { Product = p, Price = p.MSRP, Qty = 1, Id = Guid.NewGuid() };
 var o = new Order { CreatedOn = DateTime.Now, Lines = new List<IOrderLine> { ol }, Id = Guid.NewGuid() };

 session.Save(typeof(ICatalog).FullName, c);
 session.Save(typeof(IProduct).FullName, p);
 session.Save(typeof(IOrderLine).FullName, ol);
 session.Save(typeof(IOrder).FullName, o);

 transaction.Commit();

Update 2:

I found that .KeyColumns.Add("Id") and .PropertyRef(x => x.Id).ForeignKey("productId") are causing your exception(not sure whats wrong with this). Try to comment those lines and you will not have an error.

Roman Koliada
  • 4,286
  • 2
  • 30
  • 59
  • BTW, my answer to your previous question contains `Id(x => x.Id).GeneratedBy.Guid();` and it works well with your mappings. – Roman Koliada Feb 20 '18 at 10:01
  • Thanks :) I tried that and doesn't seem to have any impact -- same issue with the error being around unable to find the property "Id". So my actual app does assign a Guid on object creation so I think the "Assigned" statement should be correct for that scenario right? (I will eventually use other fields to identify the primary key, the id is just for uniqueness) – Shawn Anderson Feb 20 '18 at 12:03
  • @ShawnAnderson You're trying to save 4 items in one go. In order to achieve it you have to specify cascade saving. Or alternatively you can call `session.Save()` for each entity you want to persist(4 in you case) – Roman Koliada Feb 20 '18 at 14:03
  • I'll try to comment out the KeyColumns and ForeignKey calls -- as for the .PropertyRef(x => x.Id), if i remove that then the tables do not get generated correctly for the relationships (and different errors start showing up). I also tried calling "save" on just the 1st entity in the "chain" (catalog in this case) and still got the property not found error. Its really weird. Its almost like there is no mapping for the Id property to a db column as far as nhibernate is concerned. – Shawn Anderson Feb 21 '18 at 12:10
  • No luck - same issue. I am getting the feeling that nhibernate really cannot handle design by interface patterns :-/ – Shawn Anderson Mar 06 '18 at 15:20