I just started working with LINQ. How can I use SUM and LEFT JOIN using LINQ. I am trying to build the query below in LINQ. Is it possible?
SELECT t.TenantID, t.TenantFName, t.TenantLName, t.RentalAmount, t.PetRent, t.HousingAmount, t.UtilityCharge, t.TVCharge, t.SecurityDeposit, t.HOAFee,
t.ParkingCharge, t.StorageCharge, t.ConcessionAmount, t.ConcessionReason, t.TenantEmail, t.TenantPhone, t.CellPhoneProviderID, t.MoveInDate,
p.PropertyID, p.PropertyName,
TotalDebit, HousingDebit, TotalCredit, HousingCredit
FROM Tenants t
JOIN Properties p ON t.PropertyID = p.PropertyID
LEFT JOIN (
Select
TenantID,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 AND TenantTransactionDate <= Now() THEN TransactionAmount ELSE 0 END) AS TotalDebit,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID = 6 AND TenantTransactionDate <= Now() THEN TransactionAmount ELSE 0 END) AS HousingDebit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID != 6 AND TenantTransactionDate <= Now() THEN TransactionAmount ELSE 0 END) AS TotalCredit,
SUM(CASE WHEN TransactionTypeID = 2 AND ChargeTypeID = 6 AND TenantTransactionDate <= Now() THEN TransactionAmount ELSE 0 END) AS HousingCredit
From TenantTransactions
Group By TenantID
) sums ON sums.TenantID = t.TenantID
Where t.Prospect = 2
AND t.PropertyID = 1
Thanks
Roughing out an answer and making a few assumptions about your object model, I'd start off by calculating each of the sums individually with something akin to this statement:
var tenantsTotalDebit = tenantTransactions.Where(tt.TenantId == requestedTenantId && tt.TransactionTypeID == 1 && tt.ChargeTypeID != 6 && tt.TenantTransactionDate <= DateTime.Now).Select(tt => tt.TransactionAmount).Sum();
After you've got all the sums, you can create another query that queries the Tenants and, assuming the Tenants object has it's associated Properties as a member, you could combine them in something like this:
var tenantQuery = tenants.Where(t.Prospect == 1 && t.PropertyID ==1).Select(t.TenantID, t.TenantFName, ..., tenantsTotalDebit, tenantsHousingDebit, tenantsTotalCredit, tenantsHousingCredit);
You can include values beyond the object type that you're querying in a Select() method, so you can include the precalculated sums after determining them separately.
I used classes to model you database. See code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication53
{
class Program
{
static void Main(string[] args)
{
List<Tenants> tenants = new List<Tenants>();
List<Properties> properties = new List<Properties>();
List<TenantTransactions> transactions = new List<TenantTransactions>();
var tenantTransactions = transactions.GroupBy(x => x.TenantID).Select(x => new
{
id = x.Key,
totalDebit = x.Where(y => (y.TransactionTypeID == 1) && (y.ChargeTypeID != 6) && (y.TenantTransactionDate <= DateTime.Now)).Sum(y => y.TransactionAmount),
housingDebit = x.Where(y => (y.TransactionTypeID == 1) && (y.ChargeTypeID == 6) && (y.TenantTransactionDate <= DateTime.Now)).Sum(y => y.TransactionAmount),
totalCredit = x.Where(y => (y.TransactionTypeID == 2) && (y.ChargeTypeID != 6) && (y.TenantTransactionDate <= DateTime.Now)).Sum(y => y.TransactionAmount),
housingCredit = x.Where(y => (y.TransactionTypeID == 2) && (y.ChargeTypeID == 6) && (y.TenantTransactionDate <= DateTime.Now)).Sum(y => y.TransactionAmount)
}).ToList();
var results2 = (from t in tenants
join p in properties on t.PropertyID equals p.PropertyID
join tt in tenantTransactions on t.TenantID equals tt.id into ps
from tt in ps.DefaultIfEmpty()
select new { t = t, p = p, tt = tt })
.Where(x => (x.t.PropertyID == 1) && (x.t.Prospect == 1))
.GroupBy(x => x.t.TenantID)
.Select(x => new {
tenantID = x.Key,
tenantFirstName = x.FirstOrDefault().t.TenantFName,
tenantLastName = x.FirstOrDefault().t.TenantLName,
tenantEmail = x.FirstOrDefault().t.TenantEmail,
tenantPhone = x.FirstOrDefault().t.TenantPhone,
tenantCellPhoneProvider = x.FirstOrDefault().t.CellPhoneProviderID,
properties = x.Select(y => new {
propertyID = y.p.PropertyID,
propertyName = y.p.PropertyName,
rentalAmount = y.t.RentalAmount,
petRent = y.t.PetRent,
houseingAmount = y.t.HousingAmount,
utilityCharge = y.t.UtilityCharge,
tvCharge = y.t.TVCharge,
sercurityDeposit = y.t.SecurityDeposit,
hoaFee = y.t.HousingAmount,
parkingCharge = y.t.ParkingCharge,
storageCharge = y.t.StorageCharge,
concessionAmount = y.t.ConcessionAmount,
concessionReason = y.t.ConcessionReason,
tenantMoveInDate = y.t.MoveInDate
}).ToList(),
totalDebit = x.FirstOrDefault().tt.totalDebit,
housingDebit = x.FirstOrDefault().tt.housingDebit,
totalCredit = x.FirstOrDefault().tt.totalCredit,
housingCredit = x.FirstOrDefault().tt.housingCredit,
}).ToList();
}
}
public class TenantTransactions
{
public int TenantID { get; set; }
public int TransactionTypeID{ get;set;}
public int ChargeTypeID { get;set;}
public DateTime TenantTransactionDate { get;set;}
public decimal TransactionAmount { get;set;}
}
public class Tenants
{
public int PropertyID { get; set; }
public int Prospect { get; set; }
public int TenantID { get; set; }
public string TenantFName { get; set; }
public string TenantLName { get; set; }
public decimal RentalAmount { get; set; }
public decimal PetRent { get; set; }
public decimal HousingAmount { get; set; }
public decimal UtilityCharge { get; set; }
public decimal TVCharge { get; set; }
public decimal SecurityDeposit { get; set; }
public decimal HOAFee { get; set; }
public decimal ParkingCharge { get; set; }
public decimal StorageCharge { get; set; }
public decimal ConcessionAmount { get; set; }
public string ConcessionReason { get; set; }
public string TenantEmail { get; set; }
public string TenantPhone { get; set; }
public string CellPhoneProviderID { get; set; }
public DateTime MoveInDate { get; set; }
}
public class Properties
{
public int PropertyID { get; set; }
public string PropertyName { get; set; }
}
}
Related
Is there any way i can get number of child with max parent id and list the result with linq?
I'm trying to bring the total of values by Status, but i can only get the last one from the child
what I have done so far:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var lstRfq = new List<RfqEvents>()
{
new RfqEvents(1,1,DateTime.Parse("2021-05-06 03:00:00+00"),1),
new RfqEvents(2,2,DateTime.Parse("2021-05-06 03:00:00+00"),1),
new RfqEvents(3,2,DateTime.Parse("2021-05-06 03:00:00+00"),1),
new RfqEvents(4,3,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(5,4,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(6,5,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(7,5,DateTime.Parse("2021-05-06 00:00:00+00"),2),
new RfqEvents(8,5,DateTime.Parse("2021-05-06 00:00:00+00"),3),
new RfqEvents(9,6,DateTime.Parse("2021-05-06 00:00:00+00"),3),
new RfqEvents(10,6,DateTime.Parse("2021-05-06 00:00:00+00"),3),
};
var subquery = from c in lstRfq
group c by c.RfqId into g
select new InternalStatusInformations
{
RfqId = g.Key,
RfqEventId = g.Max(a => a.Id),
StatusId = g.Select(p => p.Status).FirstOrDefault()
};
var sss = from d in lstRfq.Where(p=> subquery.Select(p=>p.RfqEventId).Contains(p.Id))
group d by d.Status into z
select new InternalStatusInformations
{
StatusId = z.Key,
Total = z.Count(),
Past = z.Where(p => p.DueDate.HasValue && p.DueDate.Value.Date < DateTime.Now.Date).Count(),
Future = z.Where(p => p.DueDate.HasValue && p.DueDate.Value.Date > DateTime.Now.Date).Count(),
Today = z.Where(p => p.DueDate.HasValue && p.DueDate.Value.Date == DateTime.Now.Date).Count(),
FiveDays = z.Where(p => (p.DueDate.HasValue && p.DueDate.Value.Date > DateTime.Now.Date) && p.DueDate.HasValue && p.DueDate.Value.Date < DateTime.Now.Date.AddDays(5)).Count(),
};
//expected: Status 1: 2 values
// Status 2: 3 values
// Status 3: 2 value
//output: Status 1: 2 values
// Status 2: 2 values
// Status 3: 2 values
sss.Dump();
}
public class InternalStatusInformations
{
public int RfqEventId { get; set; }
public int RfqId { get; set; }
public int StatusId { get; set; }
public int Future { get; set; }
public int Past { get; set; }
public int Today { get; set; }
public int FiveDays { get; set; }
public int Total { get; set; }
public DateTime? DueDate { get; set; }
}
public class RfqEvents
{
public RfqEvents(int id, int rfqId, DateTime? dueDate, int status)
{
Id = id;
RfqId = rfqId;
DueDate = dueDate;
Status = status;
}
public int Id { get; set; }
public DateTime? DueDate { get; set; }
public int RfqId { get; set; }
public int Status { get; set; }
}
}
https://dotnetfiddle.net/YoRsIG
but something is not right with the results, could you guys help me?
If you just want to count the number of distinct RfqId values in each status, this should do it:
var pairs = lstRfq
.GroupBy(evt => evt.Status)
.Select(grouping =>
{
var status = grouping.Key;
var count = grouping
.Select(evt => evt.RfqId)
.Distinct()
.Count();
return (status, count);
});
foreach ((var status, var count) in pairs)
{
Console.WriteLine($"Status {status}: {count} values");
}
Output is:
Status 1: 2 values
Status 2: 3 values
Status 3: 2 values
I'm trying group a collection of data by it's State but I'm stuck on the correct way to do this:
FileStateInfoDto
public class FileStateInfoDto : EntityDto<int>
{
public string StateName { get; set; }
public int StateNumber { get; set; }
public int FilesByStateCount { get; set; }
}
FileGroupDto
public class FileGroupDto : EntityDto<int>
{
public int CaseId { get; set; }
public string Name { get; set; }
public string ResourceKey { get; set; }
public bool IsFolder { get; set; }
public int SequenceNumber { get; set; }
public IList<FileStateInfoDto> FileStateInfo { get; set; }
public IList<FileGroupDto> FileGroups { get; set; }
public IList<FileInfoDto> Files { get; set; }
}
Here is the code I have:
return await Context.FileGroups
.Include(g => g.Case).Include(g => g.FileGroups).Include(g => g.Files)
.Where(g => g.Id == fileGroupId &&
g.CaseId == caseId &&
g.Case.CaseState != CaseState.Approved &&
g.Case.CaseState != CaseState.Submitted &&
(g.Case.CaseState != CaseState.Draft || g.Case.CreatorUserId == userId))
.OrderBy(g => g.SequenceNumber)
.Select(g => new FileGroup
{
Id = g.Id,
CaseId = g.CaseId,
Name = g.Name,
ResourceKey = g.ResourceKey,
IsFolder = g.IsFolder,
SequenceNumber = g.SequenceNumber,
FileGroups = g.FileGroups,
FileStateInfo = g.Files.GroupBy(f => f.State), <-- My problem
Files = g.Files.Where(f => f.IsActive && f.State != FileApprovalState.Approved).Select(
f => new File
{
Id = f.Id,
CreationTime = f.CreationTime,
CreatorUserId = f.CreatorUserId,
Title = f.Title,
FileName = f.FileName,
URL = f.URL,
Size = f.Size,
KeepOnPortal = f.KeepOnPortal,
CreatorUserName = Context.Users.FirstOrDefault(u => u.Id == (f.CreatorUserId ?? 0)).UserName,
CreatorUserRole = Context.CasePersons.Where(p => p.CaseId == caseId && p.UserId == f.CreatorUserId).Take(1).Select(p => p.CaseRoleType.Title).FirstOrDefault()
}
).ToList()
}).FirstOrDefaultAsync();
I'm trying to figure out how I should write this line FileStateInfo = g.Files.GroupBy(f => f.State) so it will give the expected result as below.
FileStateInfo = [{"StateName":"Approved","StateNumber":1, "FilesByStateCount":22},
{"StateName":"NotApproved","StateNumber":2, "FilesByStateCount":11}]
The State in g.Files.GroupBy(f => f.State) is an enum that contains Approved and NotApproved
StateName = Name of the State.
StateNumber = The Integer assinged.
FilesByStateCount = The files count by this state.
I hope it's possible because I've been trying to make this for a few days now.
I've tried things like this Post
I have this linq query:
var investorData = from investor in db.Investors
join investorLine in db.InvestorStatementLines
on investor.InvestorID equals investorLine.InvestorID
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
InvestorName = investor.Name,
FundingDate = investor.FundingDate,
DueDate = investor.DueDate,
FundsCommitted = investor.FundsCommitted,
FundsInvested = investor.FundsInvested,
StatementLines =
db.InvestorStatementLines.Where(s => s.InvestorID == investor.InvestorID)
.Select(t => new InvestorStatementLineVM
{
Balance = t.Balance,
Credit = t.Credit,
Debit = t.Debit,
InvestorStatementLineDetails = t.Details,
Date = t.Date
}).ToList()
};
The viewmodel:
public class InvestorViewModel
{
public int InvestorId { get; set; }
public string InvestorName { get; set; }
public DateTime FundingDate { get; set; }
public DateTime? DueDate { get; set; }
public Decimal? FundsCommitted { get; set; }
public Decimal? FundsInvested { get; set; }
public List<InvestorStatementLineVM> StatementLines { get; set; }
}
What is happening is once I'm executing the query I'm getting 125 records, and that's the number of the StatementLines for that investor. So I'm getting 125 same records but I'm expecting one result which will have 125 statement lines in the inner list.
Is this query correct?
This is how you can do that with navigation properties
var investorData = from investor in db.Investors
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
InvestorName = investor.Name,
FundingDate = investor.FundingDate,
DueDate = investor.DueDate,
FundsCommitted = investor.FundsCommitted,
FundsInvested = investor.FundsInvested,
StatementLines = investor.InvestorStatementLines
.Select(t => new InvestorStatementLineVM
{
Balance = t.Balance,
Credit = t.Credit,
Debit = t.Debit,
InvestorStatementLineDetails = t.Details,
Date = t.Date
}).ToList()
};
Use GroupJoin instead of Join: (_join x in y on x.a equals y.a
into z_)
var investorData = from investor in db.Investors
join investorLine in db.InvestorStatementLines
on investor.InvestorID equals investorLine.InvestorID
into investorLine
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
InvestorName = investor.Name,
FundingDate = investor.FundingDate,
DueDate = investor.DueDate,
FundsCommitted = investor.FundsCommitted,
FundsInvested = investor.FundsInvested,
StatementLines = investorLine
.Select(t => new InvestorStatementLineVM
{
Balance = t.Balance,
Credit = t.Credit,
Debit = t.Debit,
InvestorStatementLineDetails = t.Details,
Date = t.Date
}).ToList()
};
Also instead of performing the sub-query just use the data from the join you just performed.
A better option, using entity framework, is using navigation properties and then you do not need to perform a join but you just have
InvestorStatementLines as a property of your investor.
To set the navigation properties:
public class InvestorViewModel
{
public int InvestorId { get; set; }
public string InvestorName { get; set; }
public DateTime FundingDate { get; set; }
public DateTime? DueDate { get; set; }
public Decimal? FundsCommitted { get; set; }
public Decimal? FundsInvested { get; set; }
public virtual ICollection<InvestorStatementLineVM> StatementLines { get; set; }
}
And the query will be as simple as:
var investorData = from investor in db.Investors
where investor.UserId == userId
select new InvestorViewModel()
{
InvestorId = investor.InvestorID,
....
StatementLines = investor.InvestorStatementLines.Select(....)
};
How do I filter an item from a list based on two different columns one being a number(smallest number) using LINQ C#?
public class Line
{
public int Id { get; set; }
public List<LineItem> LineItems { get; set; }
public Line()
{
LineItems = new List<LineItem> {
new LineItem{Num = 1, Name="i", Qty = 0, Active = false},
new LineItem{Num = 2, Name="j", Qty = 2,Active = false},
new LineItem{Num = 3, Name="k", Qty = 3,Active = false},
};
}
}
public class LineItem
{
public int Num { get; set; }
public string Name { get; set; }
public int Qty { get; set; }
public bool Active { get; set; }
}
I want to filter this list and get a LineItem based on Qty = 0 and smallest num value.
Try filtering by Qty == 0, sorting according to Num and keep the first one:
var lineItem = LineItems.Where(l => l.Qty == 0).OrderBy(l => l.Num).FirstOrDefault();
or just keep the first that Qty equals to 0 and Num equals to minimum possible:
var minNum = LineItems.Where(l => l.Qty == 0).Min(l => l.Num);
var lineItem = LineItems.FirstOrDefault(l => l.Qty == 0 && l.Num == minNum);
You could try to get the min value for Qty is 0 and order by Num in ascending mode, then taking the first item. For sample:
var item = LineItems.Where(x => x.Qty == 0).OrderBy(x => x.Num).First();
If you have your LineItem class implement IComparable<T>, then you can do something like this:
public class LineItem : IComparable<LineItem>
{
public int Num { get; set; }
public string Name { get; set; }
public int Qty { get; set; }
public bool Active { get; set; }
public int CompareTo(LineItem other)
{
if (other.Num > this.Num)
return -1;
else if (other.Num == this.Num)
return 0;
else
return 1;
}
}
then
var item = l.LineItems.Where(p => p.Qty == 0).Min();
i have tre tables T020_CLIENTI,T021_SITI,T520_REL_STRUMENTI_SITI that i would join and then distinct by T020.Ragione_sociale,T520.DA_DATA,T520.A_DATA but obtain as return parameters T020.Ragione_sociale,T020.id_cliente,T520.cod_stumento,T520.DA_DATA,T520.A_DATA
my tables are
public partial class T020_CLIENTI
{
public decimal ID_CLIENTE { get; set; }
public Nullable<decimal> ID_COMUNE { get; set; }
public Nullable<decimal> ID_CONSORZIO { get; set; }
public string COD_LINEA_ATTIVITA { get; set; }
}
public partial class T021_SITI
{
public decimal ID_SITO { get; set; }
public Nullable<decimal> ID_FORNITORE { get; set; }
public Nullable<decimal> ID_CLIENTE { get; set; }
}
public partial class T520_REL_STRUMENTI_SITI
{
public string COD_STUMENTO { get; set; }
public decimal ID_SITO { get; set; }
public System.DateTime DA_DATA { get; set; }
public System.DateTime A_DATA { get; set; }
}
my linq query is
using (var cont = DALProvider.CreateEntityContext())
{
var query =
from cliente in cont.T020_CLIENTI
from sito
in cont.T021_SITI
.Where(s => s.ID_CLIENTE == cliente.ID_CLIENTE)
.DefaultIfEmpty()
from relStrumenti
in cont.T520_REL_STRUMENTI_SITI
.Where(s => s.ID_SITO == sito.ID_SITO)
.DefaultIfEmpty()
select new
{
clienteRec = cliente,
sitoRec = sito,
relStrumentiRec = relStrumenti
};
if (!string.IsNullOrEmpty(aiFiltro.RAGIONE_SOCIALE))
query = query.Where(i => i.clienteRec.RAGIONE_SOCIALE.ToUpper().Contains(aiFiltro.RAGIONE_SOCIALE.ToUpper()));
var vRes = (from clienteDef in query
select new ClienteFiltrato
{
RAGIONE_SOCIALE = clienteDef.clienteRec.RAGIONE_SOCIALE,
ID_CLIENTE = clienteDef.clienteRec.ID_CLIENTE,
COD_STRUMENTO = clienteDef.relStrumentiRec.COD_STUMENTO,
DATA_DA = clienteDef.relStrumentiRec.DA_DATA,
DATA_A = clienteDef.relStrumentiRec.A_DATA
}) ;
return vRes.AsQueryable();
}
but in my linq query i don't know where i can insert distinct and input parameter (:pPOD) to obtain my linq that in oracle query is:
SELECT DISTINCT t020.ragione_sociale,
da_data,
a_data,
t020.id_Cliente,
:pPOD
FROM t020_clienti t020, t021_siti t021, T520_REL_STRUMENTI_SITI t520
WHERE t020.id_cliente = t021.id_cliente
AND t021.id_sito = t520.id_sito
AND (:pPOD is null or t520.cod_stumento = :pPOD)
ORDER BY da_data
where :pPOD is an input parameter that i could have set or not.
Try to add (s.COD_STUMENTO == pPod || pPod == null) to your Where clause, where you are filtering T520_REL_STRUMENTI_SITI entity. pPod should be a string variable.
Please have in mind that if you are using DefaultIfEmpty() in LINQ this will be translated to left join in SQL.
Modified query follows:
string pPod = null;
using (var cont = DALProvider.CreateEntityContext())
{
var query =
(from cliente in cont.T020_CLIENTI
from sito
in cont.T021_SITI
.Where(s => s.ID_CLIENTE == cliente.ID_CLIENTE)
.DefaultIfEmpty()
from relStrumenti
in cont.T520_REL_STRUMENTI_SITI
.Where(s => s.ID_SITO == sito.ID_SITO && (s.COD_STUMENTO == pPod || pPod == null))
.DefaultIfEmpty()
select new
{
clienteRec = cliente.Distinct(),
sitoRec = sito,
relStrumentiRec = relStrumenti
});
if (!string.IsNullOrEmpty(aiFiltro.RAGIONE_SOCIALE))
query = query.Where(i => i.clienteRec.RAGIONE_SOCIALE.ToUpper().Contains(aiFiltro.RAGIONE_SOCIALE.ToUpper()));
var vRes = (from clienteDef in query
select new ClienteFiltrato
{
RAGIONE_SOCIALE = clienteDef.clienteRec.RAGIONE_SOCIALE,
ID_CLIENTE = clienteDef.clienteRec.ID_CLIENTE,
COD_STRUMENTO = clienteDef.relStrumentiRec.COD_STUMENTO,
DATA_DA = clienteDef.relStrumentiRec.DA_DATA,
DATA_A = clienteDef.relStrumentiRec.A_DATA
}).Distinct() ;
return vRes.AsQueryable();
}
You can use:
string query =
((System.Data.Objects.ObjectQuery)query).ToTraceString();
This will show you the generated SQL from LINQ Queryable object.