Does not display data in linq C# - c#

I have Linq which counts the goods, the problem is that the names that I pass, they do not work
ProductName, CompanyName, CustomerName,
Maybe there is a error in Linq?
It produces many anonymous methods that have these fields, but after ToList() everything does not work
public async Task<IEnumerable<SalesReportItem>> GetReportData(DateTime dateStart, DateTime dateEnd)
{
dateStart = new DateTime(2000, 1, 1);
var context = await _contextFactory.CreateDbContextAsync();
var queryable = context.SalesTransactionRecords.Join(
context.Products,
salesTransactionRecords => salesTransactionRecords.ProductId,
products => products.Id,
(salesTransactionRecords, products) =>
new
{
salesTransactionRecords,
products
})
.Join(context.Companies,
combinedEntry => combinedEntry.salesTransactionRecords.CompanyId,
company => company.Id,
(combinedEntry, company) => new
{
combinedEntry,
company
})
.Join(context.VendorCustomers,
combinedEntryAgain => combinedEntryAgain.combinedEntry.salesTransactionRecords.CustomerId,
vendorCustomer => vendorCustomer.Id,
(combinedEntryAgain, vendorCustomer) => new
{
CompanyName = combinedEntryAgain.company.Name,
CustomerName = vendorCustomer.Name,
ProductId = combinedEntryAgain.combinedEntry.products.Id,
ProductName = combinedEntryAgain.combinedEntry.products.Name,
combinedEntryAgain.combinedEntry.salesTransactionRecords.MovementType,
combinedEntryAgain.combinedEntry.salesTransactionRecords.Period,
combinedEntryAgain.combinedEntry.salesTransactionRecords.Quantity,
combinedEntryAgain.combinedEntry.salesTransactionRecords.Amount,
}).Where(x => x.Period >= dateStart && x.Period <= dateEnd)
.GroupBy(combinedEntryAgain => new
{
combinedEntryAgain.ProductId,
combinedEntryAgain.ProductName,
combinedEntryAgain.CompanyName,
combinedEntryAgain.CustomerName,
}
).Select(x => new SalesReportItem
{
ProductId = x.Key.ProductId,
Quantity = x.Sum(a => a.Quantity),
Amount = x.Sum(x => (x.MovementType == TableMovementType.Income ? x.Amount : -(x.Amount)))
});
var items = await queryable.ToListAsync();
return _mapper.Map<IEnumerable<SalesReportItem>>(items);
}

my mistake was that I did not specify the fields in the select, otherwise everything is buzzing, the upper code is working
Select(x => new SalesReportItem
{
ProductId = x.Key.ProductId,
ProductName = x.Key.ProductName,
CompanyName = x.Key.CompanyName,
CustomerName = x.Key.CustomerName,
Quantity = x.Sum(x => (x.MovementType == TableMovementType.Income ? x.Quantity : - x.Quantity)),
Amount = x.Sum(x => (x.MovementType == TableMovementType.Income? x.Amount: - x.Amount))
});
Thanks for the help
Hans Kesting

Related

How can I improve this LINQ query?

I fear that I'm doing n+1 query here, how can I improve this?
var inventories = AppContext.Inventories
.GroupBy(i => new { i.LocationId, i.ProductId })
.Select(g => new InventoryAvailableQuantity
{
ProductId = g.Key.ProductId,
LocationId = g.Key.LocationId,
Product = g.FirstOrDefault().Product.Name,
Location = g.FirstOrDefault().Location.Name,
PurchasePrice = AppContext.Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault().PurchasePrice,
ResellerPrice = AppContext.Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault().ResellerPrice,
RetailPrice = AppContext.Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault().RetailPrice
}).ToList();
You can use comprehension instead of method and gain the ability to use "let":
var inventories = from inv in AppContext.Inventories
group inv by new { i.LocationId, i.ProductId } into g
let firstInv = g.FirstOrDefault()
let firstPur = AppContext.Inventories
.Where(i => i.ProductId == g.Key.ProductId)
.OrderByDescending(i => i.DateAdded)
.FirstOrDefault()
select new InventoryAvailableQuantity
{
ProductId = g.Key.ProductId,
LocationId = g.Key.LocationId,
Product = firstInv.Product.Name,
Location = firstInv.Location.Name,
PurchasePrice = firstPur.PurchasePrice,
ResellerPrice = firstPur.ResellerPrice,
RetailPrice = firstPur.RetailPrice
}; // ( select ... { ... }).ToList(); if you will
Fast answer
var inventories = Inventories
.GroupBy(i => new {i.LocationId, i.ProductId})
.Select(g => new
{
g.Key.ProductId,
g.Key.LocationId,
CurrentInventories = g.FirstOrDefault(),
LastInventories = Inventories.Where(i => i.ProductId == g.Key.ProductId).OrderByDescending(i => i.DateAdded).FirstOrDefault()
})
.Select(g => new InventoryAvailableQuantity
{
ProductId = g.ProductId,
LocationId = g.LocationId,
Product = g.CurrentInventories.Product.Name,
Location = g.CurrentInventories.Location.Name,
PurchasePrice = g.LastInventories.PurchasePrice,
ResellerPrice = g.LastInventories.ResellerPrice,
RetailPrice = g.LastInventories.RetailPrice
})
.ToList();
You can take last item after grouping and take what you want.

