Linq two table with sum column - c#

My RowMultiplevaluw table is
public class RowMultipleValues
{
public int ID { get; set; }
public String Year{ get; set; }
public string country { get; set; }
public decial Admin { get; set; }
public decimal Finance { get; set; }
public virtual ICollection<UsedAmount> UsedAmount { get; set; }
}
My used amount table is
public class UsedAmount
{
public int ID { get; set; }
public string Year{ get; set; }
public string country { get; set; }
public decial UsedAmount { get; set; }
public int RowMultipleValues ID { get; set; }
Public virtual RowMultibleValue RowMultibleValue { get; set; }
}
My query is
var query = from mtv in context.multiplerowvaluetable
join usd in dbcontext.usedtsble on mtv.year equal usd.year group g by mtv.country into g
select new { country =g.key,sumadmincolumn =g.sum(Admin),sumfinancecolumn = g.sum(finance) }).tolist();
Result which I want is
ID Year Country Admin. UsedAdmin Finance UsedFinance
1. 2017 USA 100 50 200 300
2. 2017 China 300 300 500 400
Total. 400 350 700 700
Please help me my model design and query for result.Thank.

So you want to join every MultipleValue with the UsedAmount on equal year value. Then group the result into groups of joined items with same country. Finally from every group create one object with the country, the sum of all Admin values and the sum of all finance values.
// first join the two collections on same year.
// we only need properties Country, Admin, Finance:
var result = myDbContext.MultipleRowValueTable.Join(myDbContext.UsedAmountTable,
multipleRow => multipleRow.Year, // from every multipleRow take the year
usedAmount => usedAmount.Year, // from every usedAmount take the year
(multipleRow, usedAmount) => new // when they match make a new object
{
Country = multipleRow.Country,
Admin = multipleRow.Admin,
UsedAdmin = usedAmount.Admin,
Finance = multipleRow.Finance,
UsedFinance = usedAmount.Finance,
})
// group the elements from this join table into groups with same Country
.GroupBy(joinedItem => joinedItem.Country, // all items in the group have this Country
joinedItem => new // the elements of the group
{
Admin = joinedItem.Admin,
UsedAdmin = joinedItem.UsedAdmin,
Finance = joinedItem.Finance,
UsedFinance = joinedItem.UsedFinance,
})
// finally: from every group take the Key (which is the Country)
// and the sum of the Admins and Finances in the group
.Select(group => new
{
Country = group.Key,
SumAdminColumn = group
.Select(groupElement => groupElement.Admin)
.Sum(),
... // others are similar
});
// from every group take the elements and sum the properties
.Select(group => new
{
Id = multipleRowValue.Id,
Year = multipleRowValue.Year,
Country = multipleRowValue.Country,
}

Related

LINQ getting field from another collection

I have 3 tables and I'm trying to get a combined result with a sum of one field of them.
I'm working with C#, .NET, Entity Framework 7 and SQL Server.
I need to get the city's Name of each result, but I store the idCity
Brand table:
public byte IdBrand { get; set; }
public string Name { get; set; } = null!;
Bundles table:
public int IdBundle { get; set; }
public short IdCity{ get; set; }
public short IdBrand { get; set; }
public decimal? Volume { get; set; }
Cities:
public short IdCity { get; set; }
public string Name { get; set; } = null!;
I've tried this linq query and got almost the result I want but the city field is failing and I got stuck...
var volume = context.Bundles
.GroupBy(city => city.IdCity)
.Select(cad => new
{
CITY = context.Cities.Local.ToList().ElementAt(cad.Key)!.Name,
BRAND1 = cad.Where(c => c.IdBrand == 1).Sum(c => c.Volume),
BRAND2 = cad.Where(c => c.IdBrand == 19).Sum(c => c.Volume)
}).ToList();
I get this result that I expect but the CITY is not correct, I think because the cad.Key is not the same than Cities Index
I also tried:
context.Cities.ToList()
.Where(i => context.Bundles.Any(a=> i.IdCity == a.IdCity))
.Select(x=> x.Name)
CITY
BRAND1
BRAND2
LONDON
10.2
12
MOSCOU
11.4
1
PARIS
9.1
0.4
I guess that the cad.Key is not what I need to use to get the ElementAt Cities but how can I get the city .Name from another table in the Select? Or what is the best way to perform this query?
Try the following query, it should have better performance:
var query =
from b in context.Bundles
group b by b.IdCity into g
select new
{
IdCity = g.Key,
BRAND1 = g.Sum(c => c.IdBrand == 1 ? c.Volume : 0),
BRAND2 = g.Sum(c => c.IdBrand == 19 ? c.Volume : 0)
} into agg
join city in context.Cities on agg.IdCity equals city.Id
select new
{
CITY = city.Name,
BRAND1 = agg.BRAND1,
BRAND2 = agg.BRAND2
};

Select The Record With the Lowest Payment for Each Customer using EntityFramework Core

I have an application that matches customers to vehicles in inventory. There are 3 main tables: Customer, Match, and Inventory. The match record contains the estimated monthly payment for a specific Customer and Inventory record. A customer can be matched to multiple vehicles in Inventory.
The match record contains a CustomerId and an InventoryId along with a MonthlyPayment field and a few other miscellaneous fields.
There is a 1 to Many relationship between Customer and Match.
There is a 1 to Many relationship between Inventory and Match.
For each customer, I want to select the Customer record, the match record with the lowest monthly payment, and the Inventory record for that match.
What is the best way to do this? Can it be done with a single query?
I tried this code, but entity framework can't evaluate it and it executes it locally which kills the performance.
var bestMatches = _matchRepository.GetAll(customerMatchSummaryRequest)
.Where(match =>
(_matchRepository.GetAll(customerMatchSummaryRequest)
.GroupBy(m => new { m.Bpid, m.BuyerId, m.CurrentVehicleId })
.Select(g => new
{
g.Key.Bpid,
g.Key.BuyerId,
g.Key.CurrentVehicleId,
LowestMonthlyPayment = g.Min(m => m.MonthlyPayment)
})
.Where(m => m.Bpid == match.Bpid
&& m.BuyerId == match.BuyerId
&& m.CurrentVehicleId == match.CurrentVehicleId
&& m.LowestMonthlyPayment == match.MonthlyPayment)
).Any())
.Include(m => m.Buyer)
.Include(m => m.Inventory);
I receive the following Output when stepping through the debugger:
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'GroupBy(new <>f__AnonymousType2`3(Bpid = [<generated>_2].Bpid, BuyerId = [<generated>_2].BuyerId, CurrentVehicleId = [<generated>_2].CurrentVehicleId), [<generated>_2])' could not be translated and will be evaluated locally.
Microsoft.EntityFrameworkCore.Query:Warning: The LINQ expression 'GroupBy(new <>f__AnonymousType2`3(Bpid = [<generated>_2].Bpid, BuyerId = [<generated>_2].BuyerId, CurrentVehicleId = [<generated>_2].CurrentVehicleId), [<generated>_2])' could not be translated and will be evaluated locally.
Assuming your model is something like this
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Match> Matches { get; set; }
}
public class Inventory
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Match> Matches { get; set; }
}
public class Match
{
public int CustomerId { get; set; }
public Customer Custmer { get; set; }
public int InventoryId { get; set; }
public Inventory Inventory { get; set; }
public decimal MonthlyPayment { get; set; }
}
the query in question could be something like this:
var query =
from customer in db.Set<Customer>()
from match in customer.Matches
where !customer.Matches.Any(m => m.MonthlyPayment > match.MonthlyPayment)
select new
{
Customer = customer,
Match = match,
Inventory = match.Inventory
};
Note that it could return more than one match for a customer if it contains more than one inventory record with the lowest payment. If the data allows that and you want to get exactly 0 or 1 result per customer, change the
m.MonthlyPayment > match.MonthlyPayment
criteria to
m.MonthlyPayment > match.MonthlyPayment ||
(m.MonthlyPayment == match.MonthlyPayment && m.InventoryId < match.InventoryId)
P.S. The above LINQ query is currently the only way which translates to single SQL query. Unfortinately the more natural ways like
from customer in db.Set<Customer>()
let match = customer.Matches.OrderBy(m => m.MonthlyPayment).FirstOrDefault()
...
or
from customer in db.Set<Customer>()
from match in customer.Matches.OrderBy(m => m.MonthlyPayment).Take(1)
...
lead to client evaluation.

