I am in need of some assistance with an Entity Framework query. I have the following entities:
public class Invoice
{
public Guid Id { get; set; }
public DateTime CreateDate { get; set; }
[ForeignKey("CreatedById")]
public virtual ApplicationUser CreatedBy { get; set; }
public Guid CreatedById { get; set; }
public bool Approved { get; set; }
public virtual ICollection<InvoiceDetail> Details { get; set; }
}
public class InvoiceDetail
{
public Guid Id { get; set; }
[ForeignKey("InvoiceId")]
public virtual Invoice Invoice { get; set; }
public Guid InvoiceId { get; set; }
public string Item { get; set; }
public string Description { get; set; }
public decimal Quantity { get; set; }
public decimal UnitCost { get; set; }
public decimal Discount { get; set; }
}
I am trying to sum the total of all the items where an invoice is Approved. The problem I'm having is the syntax on getting to the InvoiceDetail level once I filter for the Approved = 1:
var myInvoices = context.Invoices.Where(i => i.CreatedById == userId).Include(i => i.CreatedBy).Include(i => i.Details);
var approvedTotal = myInvoices.Where(i => i.Approved == 1).Select([GET TO DETAILS???]);
// my goal is to get the following sum for each detail for all of the approved invoices:
// ((d.Quantity * d.UnitCost) - d.Discount)
Assuming that you wanted to get a sum total of all details after the given calculation had been performed you could do something like this:
var approvedTotal = invoices.Where(invoice => invoice.Approved)
.Select(invoice => invoice.Details.Sum(detail => ((detail.Quantity * detail.UnitCost) - detail.Discount)));
So this gives you one total per invoice.
Related
I have a case scenario with two tables References and Products alreading containing many entries which can be dynamically related on demand.
public class Reference
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ReferenceId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Product> ManyProducts { get; set; }
public Reference() {}
}
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Reference")]
public Guid ReferenceId { get; set; }
public virtual Reference OneReference { get; set; }
public Product() {}
}
When a user ask to link a reference to a product I simply do :
product.ReferenceId = reference.ReferenceId ;
await context.SaveChangesAsync() ;
The entry in Products table is updated correctly, but when I try to access a reference's related data, it does not retrieve any ?? After eager loading :
var reference = await context.References
.Include(r => r.ManyProducts)
.SingleAsync(r => r.ReferenceId == referenceId) ;
or explicit loading :
var reference = await context.References.FindAsync(referenceId) ;
await context.Entry(reference).Collection(s => s.ManyProducts).LoadAsync() ;
reference.ManyProducts is empty. So I have to do something like this :
var reference = await context.References.FindAsync(referenceId) ;
var products = await context.Products.Where(l => l.ReferenceId == referenceId).ToListAsync() ;
result.ManyProducts = products ;
which works fine, but I would like to understand why ?
I´m using DataAnnotation
Sample
public class spread
{
[Key]
public int spreadid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange origem")]
public virtual exchange exchange { get; set; } // One to one
[ForeignKey("spreadid")]
public virtual ICollection<spreadhelper> spreadhelper { get; set; } // One to many
}
public class spreadhelper
{
[Key]
public int spreadhelperid { get; set; }
[Required]
public int spreadid { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange")] // One to one
public virtual exchange exchange { get; set; }
[Required, Range(0, 200)]
public decimal spreadvalue { get; set; }
}
one to one - sample
public class exchange
{
[Key]
public int exchangeid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required, MaxLength(50)]
public string name { get; set; }
[MaxLength(128)]
public string token { get; set; }
}
One to many sample
I'm using Dapper to fetch and map data. I've the following query and relevant class.
SELECT pinv.* , sp.SupplierId,sp.SupplierName,sp.Contact1,sp.SupplierAddress,ac.AccountId,ac.AccountName,
(Select Count(*) from ReturnInvoices rinv Where rinv.InvoiceId = pinv.PurchaseInvoiceId) as ReturnInvoicesCount
FROM PurchaseInvoices pinv
LEFT JOIN Suppliers sp ON sp.SupplierId = pinv.SupplierId
LEFT JOIN Accounts ac on ac.AccountId = pinv.AccountId
And the class.
public class PurchaseInvoice
{
public int PurchaseInvoiceId { get; set; }
public int PurchaseOrderId { get; set; }
public string PurchaseInvoiceType { get; set; }
public int SupplierId { get; set; }
public int AccountId { get; set; }
public int ReceiptNumber { get; set; }
public int ReturnInvoicesCount { get; set; } // to map ReturnInvoiceCount by SubQuery
public DateTime PromisedDate { get; set; }
public DateTime InvoiceDate { get; set; }
public decimal InvoiceQuantity { get; set; }
public decimal Discount { get; set; }
public decimal Tax { get; set; }
public decimal ShippingCharges { get; set; }
public decimal Surcharge { get; set; }
public string PaymentMode { get; set; }
public decimal SubTotal { get; set; }
public decimal TotalPayment { get; set; }
public decimal AmountPaid { get; set; }
public decimal AmountRemaining { get; set; }
public string Comments { get; set; }
public string Status { get; set; }
public DateTime UpdatedDate { get; set; }
public Supplier Supplier { get; set; }
public ProductReceived ProductReceived { get; set; }
public Account Account { get; set; }
}
I'm not sharing other classes because they are not relevant. Now, if i run above query, I get perfect results as excepted. After fetching results the dapper returns all data just fine but ReturnInvoicesCount is not mapped (or idk something else is problem). This is how I'm using dapper.
using (SqlConnection connection = new SqlConnection(_conString))
{
var query = QueryHelper.GetPurchaseInvoicesQuery(start, end, simpleSearchText, searchTokensList);
// ignore above line, just getting query with parameters etc
var data = await connection.QueryAsync<PurchaseInvoice, Supplier, Account>(query.Query,
query.queryParams, splitOn: "SupplierId,AccountId");
return data;
}
As i said, I get correct result mapped except field ReturnInvoicesCount which is 0. While on server as can be seen ReturnInvoiceCount have a value.
You have splits on SupplierId (Maps to Supplier) and AccountId (Maps to Account). The problem is that ReturnInvoicesCount is at the wrong column position to be correctly mapped. Currently, you tell dapper that ReturnInvoicesCount belongs to Account.
Since ReturnInvoicesCount belongs to PurchaseInvoice you have to move it before SupplierId.
i have the following problem, i need access to items of sales line from salesheader, when i try access by entity works fine by lazy loading, but i try map with Automapper 6
can´t access to Item from sales header
thanks
public class SalesHeader
{
public int DocumentNo { get; set; }
public virtual ICollection<PostedSalesLine> SalesLines { get; set; }
}
public class SalesLine
{
public int LineNo { get; set; }
public int DocumentNo { get; set; }
public int ItemId { get; set; }
public virtual Item Item { get; set; }
public int Quantity { get; set; }
public decimal Amount { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public decimal UnitCost { get; set; }
public decimal UnitPrice { get; set; }
}
var result = unitOfWork.SalesHeader.GetById(documenNo);
Mapper.Initialize(cfg => cfg.CreateMap<SalesHeader, SalesHeaderDTO>()
return Mapper.Map<SalesHeaderDTO>(result);
Done!
Dont use lazy loading, it creates a mess of proxys
public IEnumerable<SalesHeader> GetAllFullDocuments()
{
return SalesContext.SalesHeader.Include(sh => sh.SalesLines.Select(i => i.Item))
.Include(sh => sh.SellToCustomer)
.Include(sh => sh.BillToCustomer)
.ToList();
}
So im trying to create a little website to help with my family's business.
I have two models, Jobs and Days.
public class Job
{
public int JobId { get; set; }
public int LotNum { get; set; }
public string Street { get; set; }
public string Suburb { get; set; }
public double PerHour { get; set; }
public string Description { get; set; }
}
public class Day
{
public int JobId { get; set; }
public int DayId { get; set; }
public DateTime Date { get; set; }
public double Hours { get; set; }
public string Details { get; set; }
public int Invoice { get; set; }
}
I need to make an Invoice, the invoice will be numbered. The days have the invoice number so ill be able to pick the days needed.
It would look something like this.
Date LotNum Street Suburb Hours
1/1/01 1 John Street Hurstville x hours
1/1/01 1 John Street Hurstville x hours
1/1/01 1 John Street Hurstville x hours
1/1/01 1 John Street Hurstville x hours
I am able to get the days with the specific invoice number using this:
vm.Days = _dayRepo.GetAll().Where(d => d.Invoice == id);
By doing so i have the date and hours of that day but now i need to get the job information. Both Day and Job have the JobId so i would be able to link them but i just dont know how.
This is what i have so far:
public IActionResult Invoice(int id)
{
CreateInvoiceViewModel vm = new CreateInvoiceViewModel();
vm.Days = _dayRepo.GetAll().Where(d => d.Invoice == id);
//vm.JobId =
vm.Jobs = _jobRepo.GetAll();
return View(vm);
}
My view looks like this:
#model CreateInvoiceViewModel
<table>
#foreach (var item in Model.)
{
<tr>
<td>#item.Date.Date.ToString("dd/MM/yy")</td>
<td>#item.Hours</td>
</tr>
}
</table>
I dont know what to put in for the foreach.
Thanks in advance!
you just need a join query. Define Your ViewModel like:
public class InvoiceViewModel
{
public DateTime Date { get; set; }
public int LotNum { get; set; }
public string Street { get; set; }
public string Suburb { get; set; }
public double Hours { get; set; }
}
Create a join query and convert it to ViewModel:
public IActionResult Invoice(int id)
{
var query = from d in _dayRepo.GetAll()
join job in _jobRepo.GetAll() on d.JobId equals job.JobId
select new { Date=d.Date, LotNum= job.job , Street =job.Street , Suburb =job.Suburb , Hours =d.Hours };
IEnumerable<InvoiceViewModel> viewModel = query.Select(c => new InvoiceViewModel()
{
Date=query.Date,
LotNum=query.LotNum,
Street=query.Street,
Suburb=query.Suburb,
Hours=query.Hours
});
return View(viewModel);
}
This should result in an IEnumerable with your ids
var ids = _dayRepo.GetAll().Where(d => d.Invoice == id).Select(x => x.JobId);
You can also add .ToList() to the end if you want it as a list.
This is more or less how I would set it up, or at least the beginnings of how.
(I'm using EntityFrameworkCore and yes I understand that you are using repos at the moment)
public class Job
{
public int Id { get; set; }
public int LotNum { get; set; }
public string Street { get; set; }
public string Suburb { get; set; }
public double PerHour { get; set; }
public string Description { get; set; }
// Navigation Helper
public virtual ICollection<InvoiceJobRelationship> Invoices { get; set; }
}
public class Day
{
public int Id { get; set; }
public DateTime Date { get; set; }
public double Hours { get; set; }
public string Details { get; set; }
// Navigation Helper
public virtual ICollection<InvoiceDayRelationship> Invoices { get; set; }
}
public class Invoice
{
public int Id { get; set; }
// Navigation Helpers
public virtual ICollection<InvoiceJobRelationship> Jobs { get; set; }
public virtual ICollection<InvoiceDayRelationship> Days { get; set; }
}
public class InvoiceDayRelationship
{
public int InvoiceId { get; set; }
// Navigation Helper
[ForeignKey("InvoiceId")]
public Invoice Invoice { get; set; }
public int DayId { get; set; }
// Navigation Helper
[ForeignKey("DayId")]
public Day Day { get; set; }
}
public class InvoiceJobRelationship
{
public int InvoiceId { get; set; }
// Navigation Helper
[ForeignKey("InvoiceId")]
public Invoice Invoice { get; set; }
public int JobId { get; set; }
// Navigation Helper
[ForeignKey("JobId")]
public Job Job { get; set; }
}
Then in your context
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<InvoiceDayRelationship>()
.HasKey(id => new { id.DayId, id.InvoiceId });
builder.Entity<InvoiceJobRelationship>()
.HasKey(ij => new { ij.JobId, ij.InvoiceId });
}
public DbSet<Invoice> Invoices { get; set; }
public DbSet<Day> Days { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<InvoiceDayRelationship> InvoiceDays { get; set; }
public DbSet<InvoiceJobRelationship> InvoiceJobs { get; set; }
Then you would be able to call pretty much anything you would like.
(Example Query)
(from x in context.Invoices where x.Id == id select x).Include(inv => inv.Days).Include(inv => inv.Jobs);
I have this piece of code which performs quite poorly, and delays loading of web pages as a result. The objects underlying the query are not overly complicated and I only have around 6,000 rows in the time entry table, and a small number of rows in the included tables.
How can I re-write it to be more efficient? Why is it so slow? Is it the multiple includes, the lambdas, or the ToList()? When I remove the ToList() it is quite fast, but the code following this query expects a list of time entries, so I would like to find a faster alternative that still returns a list.
var timeEntries = db.TimeEntries
.Include(t => t.Assignment)
.Include(t => t.TimeStatus)
.Include(t => t.User)
.Where(t => t.UserID == strCurrentUserId)
.Where(t => t.TimeDate >= wcDate && t.TimeDate <= sundayDate)
.OrderBy(t => t.TimeDate)
.ToList();
A few of the classes are below (but, I'm not exposing my user class):
public class TimeEntry
{
[Key]
public int ID { get; set; }
public int AssignmentID { get; set; }
public virtual Assignment Assignment { get; set; }
public DateTime TimeDate { get; set; }
public decimal TimeUsed { get; set; }
public int TimeStatusID { get; set; }
public TimeStatus TimeStatus { get; set; }
public string UserID { get; set; }
public virtual User User { get; set; }
[DataType(DataType.MultilineText)]
public string Comment { get; set; }
}
public class TimeStatus
{
[Key]
public int ID { get; set; }
public string Status { get; set; }
}
public class Assignment
{
[Key]
public int ID { get; set; }
public int ProjectID { get; set; }
public virtual Project Project { get; set; }
public string UserID { get; set; }
public virtual User User { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int AssignmentStatusID { get; set; }
public AssignmentStatus AssignmentStatus { get; set; }
public decimal ChargeRate { get; set; }
public int MaxDays { get; set; }
public bool Billable { get; set; }
public bool BillableInternal { get; set; }
[Display(Name = "Assignment No.")]
public string AssignmentNumber
{
get
{
return "A" + ID.ToString("D5");
}
set { }
}
}