3

Using ASP.NET/C#/fluent Hibernate architecture, I am trying to use a GridView and ObjectDataSource to manage my business objects. I have a main class Project with a one-to-many relationship with OIC - simplified mappings and classes below.

// OIC class definition
public partial class OIC
{
    public int pkOICID { get; set; }
    public string Guarantor { get; set; }
    // ... other fields
    public Project Project { get; set; }
}

// OIC mapping
public class OICMap : ClassMap<OIC>
{
    public OICMap()
    {
        Table(@"OIC");
        LazyLoad();
        Id(x => x.pkOICID)            // primary key for OIC
          .Column("pkOICID")
          .Not.Nullable()
          .GeneratedBy.Identity();
        Map(x => x.Guarantor)
          .Column("Guarantor")
          .Not.Nullable();
        // ...more fields not listed
        References(x => x.Project)    // foreign key to Project
          .Class<Project>()
          .Access.Property()
          .Cascade.None()
          .LazyLoad()
          .Columns("fkProjectID");
    }
}

// Project class definition
public partial class Project
{
    public int pkProjectID { get; set; }
    // ...
    public Iesi.Collections.ISet OICs { get; set; }
}

// Project mapping
public class ProjectMap : ClassMap<Project>
{
    public ProjectMap()
    {
        Table(@"Project");
        LazyLoad();
        Id(x => x.pkProjectID)
          .Column("pkProjectID")
          .Not.Nullable()
          .GeneratedBy.Identity();
        // ...
        HasMany<OIC>(x => x.OICs)
          .Access.Property()
          .AsSet()
          .Cascade.AllDeleteOrphan()
          .LazyLoad()
          .Inverse()
          .Not.Generic()
          .KeyColumns.Add("fkProjectID", mapping => mapping.Name("fkProjectID")
                                                               .SqlType("int")
                                                               .Not.Nullable());
    }
}

When doing unit testing on my repositories, all of the CRUD operations for the OIC class worked perfectly, so I know that the mappings work. However, when I use a GridView and ObjectDataSource to try and delete an instance of OIC, I get a "not-null property references a null or transient value CLS.BusinessLayer.Model.OIC.Guarantor" exception when I try to commit the transaction.

Setting a breakpoint and inspecting the object in the repository's Remove function (code below) reveals that only the primary key for the OIC object is being populated - the rest of the fields are null.

public virtual void Remove(T entity)
{
    if (entity == null)
        throw new ArgumentNullException("entity");
    _session = NHibernateSessionProvider.GetSession();
    if (!_session.Transaction.IsActive)
    {
        try
        {
            using (ITransaction transaction = _session.BeginTransaction())
            {
                _session.Delete(entity);
                transaction.Commit();      // exception caused here
            }
        }
        catch
        {
        }
    }
    else
        _session.Delete(entity);
}

If I remove the .Not.Nullable() on the Guarantor property in OIC, the delete works fine (I only tried this out of curiosity). Also, if I include the Guarantor field in the list of DataKeyNames for my GridView, the field is correctly populated when I inspect the object in the Remove() function above, and the transaction commits successfully. Currently, the GridView only contains pkOICID - the primary key, in DataKeyNames.

So there are a couple things I am stuck on:

  1. If I am only removing an object and have the primary key, why does nHibernate care if other fields are null?
  2. More importantly, I figure I must be missing something in the GridView or ObjectDataSource that is causing the object not to be fully populated before being deleted - is this something that needs to be done in ObjectDataSource_Deleting() or elsewhere?

Please let me know if more code or explanation is needed - thank you.

Brett
  • 1,267
  • 1
  • 13
  • 21
  • `entity` is of type `OIC`? Does using `_session.Delete(entity.Id)` work? – Daniel Hilgarth Feb 14 '13 at 17:19
  • @DanielHilgarth - I'm using a generic repository to be used for all basic CRUD operations - so in this case yes, `entity` is of type `OIC`. I made a quick OIC-specific repository, but that gives me a "No persister for: System.Int32" exception on the `_session.Delete(entity.pkOIDID)` call. – Brett Feb 14 '13 at 17:49
  • a well detailed answer: http://stackoverflow.com/a/19502535/1348121 – Hashem Aboonajmi Oct 22 '13 at 14:33

1 Answers1

1

this should work to delete when only the id is known/populated

session.Delete(Session.Get(entity.GetType(), entity.Id);
Firo
  • 30,626
  • 4
  • 55
  • 94
  • Thanks, @Firo. I still wish I knew why nHibernate checked for nulls on delete (might be a bug related to: https://hibernate.onjira.com/browse/HHH-2792), but I modified my classes and used an interface to effectively get the ID from each business object. – Brett Feb 22 '13 at 22:34
  • My guess is that it was easier to implement in the current architecture and it handles some special cases like pending updates, one-to-many which have to be updated, optimistic concurrency with dirty properties, audit triggers or listeners – Firo Feb 25 '13 at 09:22
  • I have implemented like this. but it says entity has not a property named Id: public void Delete(T entity) { session.Delete(session.Get(entity.GetType(),entity.Id)); } – Hashem Aboonajmi Oct 21 '13 at 17:30
  • does your entity have an Id property? Is it spelled differently? Does the Typeargument T allow access to the Id property? – Firo Oct 22 '13 at 05:41