NHibernate 异常“对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例”使用 HasMany 保存对象时

发布于 2024-12-29 01:50:34 字数 2695 浏览 6 评论 0原文

我试图保存具有多个 HasMany 关系的对象,但出现异常:“对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例”。

下面是我的简化类、它们相应的映射和我的“应用程序”代码。

“应用程序代码”部分显示了我想要执行的操作:将费用报告和工作时间添加到发票中,然后保存发票。

但是,异常发生在 GetTimeWorked() 中。如果我颠倒顺序(在费用报告之前添加工作时间),则 GetExpenseReports() 中会出现错误。

如果我在添加费用报告后保存发票,然后在添加工作时间后再次保存它,则效果很好。然而,这种保存需要是事务性的:费用报告和工作时间必须一起保存。

我已经阅读了很多有关此异常的内容,但我尝试的任何方法都不起作用。我读到的情况似乎与此略有不同。我猜这是一个映射问题,我尝试了一些替代映射(在 HasMany 方面,使用 Cascade),但我不知所措。

知道这里发生了什么以及我该如何解决它吗?

谢谢!

// 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);

} 

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);

} 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

柠檬色的秋千 2025-01-05 01:50:34

在创建新发票之前获取列表,默认发票 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);

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);
独享拥抱 2025-01-05 01:50:34

设置 session.Flushmode == FlushMode.Commit 会有所帮助。我猜 GetExpenses()GetTimeWorked() 引用 Invoices 会导致刷新以获得正确的数据

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文