The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities

var residenceRep =
ctx.ShiftEmployees
.Include(s => s.UserData.NAME)
.Include(s => s.ResidenceShift.shiftName)
.Join(ctx.calc,
sh => new { sh.empNum, sh.dayDate },
o => new { empNum = o.emp_num, dayDate = o.trans_date },
(sh, o) => new { sh, o })
.Where(s => s.sh.recordId == recordId && s.o.day_flag.Contains("R1"))
.OrderBy(r => r.sh.dayDate)
.Select(r => new
{
dayDate = r.sh.dayDate,
empNum = r.sh.empNum,
empName = r.sh.UserData.NAME,
shiftId = r.sh.shiftId,
shiftName = r.sh.ResidenceShift.shiftName,
recordId,
dayState = r.o.day_desc.Split('[', ']')[1]
}).ToList();
I get an exception :
The LINQ expression node type 'ArrayIndex' is not supported in LINQ to
Entities
How i could find an alternative to Split('[', ']')[1] in this query
You must commit the query and do the split after loading the data:
var residenceRep =
ctx.ShiftEmployees
.Include(s => s.UserData.NAME)
.Include(s => s.ResidenceShift.shiftName)
.Join(ctx.calc,
sh => new { sh.empNum, sh.dayDate },
o => new { empNum = o.emp_num, dayDate = o.trans_date },
(sh, o) => new { sh, o })
.Where(s => s.sh.recordId == recordId && s.o.day_flag.Contains("R1"))
.OrderBy(r => r.sh.dayDate)
.Select(r => new
{
dayDate = r.sh.dayDate,
empNum = r.sh.empNum,
empName = r.sh.UserData.NAME,
shiftId = r.sh.shiftId,
shiftName = r.sh.ResidenceShift.shiftName,
recordId = r.sh.recordId,
dayState = r.o.day_desc,
})
.ToList()//Here we commit the query and load data
.Select(x=> {
var parts = x.dayState.Split('[', ']');
return new {
x.dayDate,
x.empNum,
x.empName,
x.shiftId,
x.shiftName,
x.recordId,
dayState = parts.Length > 1 ?parts[1]:"",
};
})
.ToList();
I had this Issue and the approach that I've chose was that get all element I wanted and save them into a List and then filter the actual data on that list.
I know this is not the best answer but it worked for me.

Remove duplicate rows has the same datetime from List<T>

I have list with 2 parameter's (dynamic)
DateTime OrderDate
decimal TotalPrice
every list may have a same datetime and diffrent price
- DateTime -- Price
- 10/10/10 -- 100
- 11/11/11 -- 111
- 11/11/11 -- 100
- 10/10/10 -- 122
- etc
now i need to combine them. for i see only 1 datetime and 1 price
- DateTime -- Price
- 10/10/10 -- 222
- 11/11/11 -- 211
- etc
here the code
var data = db.CheckOut.Where(x => x.ISOrderComplete == true).OrderBy(c => c.Order.OrderDate).ToArray()
.GroupBy(y => new { OrderDate = y.Order.OrderDate, TotalPrice = y.TotalPrice })
.Select(a => new { OrderDate = a.Key.OrderDate, TotalPrice = a.Key.TotalPrice })
.ToList();
I try to add the function the
var data = db.CheckOut.Where(x => x.ISOrderComplete == true).OrderBy(c => c.Order.OrderDate).ToArray()
.GroupBy(y => new { OrderDate = y.Order.OrderDate, TotalPrice = y.TotalPrice })
.Select(a => new { OrderDate = a.Key.OrderDate, TotalPrice = a.Sum(b => b.TotalPrice) })
.ToList();
What i have to do?
i dont need this sum in db. i need this sum to display statistic about incoms to company in charts so i need to sum each data for how much getting .
var data = db.CheckOut.Where(x => x.ISOrderComplete == true)
.GroupBy(y => new { OrderDate = y.Order.OrderDate})
.Select(a => new { OrderDate = a.Key.OrderDate, TotalPrice = a.Sum(b => b.TotalPrice)})
.OrderBy(c => c.Order.OrderDate)
.ToList();
Following deramko approach, the only missing thing is that you shouldn't group by OrderDate, but instead, OrderDate.Date, because the time can be different.
Try something like this:
var data = db.CheckOut.Where(x => x.IsCheckoutComplete)
.GroupBy(x => new { OrderDate = x.Order.OrderDate.Date})
.Select(a => new { OrderDate = a.Key.OrderDate, TotalPrice = a.Sum(b => b.Order.TotalPrice)})
.OrderBy(c => c.OrderDate)
.ToList();
You can check it on https://dotnetfiddle.net/3mrZkf
becuse in Linq we cant groupby orderdate.date so i split it. then we can order by the date.
public JsonResult TotalIncomeJson()
{
var tempD = db.CheckOut.Where(x => x.ISOrderComplete).ToList();
var data = tempD.GroupBy(x => x.Order.OrderDate.Date).Select(y => new { OrderDate = y.Key, TotalPrice = y.Sum(a => a.TotalPrice) })
.OrderBy(b=>b.OrderDate.Year).ToList();
return Json(data, JsonRequestBehavior.AllowGet);
}

