Write own functions to be used in c# linq statement - c#

I have an object
public class Product{
public int Id { get; set; }
public double Cost { get; set; }
public DateTime DatePurchased { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
public string Category { get; set; }
}
I also have a function public void TotalProductDiscountHistory(DateTime datePurchased, double cost) that does some complex maths and return the discount values from the day it was purchased to date.
I would like to basically be able to call this function inside my query statement to return the sum of all possible for all products as show below.
var query = db.Products.where(x => x.clientId == clientId)
.GroupBy(c => c.Category).Select(a => new {
Category = a.Category,
Cost = a.Sum(u => u.Cost),
TotalDiscounts = a.Sum( TotalProductDiscountHistory(a.DatePurchased,
a.Cost))
});
My questions is how do I achieve this or rather how do I create a function so that I am able to call it within a linq query statement, pass values and return a result.

Your main problem is that the EF is trying to convert the LINQ query into SQL. As there is no SQL equivalent of your function, it really has to just pluck out the data needed for that calculation, and do it after the SQL query, in code.
Linq2SQL was excellent as handling that automatically. EF (even after 6 versions), not so much.
So, we'll have to do it manually:
public double TotalProductDiscountHistory(DateTime datePurchased, double cost) {...}
class CategoryTotals
{
public int Category {get; set;}
public double Cost {get; set;}
public double TotalDiscounts {get; set;}
}
var query = from p in db.Products
where P.clientId == clientId
group p by p.Category;
var totals = new List<CategoryTotals>();
foreach(var grp in query)
{
var ct = new CategoryTotals
{
Category =grp.Category,
Cost = grp.Sum(u => u.Cost),
TotalDiscounts = grp.Sum(u=>
TotalProductDiscountHistory(u.DatePurchased, u.Cost))
};
totals.add(ct);
}

Related

Linq Query where related entity contains value from array

I have a Model called JobReport which looks like this (simplified)
public class JobReport
{
public JobReport()
{
WorkOrders = new List<WorkOrder>();
}
public int JobID { get; set; }
public List<WorkOrder> WorkOrders{ get; set; }
}
public class WorkOrder
{
public WorkOrder()
{
Total = 0;
}
public string Trade { get; set; }
public int WorkOrderID { get; set; }
public decimal? Total { get; set; }
}
I'd like to run a Linq query which gets me all the Jobs that have WorkOrders that have a trade which is in a passed array.
var trades = new string[] { }
I've tried something like the following which doesn't work, as it tries to get me a list of workorders, when I actually need the underlying jobs.
The problem appears to be because I'm calling Select
var jobsDB = db.Jobs.Include(x=>x.WorkOrders).ToList();
var jobs = (from p in jobsDB
select new JobReport()
{
JobID = p.JobID,
WorkOrders = p.WorkOrders.ToList()
}
jobs = jobs
.Select(x => x.WorkOrders
.Where(y => trades.Contains(y.Trade)));
This will work:
jobs = jobs
.Where(x => x.WorkOrders.Any(y => trades.Contains(y.Trade)));
The way I usually tackle these problems is that I look at what the outcome must be (a list of jobs) - that means we need to put the Where first, and we must look for a condition for a job to be included. It's a bit like constructing a SQL query - in fact you can use the SQL like query syntax for most LINQ tasks, if you prefer.

Add data to a list insde a list where item in the prerent list

I would like to select a where statement that adds items to a list where only product codes match. I have it so it gets all of the products sold in the sale but I would like there were statement to get only products in this sale.
PS: This is really hard to explain
Model
public class userSales
{
public string Name { get; set; }
public string UserName { get; set; }
public int Sale_Id { get; set; }
public int CostumerID { get; set; }
public string Sale_Date { get; set; }
public string Paid { get; set; }
public Nullable<int> Sale_Cost { get; set; }
public string Discount_Code { get; set; }
public List<SaleProduct> saleProductsList { get; set; }
}
public class SaleProduct
{
public int SaleID { get; set; }
public string ProductCode { get; set; }
public int ProductCount { get; set; }
public string Image_Path { get; set; }
public string Shoot_Date { get; set; }
public string Shoot_Info { get; set; }
}
Linq statement where I'm having trouble:
var test = (from _ClientData in db.ClientDatas
join _salesInfo in db.Sales_Infoes
on _ClientData.CostumerID
equals _salesInfo.CostumerID
where _ClientData.UserName == _userName
select new userSales()
{
CostumerID = _ClientData.CostumerID,
Name = _ClientData.Name,
UserName = _ClientData.UserName,
Sale_Id = _salesInfo.Sale_Id, // This is the item i would like to use in my were statement
Sale_Date = _salesInfo.Sale_Date,
Sale_Cost = _salesInfo.Sale_Cost,
Discount_Code = _salesInfo.Discount_Code,
Paid = _salesInfo.Paid,
// Problem here
saleProductsList = db.SaleProducts.Where()
}).ToList();
Got to this based on the answer:
var reult = db.ClientDatas.Where(a => a.UserName == _userName)
.Join(db.Sales_Infoes,
a => a.CostumerID,
b => b.CostumerID,
(a, b) => new userSales()
{
CostumerID = a.CostumerID,
Discount_Code = b.Discount_Code,
Sale_Cost = b.Sale_Cost,
Sale_Id= b.Sale_Id,
Name = a.Name,
Sale_Date = b.Sale_Date,
UserName = a.UserName,
Paid = b.Paid,
saleProductsList = db.SaleProducts.Where(c => c.SaleID == b.Sale_Id).ToList()
}).ToList();
You're not looking for a where, you're looking for a join. Where filters the results on a single table, join intersects two tables which is actually what you want here.
var result = db.Sales_Infoes.Where(x => x.UserName == _userName)
.Join(db.ClientDatas,
x => x.Sale_Id,
y => y.Sale_id,
(x, y) => new userSales() {
// x is SalesInfo obj y is ClientDatas obj do assignement here
Name = y.Name,
Sale_Date = y.Sale_date
}).ToList();
Just fyi I haven't had a chance to test that but it's the basic idea. You don't need a select like in your statement because the last argument I'm passing into join is the lambda (x, y) => ... in that case x and y are the current row from each table (that we've gotten from applying our where to the user sales table then joining those results into the salesproduct table) so whatever projections you want to do occur there. The other two method args above that are the telling join which fields to compare, it's the 'key selector' lambda expression for each table.

Using a calculated value in the OrderBy clause with EF

I'm trying to use a calculated value in my OrderBy clause in a LINQ query.
The error I am getting is:
DbArithmeticExpression arguments must have a numeric common type.
My model looks like this:
public class PostModel
{
public int ID { get; set; }
public DateTime Created { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string FilePath { get; set; }
public float Rank { get; set; }
public UserProfile Creator { get; set; }
public bool ShowPost { get; set; }
public PostModel()
{
Created = DateTime.Now;
Rank = 0;
ShowPost = false;
}
}
and I'm trying to select posts using this:
var todaysDate = DateTime.Now.AddDays(-10);
var result = _database.Posts
.Where(p => p.ShowPost == true)
.OrderBy(x => ((float)x.Rank) - (((float)(x.Created - todaysDate).TotalDays)) / 2f)
.Skip(page * StaticVariables.ResponseDataPageSize)
.Take(StaticVariables.ResponseDataPageSize)
.Select(s => new
{
id = s.ID,
rank = s.Rank,
title = s.Title,
description = s.Description
}
);
It's the order by causing the error. I first thought it was that I was not casting all my variables to the same type, but adding (float) does not seem to help.
The purpose of the code is to make make high ranking posts fall down the list over time as to allow newer information to be shown.
Any ideas?
Use EntityFunctions in LinqToEntity:
EntityFunctions.DiffDays(todaysDate, x.Created)

Sorting Collections within and by the Sum of Another Collections

2 classes
public class Student
{
public int StudentID { get; set;}
public string Name { get; set;}
public List<Fee> Fees {get;set;}
}
public class Fee
{
public int FeeID { get; set;}
public decimal FeeAmount { get; set; }
}
let say there are 10 students objects Student[] stud = new Student[10]
if stud[0] has 2 fees ( Fee[2] ) and they are
FeeID=1, FeeAmount=54.23
FeeID=2, FeeAmount=234.98
if stud[1] has 1 fees ( Fee[2] ) and they are
FeeID=1, FeeAmount=9.99
if stud[2] has 3 fees ( Fee[3] ) and they are
FeeID=1, FeeAmount=123.45
FeeID=2, FeeAmount=67.89
FeeID=3, FeeAmount=987.65
I need to sort the Student Collections by TotalAmount(Fee Collection)
TotalAmount of Fee
stud[0] = 54.23+234.98=289.21
stud[1] = =9,99
stud[2] = 123.45+67.89+987.65=1178.99
there for after sorted it should become
stud[0] = 123.45+67.89+987.65=1178.99
stud[1] = 54.23+234.98=289.21
stud[2] = =9,99
It sounds like you just want:
stud = stud.OrderByDescending(x => x.Fees.Sum(fee => fee.FeeAmount)).ToArray();
Gotta love LINQ :)
A couple of things to note:
This will still only calculate the sum of the fees once per student
This will not currently handle null elements. Do you need to? (You seem to have a fixed array size... perhaps use List<Student> instead?)
Unless you actually need it as an array afterwards, just drop the ToArray call. Be aware that it will sort it every time you iterate through it unless you use ToArray or ToList though.
var results = stud.OrderByDescending(s => s.Fees.Sum(f => f.FeeAmount)).ToArray();
A simple Linq query should do the job:
stud =
(from s in stud
orderby s.Fees.Sum(f => f.FeeAmount)
select s)
.ToArray();
var students = new List<Student>() { .. add students here ... };
students.OrderBy(x => x.Fees.Sum(y => y.FeeAmount));
And if you use an old .net framework (without Linq) :
public class Student : IComparable
{
public int StudentID { get; set; }
public string Name { get; set; }
public List<Fee> Fees { get; set; }
private decimal TotalAmount
{
get
{
decimal total = 0;
if (Fees != null)
foreach (var fee in Fees)
total += fee.FeeAmount;
return total;
}
}
public int CompareTo(object obj)
{
//Ascending
//return TotalAmount.CompareTo((obj as Student).TotalAmount);
//Descending
return (obj as Student).TotalAmount.CompareTo(TotalAmount);
}
}
public class Fee
{
public int FeeID { get; set; }
public decimal FeeAmount { get; set; }
}
List<Student> students = new List<Student>();
...
students.Sort();
Linq is better...

Joining two tables with one to many relatipnship in entity framework code first

i have
public class Menu
{
public int ID { get; set;}
public List<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
public byte[] Image { get; set; }
public string Name { get; set; }
}
i would like to know all tasks which has a certain List ID using LINQ queries
Try
var result = Menus.Where(menu => menu.ID == id)
.Select(menu => menu.Tasks)
.FirstOrDefault();
Also you may want to peruse http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b as this would answer most of your queries like the above.
You can use Enumerable.Where
var list = Tasks.Where(l=>l.ID ==x);
or
var list = from t in Tasks
where t.ID == x
select t;
x will be the id you need to compare

Categories

Resources