NHibernate Exception “object references an unsaved transient instance - save the transient instance before flushing” when saving object with HasMany

StackOverflow https://stackoverflow.com/questions/9012937

Domanda

I'm trying to save an object with multiple HasMany relationships and I'm getting the exception: "object references an unsaved transient instance - save the transient instance before flushing".

Below are my simplified classes, their corresponding mappings and my "application" code.

The "Application Code" section shows what I want to do: add expense reports and time worked to an invoice, and then save the invoice.

However, the exception occurs in GetTimeWorked(). If I reverse the order (add time worked before expense reports), then the error occurs in GetExpenseReports().

If I save the invoice after I add expense reports, then save it again after I add the time worked, it works fine. However, this save needs to be transactional: expense reports and time worked must be saved together.

I have read a lot about this exception, but nothing I try works. The situations I have read about seem to be slightly different than this. I'm guessing this is a mapping issue, and I've tried some alternative mapping (on the HasMany side, with Cascade) but I'm at a loss.

Any idea what's going on here and how I can resolve it?

Thanks!

// Classes
public class TimeWorked {
    public virtual long Id { get; private set; }
    public virtual float Hours { get; set; }
    public virtual Invoice Invoice { get; set; }
}

public class ExpenseReport {
    public virtual long Id { get; set; }
    public virtual IList<Expense> Expenses { get; set; }                
    public virtual Invoice Invoice { get; set; }
}

public class Invoice {
    public virtual long Id { get; set; }
    public virtual IList<ExpenseReport> ExpenseReports { get; set; }
    public virtual IList<TimeWorked> BilledTime { get; set; }

    public virtual void AddExpenseReport(List<ExpenseReport> expenseReports)
    {
        foreach (ExpenseReport er in expenseReports)
        {
            ExpenseReports.Add(er);
            er.Invoice = this;
        }
    }

    public virtual void AddTimeWorked(List<TimeWorked> timeWorked)
    {
        foreach (TimeWorked tw in timeWorked)
        {
            BilledTime.Add(tw);
            tw.Invoice = this;
         }
    }       
}

// Mapping
public class TimeWorkedMapping : ClassMap<TimeWorked>
{
    public TimeWorkedMapping()
    {
        Id(x => x.Id);
        References(x => x.Invoice);
    }
}

public class ExpenseReportMapping : ClassMap<ExpenseReport>
{
    public ExpenseReportMapping()
    {
        // Primary Key
        Id(x => x.Id);
        HasMany(x => x.Expenses).Cascade.AllDeleteOrphan();
            References(x => x.Invoice);
    }
}

public class InvoiceMapping : ClassMap<Invoice>
{
    public InvoiceMapping()
    {
        Id(x => x.Id);      
        HasMany(x => x.ExpenseReports).Inverse();
        HasMany(x => x.BilledTime).Inverse();
    }
}


// Application Code

public class MyPage
{
    // Do stuff...
    Invoice invoice = new Invoice();

    // Add the expense reports                       
    List<ExpenseReport> erList = GetExpenseReports();
    invoice.AddExpenseReport(erList);

    // Add billable time
    List<TimeWorked> twList = GetTimeWorked();     <<== Exception occurs in here
    invoice.AddTimeWorked(twList);

    // Save invoice
    Save(invoice);

} 
È stato utile?

Soluzione

Fetch the lists before creating the new invoice, there is probably a problem with the default invoice ID.

List<TimeWorked> twList = GetTimeWorked();
List<ExpenseReport> erList = GetExpenseReports();
Invoice invoice = new Invoice();

// Add the expense reports                       
invoice.AddExpenseReport(erList);

// Add billable time  
invoice.AddTimeWorked(twList);

// Save invoice
Save(invoice);

Altri suggerimenti

setting session.Flushmode == FlushMode.Commit would help. i guess GetExpenses() and GetTimeWorked() reference Invoices which results in a flush to get the right data

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top