I am using EF 6.1 with code first, and trying to define a class of Invoice, which can consist of a number of Timesheet instances.
What I would like it to do is INSERT a new Invoice row into the database (which it does), and update the Timesheet table so that the foreign key for InvoiceId changes from NULL to the Id of the newly created invoice. Unfortunately, what is happening instead is that new rows are inserted into the timesheet table, and these are associated with the newly generated invoice.
My classes are:
public class TimeSheet
{
public int Id { get; set; }
public DateTime WeekStart { get; set; }
public List<TimeSheetLine> TimeSheetLines { get; set; }
public int? InvoiceId { get; set; }
public virtual Invoice Invoice { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public int? AgencyId { get; set; }
public int? ClientId { get; set; }
public int Rate { get; set; }
public Client Client { get; set; }
public Agency Agency { get; set; }
public List<TimeSheet> Timesheets { get; set; }
}
And a mapping class:
public class TimeSheetMap : EntityTypeConfiguration<TimeSheet>
{
public TimeSheetMap()
{
this.HasKey(x => x.Id);
this.ToTable("TimeSheet");
this.HasOptional(x => x.Invoice)
.WithMany(x => x.Timesheets)
.HasForeignKey(x => x.InvoiceId);
}
}
edit Code to build the Invoice Object:
public bool GenerateInvoice(InvoiceVM invoice)
{
//create an Invoice record from the InvoiceVM
Invoice inv = new Invoice();
List<TimeSheet> sheets = new List<TimeSheet>();
foreach (var item in invoice.TimesheetList)
{
TimeSheet ts = manager.GetTimesheetById(item.Id);
sheets.Add(ts);
}
inv.Timesheets = sheets;
//save the invoice
manager.GenerateInvoice(inv);
return true;
}
edit 2 manager class
public bool GenerateInvoice(Invoice invoice)
{
bool ret = false;
using (DataContext dc = new DataContext())
{
dc.Invoices.Add(invoice);
dc.SaveChanges();
ret = true;
}
return ret;
}
Any obvious changes I can make to get this to work?
It appears as though you are using one context to pull your invoices from the database and a separate context to save the invoice. The
DataContext
inside theGenerateInvoice
function doesn't know that theTimeSheets
are already in the database, because they were retrieved by a different context, inmanager.GetTimesheetById(item.Id);
.There are a few ways you could handle this process, here are two options:
Perform your lookups and attachment inside
manager.GenerateInvoice()
, instead of inside the ViewModel. This would be the cleanest approach, but would require the manager to deal with the TimeSheets. i.e.:Perform two separate database calls, one to insert an
Invoice
, returning the invoiceId
, and a second to update the timesheets.You will likely need to refactor either of these solutions to fit your full domain model, but hopefully these approaches can push you in the right direction.