I am trying to calculate my customer debt using groupby
I have 2 tables with the structures as you can see here :
public partial class Inovice
{
public Inovice()
{
this.Payments = new HashSet<Payment>();
this.InvoiceDetails = new HashSet<InvoiceDetail>();
}
public PersianCalendar objPersianCalender = new PersianCalendar();
[DisplayName("شناسه")]
public int Id { get; set; }
[DisplayName("شناسه کاربر")]
public int MemberId { get; set; }
public virtual ICollection<InvoiceDetail> InvoiceDetails { get; set; }
}
This table holds the customer invoices ,another tables hold the items that the customers buy.the structure is like this :
public partial class InvoiceDetail
{
[DisplayName("شناسه ")]
public int Id { get; set; }
[DisplayName("مقدار")]
public Nullable<double> Amount { get; set; }
[DisplayName("شناسه فاکتور")]
public int InoviceId { get; set; }
public Nullable<bool> IsReturned { get; set; }
public virtual Inovice Inovice { get; set; }
}
My values:
MemberOrCustomer sumOfAmountvalues
1 12
1 8
2 12
2 11
These two tables has a relations on invoiceid.I need to sum amount(InvoiceDetail) values for each memberId(Invoice) for example :
MemberOrCustomer sumOfAmountvalues
1 20
2 23
I write this code for doing this but it doesn't work :
var result= invoices.GroupBy(i => i.MemberId).Select(group=>new {Amount=group.Sum(Amount)} );
Best regards
Well, you could work with a list of invoicesDetails.
var result = invoicesDetails.GroupBy(id => id.Invoice.MemberId)
.Select(g => new {
MemberOrCustomer = g.Key,
Amount = g.Sum(x => x.Amount)
});
where invoiceDetails might be
invoices.SelectMany(x => x.InvoiceDetails)
so
invoices
.SelectMany(x => x.InvoiceDetails)
.GroupBy(id => id.Invoice.MemberId)
.Select(g => new {
MemberOrCustomer = g.Key,
Amount = g.Sum(x => x.Amount)
});
Related
As an exercise in learning EF, I have the following 4 tables. Person 1toM, with Orders M2M, with Products via OrderProducts (Gender is an Enum):
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public Gender Gender { get; set; }
public IList<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
public IList<OrderProduct> OrderProducts { get; set; }
}
public class OrderProduct
{
public int Id { get; set; }
public int Qty { get; set; }
public Order Order { get; set; }
public int OrderId { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public IList<OrderProduct> OrderProducts { get; set; }
}
I'm trying to generate the following with LINQ extension methods:
A list of products and total [=Sum(OrderProducts.Qty * Product.Price)] spent, grouped by product name then grouped by gender.
The result would look something like this:
Female
Ball $655.60
Bat $1,925.40
Glasses $518.31
Etc...
Male
Ball $1,892.30
Bat $3,947.07
Glasses $1,315.71
Etc...
I'm committing myself to LINQ extension methods and hope that I can also develop some best practice here. I can't work out how to now group by ProductName and aggregate the Qty * Price into a total by product:
var prodsalesbygender = context.Orders
.GroupBy(o => new
{
Gender = o.Person.Gender
})
.Select(g => new
{
ProductName = g.Select(o => o.OrderProducts.Select(op => op.Product.Name)),
Qty = g.Select(o => o.OrderProducts.Select(op => op.Qty)),
Price = g.Select(o => o.OrderProducts.Select(op => op.Product.Price))
}
);
I've tried adding .GroupBy(g => g.ProductName) to the end but get the error "The key selector type for the call to the 'GroupBy' method is not comparable in the underlying store provider.".
I think you are almost there..
Try this one:
var prodsalesbygender = context.Orders
.GroupBy(o => new
{
Gender = o.Person.Gender
})
.Select(g => new
{
Gender = g.Key,
Products = g.Select(o => o.OrderProducts
.GroupBy(op => op.Product)
.Select(op => new
{
ProductName = op.Key.Name,
Qty = op.Sum(op2 => op2.Qty),
Price = op.Select(x => x.Product.Price)
.First(),
})
.Select(x => new
{
ProducName = x.ProductName,
Qty = x.Qty,
Price = x.Price,
TotalPrice = x.Qty * x.Price
}))
});
In short, you just need more projection. In my suggested solution, first you group by the gender. The next step is to project the gender and 'list of product' directly (yes, the difficult part to get around is the OrderProducts). Within the Products we group it by product name, then take the total quantity (Qty) and set the Price - assuming the price for the same product is constant. The next step is to set the TotalPrice, the Qty * Price thing.
Ps. I am fully aware that this query had many deficiencies. Perhaps LINQ expert can give a better help on this.
It will result in a class something like:
{
Gender Gender
IEnumerable<{ ProductName, Qty, Price, TotalPrice }> Products
}
Yes, so much for anonymous type..
Nevertheless, i am baffled by the down votes as the question contains the models in question and the attempt OP have provided.
Finally here is my solution, producing a result exactly as required.
var prodsalesbygender = context.OrderProducts
.GroupBy(op => new
{
Gender = op.Order.Person.Gender
})
.Select(g => new
{
Gender = g.Key.Gender,
Products = g.GroupBy(op => op.Product)
.Select(a => new
{
ProductName = a.Key.Name,
Total = a.Sum(op => op.Qty * op.Product.Price)
})
.OrderBy(a => a.ProductName)
});
foreach (var x in prodsalesbygender)
{
Console.WriteLine(x.Gender);
foreach (var a in x.Products)
Console.WriteLine($"\t{a.ProductName} - {a.Total}");
}
My thanks to #Bagus Tesa
i am using NHibernate 4 with mysql. i have got 2 tables. My tables are cat and answer.
public class cat
{
[Key]
public virtual int id { get; set; }
public virtual string catName { get; set; }
public virtual IList<answer> answers { get; set; }
}
public class answer
{
[Key]
public virtual int id { get; set; }
public virtual int catId { get; set; }
public virtual string detail { get; set; }
public virtual bool stat { get; set; }
[ForeignKey("catId")]
public virtual cat cats { get; set; }
}
i want to select all cat record(with answer list) and with their cild answers count.
my sql query like that;
select count(t2.id) as count, cats.*from cat cats left join answer t2 ON(cats.id=t2.catId and t2.stat=0) GROUP BY(cats.id);
Result like this;
id - catName - count
1 - Book - 5
2 - Pc - 0
3 - English - 22
4 - Arts - 56
i have try also this NH query;
public class myClass {
public virtual int count { get; set; }
public virtual cat cats { get; set; }
}
var u = db.CreateCriteria(typeof(cat), "cats")
.CreateAlias("answer", "t2", NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("t2.stat", false))
.SetProjection(Projections.ProjectionList()
.Add(Projections.Count("t2.id"), "count")
.Add(Projections.Group<cat>(g => g.id)));
var list = u.SetFetchMode("answer", FetchMode.Eager)
.SetResultTransformer(Transformers.AliasToBean<myClass>())
.List<myClass>();
This NHibernate query return also answers count. but cats always return null. How can i do my query for this result ?
Edit 1
i can do it like that
public class myClass {
public virtual int count { get; set; }
public virtual catId count { get; set; }
public virtual cat cats { get; set; }
}
cat cats = null;
answer answers = null;
var u = db.QueryOver<cat>(() => cats)
.JoinQueryOver(x => x.answers, () => answers, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("answers.stat", false))
.SelectList(cv => cv
.SelectCount(() => answers.id)
.SelectGroup(c => c.id))
.List<object[]>()
.Select(ax => new myClass
{
count = (int)ax[0],
catId = (int)ax[1],
cats = (cat)db.QueryOver<cat>().Where(w=>w.id==(int)ax[1]).Fetch(fe => fe.answers).Eager.SingleOrDefault()
})
.ToList();
In your result cats is always null, because the ResultTransformer tries to map the properties by their names.
Please check your NHibernate logfile. You will probably see that your query returns the columns count and id, but myClass has the properties count and cats.
Edit:
New suggestion:
The previous suggestion did not work, because the property id is of type Int32 and cannot be assigned to myClass.cat (which is of type cat).
If you don't need a reference to a cat-object in myClass then you can change it to this:
public class myClass {
public virtual int count { get; set; }
public virtual int catId { get; set; }
}
and have the projection like this:
.Add(Projections.Group<cat>(g => g.id), "catId"));
If you do need a property of type cat in your result class I don't think this can be done with a simple projection but I might be wrong.
Edit 2:
Since you require an object of type cat in your result I suggest you put it together manually after the query, e.g.:
New result class:
public class myClassResult {
public virtual int count { get; set; }
public virtual cat cats { get; set; }
}
Add this after your query logic:
IList<myClassResult> result = new List<myClassResult>();
foreach (var idWithCount in list)
{
result.Add(new myClassResult()
{
cats = catsOnlyList.FirstOrDefault(x => x.id == idWithCount.catId),
count = idWithCount.count
});
}
catsOnlyList refers to a simple list of cats that you need to get beforehand. I know this isn't pretty but I don't think you can group by cat itself in the query.
Old suggestion (does not work because of incompatible types):
Instead of
.Add(Projections.Group<cat>(g => g.id)));
use
.Add(Projections.Group<cat>(g => g.id), "cats"));
i can do that query like that;
public class myClass {
public virtual int count { get; set; }
public virtual İnt catId { get; set; }
public virtual cat cats { get; set; }
}
cat cats = null;
answer answers = null;
var u = db.QueryOver<cat>(() => cats)
.JoinQueryOver(x => x.answers, () => answers, NHibernate.SqlCommand.JoinType.LeftOuterJoin, Restrictions.Eq("answers.stat", false))
.SelectList(cv => cv
.SelectCount(() => answers.id)
.SelectGroup(c => c.id))
.List<object[]>()
.Select(ax => new myClass
{
count = (int)ax[0],
catId = (int)ax[1],
cats = (cat)db.QueryOver<cat>().Where(w=>w.id==(int)ax[1]).Fetch(fe=>fe.answers).Eager.SingleOrDefault()
})
.ToList();
I have a model class which contains a property ProductPrice of type decimal. I am not able to store an IQueryable type to a property decimal. I have even tried to Convert.ToDecimal but it still showing me the error.
Model - Product
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
public string ProductName { get; set; }
public decimal ProductPrice { get; set; }
public int ProductQty { get; set; }
}
Model CartDispay
public class CartDisplay
{
public int ItemId { get; set; }
public String ProductName { get; set; }
public int ProductQty { get; set; }
public decimal ProductPrice { get; set; }
}
Controller
public ActionResult Index()
{
int userId = 3;
var items = _context.Cart.Join(_context.Items, c => c.CartId, i => i.CartId, (c, i) => new { c, i }).Where(c => c.c.UserId == userId).ToList();
foreach(var item in items)
{
CartDisplay display = new CartDisplay();
display.ItemId = item.i.ItemId;
display.ProductName = _context.Product.Where(p => p.ProductId == item.i.ProductId).Select(p => p.ProductName).ToString();
display.ProductPrice = _context.Product.Where(p => p.ProductId == item.i.ProductId).Select(p => p.ProductPrice); ;
display.ProductQty = item.i.ProductQty;
cartView.CartDisplay.Add(display);
}
retu
IQueryable<T> defines a sequence of elements of type T, rather than a single item. It also lets you perform additional querying without bringing the sequence into memory, but that is not important in the context of your question.
Your situation is a lot simpler, though: rather than querying for individual properties, you should query for the whole product by its ID, then take its individual properties, like this:
var prod = _context.Product.SingleOrDefault(p => p.ProductId == item.i.ProductId);
if (prod != null) {
display.ProductName = prod.ProductName
display.ProductPrice = prod.ProductPrice;
display.ProductQty = ...
} else {
// Product with id of item.i.ProductId does not exist
}
I have a model Group:
public class GroupModel
{
[Key]
public int GroupModelId { get; set; }
[Required]
[MaxLength(50)]
[DataType(DataType.Text)]
public string GroupName { get; set; }
[Required]
public virtual ICollection<FocusArea> FocusAreas { get; set; }
...
And a model Focus:
public class FocusArea
{
public int FocusAreaId { get; set; }
public FocusEnum Focus { get; set; }
public List<ApplicationUser> ApplicationUser { get; set; }
public virtual ICollection<GroupModel> GroupModel { get; set; }
public enum FocusEnum
{
Psych,
Medical,
LivingWith
}
Group and Focus has a many-to-many relationship. My Controller is receiving:
public ActionResult GroupSearch(string[] focusSelected) // Values possible are Pysch, Medical and LivingWith
{
List<GroupModel> groups;
...
Problem: I want to select the groups that have all the focus that are inside the focusSelected array.
What I've tried:
groups = groups.Where(t => t.FocusAreas.Where(x => focusSelected.Contains(x.Focus))).ToList()).ToList();
Obviously not working. Does anyone have another idea?
This may help you
var result = groups.Where(g => g.FocusAreas.All(f => focusSelected
.Any(fs => (FocusEnum)Enum.Parse(typeof(FocusEnum), fs, true) == f.Focus)));
Where needs a delegate / expression that returns bool. In your sample - you are putting Where inside Where, where Where returns collection.
Changing inner Where to All should do the trick:
var allSelParsed = focusSelected.Select(s => (FocusEnum)Enum.Parse(typeof(FocusEnum), s)
.ToList();
groups = groups.Where(gr => allSelParsed.All(selected =>
gr.FocusAreas.Any(fc =>
fc.Focus == selected)))
.ToList();
This should give you expected result
var result = groups.Where(g =>
focusSelected.All(fs =>
g.FocusAreas.Any(fa => fa.ToString() == fs)));
I have these two classes:
public class Order
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOf { get; set; }
public int[] Product { get; set; }
public int[] Quantity { get; set; }
public int[] Storage { get; set; }
public override bool Equals(Order obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// Return true if the fields match:
return (ID == obj.ID);
}
}
public class RawOrderData
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOfID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
}
Every order in the system is in the form as class Order, the array is used when there are more than one product in the order.
RawOrderData is created from a JSON string where every product in the order have its own object. I want to create a List<Order> where every order gets its own object in the list so there not are several orders with same order id when order contains more than one product.
// raw data is here the JSON string
rawdatalist = serializer.Deserialize<List<RawOrderData>> (rawdata);
// Convert raw objects to List<Order>, list of orders
List<Order> orders = new List<Order> ();
orders = ConvertRawOrderToList (rawdatalist);
private List<Order> ConvertRawOrderToList(List<RawOrderData> datalist)
{
List<Order> orders = new List<Order> ();
foreach (RawOrderData dataobj in datalist)
{
// Check if order exists in list
if (orders.Contains(new Order () {ID = dataobj.ID}))
{
// Order exists, add more products
// CODE HERE?
} else {
// order not existing, add new order to list
short storage = GetStorageArea(dataobj.ProductID);
orders.Add (new Order () {ID = dataobj.ID, Output = dataobj.Output, Wharf = dataobj.Wharf, PartOf = dataobj.PartOfID, Product = dataobj.ProductID, Quantity = dataobj.Quantity});
}
}
return orders;
}
Do I think correct with the ConvertRawOrderToList method? The problem is I don't know what to write in // CODE HERE?. When there is array inside the list-object I'm confused.
I'm also wondering how to access all values in the List<Order> orders.
The information to Storage[] is created from another method that have product ID as input.
It sounds like you have a "flattened" collection of objects that you want to group into Orders. If that's the case, a basic Linq projection would be simplest:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
})
.ToList();
Then you don't need to worry about overriding Equals (at least not for this purpose).
Where would I add the method for adding Storage also?
Since your GetStorageArea function takes a single ProductID you need to pass the product IDs to that function:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
Storage = g.Select(o => GetStorageArea(o.Product)).ToArray()
})
.ToList();