Conditional Groupby in LINQ

I m trying to do very similar linq statements but conditional on conditions they are slightly different. right now i just repeat the entire statement with small amendments but this should be possible much more concise. What I struggle with is to do the conditional groupby and also conditional select within the statement. My long version is:
class data
{
public string year;
public string quarter;
public string month;
public string week;
public string tariff;
public double volume;
public double price;
}
class results
{
public string product_code;
public string tariff;
public double volume;
public double price;
}
class Program
{
public static List<results> aggregationfunction(List<data> inputdata, string tarifftype, string timecategory)
{
List<results> returndata = new List<results>();
if (tarifftype.Equals("daynight") & timecategory.Equals("yearly"))
{
returndata = inputdata.GroupBy(a => new { a.tariff, a.year })
.Select(g => new results { product_code = g.Select(a => a.year).First(), tariff = g.Select(a => a.tariff).First(), volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
.ToList();
}
else if (tarifftype.Equals("allday") & timecategory.Equals("yearly"))
{
returndata = inputdata.GroupBy(a => new { a.year })
.Select(g => new results { product_code = g.Select(a => a.year).First(), tariff = "allday", volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
.ToList();
}
else if (tarifftype.Equals("daynight") & timecategory.Equals("quarterly"))
{
returndata = = inputdata.GroupBy(a => new { a.tariff, a.year, a.quarter })
.Select(g => new results { product_code = g.Select(a => a.year).First() + "_" + g.Select(a => a.quarter).First(), tariff = g.Select(a => a.tariff).First(), volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
.ToList();
}
else if (tarifftype.Equals("allday") & timecategory.Equals("quarterly"))
{
returndata = inputdata.GroupBy(a => new { a.year, a.quarter })
.Select(g => new results { product_code = g.Select(a => a.year).First() + "_" + g.Select(a => a.quarter).First(), tariff = "allday", volume = g.Sum(a => a.volume), price = g.Average(a => a.price) })
.ToList();
}
return returndata;
}
}
Any pointers would be appreciated. As you can see the group by and allocation of tariff and product code differ but this shouldnt mean I need to repeat it all, does it?
Shorter code:
return inputdata
.GroupBy(a => new
{
a.year,
quarter = timecategory == "quarterly" ? a.quarter : string.Empty,
tariff = tarifftype == "daynight" ? a.tariff : "allday"
})
.Select(g => new results
{
product_code = g.Key.year + (string.IsNullOrEmpty(g.Key.quarter) ? "" : "_" + g.Key.quarter),
tariff = g.Key.tariff,
volume = g.Sum(a => a.volume),
price = g.Average(a => a.price)
})
.ToList();
The IGrouping<TKey, data> items returned by inputdata.GroupBy (where data is the element type of inputdata) will always implement IEnumerable<data> regardless of the anonymous key type (which may be either {year} or {tariff, year} etc). So all the GroupBy values could be generalized to IEnumerable<IEnumerable<data>> and you could then break it down to two steps:
IEnumerable<IEnumerable<data>> groups;
if (condition) {
groups = inputdata.GroupBy(a => new { a.year });
} else if (condition) {
groups = inputdata.GroupBy(a => new { a.tariff, a.year });
} else {
groups = inputdata.GroupBy(a => new { a.year, a.quarter });
}
returndata = groups.Select(...)
The conditional select should be simpler to implement because you can just use conditional operators inline, or expand the selector function to a multi-line block, e.g.:
.Select(g => {
var product_code = ...;
if (condition) {
product_code += "_" + ...;
}
return new results { product_code = product_code, ... };
})

Lambda expression Group by in C#

I would like to group my LINQ query by ItemNumber and return the whole table with the total for Quantity.
Example:
ItemNumber - ItemName - Quantity
100 Item1 1
150 Item2 2
100 Item1 2
200 Item3 1
150 Item2 2
Should be:
ItemNumber - ItemName - Quantity
100 Item1 3
150 Item2 4
200 Item3 1
This is the query I am trying to group:
public IQueryable<WebsiteOrderStatus> GetOrderStatusByAccountNumberWithoutDeleted
(string accountNumber)
{
return db.WebsiteOrderStatus
.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1);
}
And my best result so far(this can't compile though):
public IQueryable<IGrouping<Int32?, WebsiteOrderStatus>> lol(string accountNumber)
{
db.WebsiteOrderStatus
.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
.GroupBy(g => g.ItemNumber)
.Select(g => new
{
g.Key.ItemNumber,
Column1 = (Int32?)g.Sum(p => p.Quantity)
});
}
EDIT:
Thanks for the replies everyone, I must face it. Theese anonymous types are pretty hard to work with in my opinion, so I found another solution.
I made another method, which sums the quantity of the users items and grouped the first one.
public IQueryable<WebsiteOrderStatus> GetOrderStatusByAccountNumberWithoutDeleted(string accountNumber)
{
return db.WebsiteOrderStatus.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1).GroupBy(x => x.ItemNumber).Select(grp => grp.First());
}
public int GetQuantityOfUsersItem(string accountNumber, string itemNumber)
{
return db.WebsiteOrderStatus.Where(x => x.ItemNumber == itemNumber && x.AccountNumber == accountNumber).Sum(x => x.Quantity);
}
At the page where I have my gridview I did:
var query = websiteOrderStatusRep.GetOrderStatusByAccountNumberWithoutDeleted(AppSession.CurrentLoginTicket.AccountNumber).Select(x => new { x.ItemName, x.ItemNumber, x.FormatName, x.Price, x.Status, x.Levering, Quantity = websiteOrderStatusRep.GetQuantityOfUsersItem(x.AccountNumber, x.ItemNumber)});
public IQueryable<IGrouping<Int32?, WebsiteOrderStatus>> lol(string accountNumber)
{
db.WebsiteOrderStatus
.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
.GroupBy(g => g.ItemNumber)
.Select(g => new
{
ItemNumber = g.Key,
ItemName = g.First().ItemName,
Count = g.Sum(item => item.Quantity)
});
}
public IQueryable<OrderStatus > lol(string accountNumber)
{
return db.WebsiteOrderStatus
.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
.GroupBy(g => g.ItemNumber)
.Select(g =>
new OrderStatus //This is your custom class, for binding only
{
ItemNumber = g.Key,
ItemName = g.First().ItemName,
Quantity = g.Sum(g => g.Quantity)
}
);
}
I think the Select should be:
.Select(g => new
{
ItemNumber = g.Key,
Column1 = (Int32?)g.Sum(p => p.Quantity)
});
Note the change in the first line of the anonymous type. The key of the grouping is already the item number.
The only problems I see with your query are
Missing return statement as per comments
The select statement should be:
-
.Select(g => new {
ItemNumber = g.Key,
Total = g.Sum(p => p.Quantity)
});
EDIT: If you want to get, lets say ItemNumber and ItemName , in the resulting object, you must also group on those fields
db.WebsiteOrderStatus
.Where(x => x.AccountNumber == accountNumber && x.LastUpdatedStatus != 1)
.GroupBy(g => new { g.ItemNumber, g.ItemName })
.Select(g => new
{
ItemNumber = g.Key.ItemNumber,
ItemName = g.Key.ItemName,
Count = g.Sum(item => item.Quantity)
});
You cannot use anonymous type for return value type. So you will never compile the code.
Also your linq expression has IQueryable< [anonymous type] > result type.
I believe that you can do something like this:
public IQueryable<OrderStatus> lol(string accountNumber)
{
db.WebsiteOrderStatus
.Where(order => order.AccountNumber == accountNumber && order.LastUpdatedStatus != 1)
.GroupBy(order => order.ItemNumber)
.Select(grouping => new OrderStatus //This is your custom class, for binding only
{
ItemNumber = grouping.Key,
ItemName = grouping.First().ItemName,
Quantity = grouping.Sum(order => order.Quantity)
});
}
I`ve fixed my answer too :)

Categories