Creating object from another list for a specific ID

I have a following list after joining 2 tables
Id Name IsActive FeeName Amount
1 Test 1 Fee1 100
1 Test 1 Fee2 200
1 Test 1 Fee3 300
I have a class called products
public class Products
{
public int Id { get; set; }
public string ProductName { get; set; }
public bool IsActive { get; set; }
public List<ProductFee> ProductFunding { get; set; }
}
Product fee class has FeeName and amount properties,
I need to create a object of above class something like
1,test,1,List of ProductFee
var products = results.GroupBy(r => new { r.Id, r.Name, r.IsActive }).Select(g => new Products(){ Id = g.Key.Id, ProductName = g.Key.Name, IsActive = g.Key.IsActive, ProductFunding = g.Select(p => new ProductFee(){ FeeName = p.FeeName, Amount = p.Amount }).ToList() });

Linq returning single item in list part of an object

I have the following database tables:
# account_type table #
id desc
----------------
1 savings
2 checking
# account table #
id account_type_id Name
--------------------------
1 2 Don
2 1 Henry
3 1 Lisa
4 2 Jenifer
I want to write a linq query so that it returns object with collection in it, i.e.
desc: Savings { Don, Jenifer }
I created these classes:
public class acctType
{
public id { get; set; }
public string desc { get; set; }
public IEnumerable<account> account{ get; set;}
}
public class account
{
public int id { get; set; }
public int account_type_id { get; set; }
public string name { get; set;}
}
My method in api is calling:
public accType Get(int id)
{
var accounts = (from c in db.account_type
where c.p_id == id
select new accType
{
id = c.id,
name = c.desc,
account = new List<account> { name = c.desc }
});
}
return accounts.FirstOrDefault()
Problem is when I get the account object back it only has single for name i.e
dsc: checking { Don }
While answer should be Don and Jenifer. How do I fix this?
You are returning only FirstOrDefault() item,Change the return statement to
return accounts.ToList()
Edit:
public List<accType> Get(int id)
{
var accounts = (from c in db.account_type
where c.p_id == id
select new accType
{
id = c.id,
name = c.desc,
account = new List<account>()
{
name=c.desc
}
});
return accounts.ToList()
}

How can I get two different aggregates in a single LINQ?

I have a list of this object:
public class Customer
{
public int Id { get; set; }
public string EmailAddress { get; set; }
public DateTime ServiceStartDate { get; set; }
public DateTime? BillingStartDate { get; set; }
public string Status { get; set; }
}
In preparation for making a chart displayed on a dashboard I am trying to condense this list into another list of this object:
public class DashboardCustomerConversions
{
public string Month { get; set; }
public int Trials { get; set; }
public int Purchased { get; set; }
}
Where the end result looks something like:
Month Trials Purchases
--------- ------ ---------
Dec 2010 390 250
Jan 2011 345 190
Feb 2011 576 340
I am having a hard time coming up with a LINQ statement that can achieve the desired end result. This statement is very close:
var list = from b in results
group b by new { b.ServiceStartDate.Year, b.ServiceStartDate.Month } into g
select new Test
{
Month = string.Format("{0} {1}", CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(g.Key.Month), g.Key.Year),
Trials = g.Count(),
Purchased = g.Count()
};
The obvious problem in is the "Purchased = g.Count()" line in that it just repeats the Trials result. I would like to count objects where the BillingStartDate.HasValue is true.
Is there a way to restructure the LINQ to make this work?
Edit: I would prefer a fluent style of syntax but I was unable to get the above to work. Answer in any variation would be great.
You need to pass a condition to the Count method.
Purchased = g.Count(q => q.BillingStartDate.HasValue)
So SLaks had the right solution. Here it is written in fluent syntax:
listOfCustomer.GroupBy(c => new { c.ServiceStartDate.Year, c.ServiceStartDate.Month })
.Select(group => new DashboardCustomerConversions()
{
Month = string.Format("{0} {1}", CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(group.Key.Month), group.Key.Year),
Trials = group.Count(),
Purchased = group.Count(c => c.BillingStartDate.HasValue)
});

Categories