Get the latest record in join linq c# - c#

I have this query
return from fitUpExaminationDetails in _ctx.FitUpExaminationDetails
where fitUpExaminationDetails.FitUpExaminationId == Id
join joint in _ctx.Joints on fitUpExaminationDetails.JointId equals joint.Id
join line in _ctx.Lines on joint.LineId equals line.Id
join fileIsoManager in _ctx.FileIsoManagers on line.Id equals fileIsoManager.LineId
select new ViewDomainClass.QualityControl.Report.ViewFitupExaminationReport
{
HeatNumber1 = fitUpExaminationDetails.HeatNumber1,
HeatNumber2 = fitUpExaminationDetails.HeatNumber2,
JointNumber = joint.JointNumber,
LineNumber = line.LineNumber,
Revision = fileIsoManager.Revision,
};
My line tables can have multi fileIsoManager records.So in the select statement i mean in this part Revision = fileIsoManager.Revision i want to show the latest record in fileIsoManager table .how can i change this query to do that ?

Ok, since you didn't provide much details, i've wrote a few classes to show you how to get your latest revision for each line. Pay attention to the "main" function.
// These are a representation of your classes :
public class Line
{
public int LineId { get; set; }
public string SomeValue { get; set;}
}
public class Revision
{
public int RevisionId { get; set; }
public int LineId { get; set;}
}
void Main()
{
// generating some data so we can test the query.
var lines = new List<Line>() {
new Line() { LineId = 1, SomeValue = "Allo!" }
};
var revisions = new List<Revision>() {
new Revision() { LineId = 1, RevisionId = 1 },
new Revision() { LineId = 1, RevisionId = 2 },
new Revision() { LineId = 1, RevisionId = 3 }
};
var result = (
from line in lines
join revision in revisions on line.LineId equals revision.LineId
group revision by line into grp
select new
{
Line = grp.Key,
LastRevision = grp.OrderByDescending(rev => rev.RevisionId).FirstOrDefault()
}
).ToList();
}
So, basically, you need to group your revisions by line. In the select part, you can then get the latest revision for each line.
In this example, result will contain a dynamic object containing the line id=1 and the revision id = 3.
Going back to your query, it should look like this to get a similar result :
return
from fitUpExaminationDetails in _ctx.FitUpExaminationDetails
where fitUpExaminationDetails.FitUpExaminationId == Id
join joint in _ctx.Joints on fitUpExaminationDetails.JointId equals joint.Id
join line in _ctx.Lines on joint.LineId equals line.Id
join fileIsoManager in _ctx.FileIsoManagers on line.Id equals fileIsoManager.LineId
group fileIsoManager by new { fitUpExaminationDetails, joint, line} into grp
select new ViewDomainClass.QualityControl.Report.ViewFitupExaminationReport
{
HeatNumber1 = grp.Key.fitUpExaminationDetails.HeatNumber1,
HeatNumber2 = grp.Key.fitUpExaminationDetails.HeatNumber2,
JointNumber = grp.Key.joint.JointNumber,
LineNumber = grp.Key.line.LineNumber,
Revision = grp.OrderByDescending(fileIsoMgr => gileIsoMgr.Id).FirstOrDefault()
};

Related

Left outer join to include null values linq lambda

I have one table in database named Balance and a list of dates as follows:
List<string> allDates = { "2021-01-02", "2021-01-03", "2021-01-04" }
Balance table:
Id, Amount, BalanceDate
1, 233, "2021-01-02"
2, 442, "2021-01-03
I need to fetch the records in Balance table with amount 0 for the missing dates. For example:
233, "2021-01-02"
442, "2021-01-03"
0, "2021-01-04"
I have tried the following:
balnces.GroupJoin(allDates,
balance => balance.Date,
d => d,
(balance, d) => balance);
But the records are still the same (only the ones in the balance table)
Given a data structure from database:
private class balance
{
public int id { get; set; }
public double amount { get; set; }
public string date { get; set; }
}
You get your data as you want (this is only a mock-up)
List<string> allDates = new List<string> { "2021-01-02", "2021-01-03", "2021-01-04" };
List<balance> balances = new List<balance>();
balances.Add(new balance { id = 1, amount = 233 , date = "2021-01-02" });
balances.Add(new balance { id = 2, amount = 442, date = "2021-01-03" });
you can get your desired result this way:
List<balance> result = allDates.Select(d=>
new balance {
amount =
balances.Any(s=> s.date == d)?
balances.FirstOrDefault(s => s.date == d).amount:0,
date = d
}).ToList();
If your default contains a 0 in amount instead a null, you can skip the .Any check
Assumption
Balance query had been materialized and data are returned from the database.
Solution 1: With .DefaultIfEmpty()
using System.Linq;
var result = (from a in allDates
join b in balances on a equals b.Date.ToString("yyyy-MM-dd") into ab
from b in ab.DefaultIfEmpty()
select new { Date = a, Amount = b != null ? b.Amount : 0 }
).ToList();
Sample Program for Solution 1
Solution 2: With .ToLookup()
var lookup = balances.ToLookup(x => x.Date.ToString("yyyy-MM-dd"));
var result = (from a in allDates
select new
{
Date = a,
Amount = lookup[a] != null && lookup[a].Count() > 0 ? lookup[a].First().Amount : 0
}
).ToList();
Sample Program for Solution 2

C# EntityFramework 6.0 Querying Decimal > 0 Gives No Result

So I have set the model here:
public class Stock
{
[Key]
//[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid StockId { get; set; } = Guid.NewGuid();
.....
public decimal Remaining { get; set; }
}
Also with fluent mapping:
protected override void OnModelCreating(DbModelBuilder mb)
{
base.OnModelCreating(mb);
mb.HasDefaultSchema("dbo");
mb.Entity<Stock>().Property(m => m.Remaining).HasPrecision(16, 3);
....
}
So the precision is 3 decimal points.
It is so weird when I querying with linq:
IQueryable<StockDetail> stocks = (from s in db.Stocks .... where s.Remaining > 0);
stocks.Any() --> is false;
The where s.Remaining > 0 is the problem, how to fix this?
EDIT
The actual query:
IQueryable<StockDetail> stocks = (from s in db.Stocks
join items in db.Items on s.Item equals items.ItemId
join types in db.ItemTypes on s.ItemType equals types.ItemTypeId
join colors in db.ItemColors on s.ItemColor equals colors.ItemColorId
join units in db.ItemUnits on s.Unit equals units.ItemUnitId
join buyers in db.Buyers on new { Buyer = s.Buyer } equals new { Buyer = (Guid?)buyers.BuyerId } into temp2
from buyers in temp2.DefaultIfEmpty(null)
join suppliers in db.Suppliers.DefaultIfEmpty() on new { Supplier = s.Supplier } equals new { Supplier = (Guid?)suppliers.SupplierId } into temp3
from suppliers in temp3.DefaultIfEmpty(null)
join op in db.UserProfiles on s.Operator equals op.UserId
join curr in db.Currencies on s.Currency equals curr.CurrencyId
let sales = db.Sales.Where(m => m.SId == s.Sales).FirstOrDefault()
join parent in db.Stocks on s.Parent equals parent.StockId
where s.Remaining > 0
select new StockDetail()
{
Buyer = buyers != null ? (Guid?)buyers.BuyerId : null,
BuyerName = buyers != null ? buyers.Name : null,
Supplier = suppliers.SupplierId,
SupplierName = suppliers.Name,
Code = s.Code,
Color = colors.ItemColorId,
ColorCode = colors.Code,
ColorName = colors.Color,
DateCreated = s.DateCreated,
Gramation = s.Gramation,
Item = items.ItemId,
ItemName = items.Name,
LastEdited = s.LastEdited,
Operator = op.UserId,
OperatorName = op.UserName,
PO = s.PO,
Remaining = s.Remaining,
SC = s.SC,
Qty = s.Qty,
Setting = s.ItemSetting,
StockId = s.StockId,
Type = types.ItemTypeId,
TypeName = types.Type,
Unit = units.ItemUnitId,
UnitName = units.Unit,
Lot = s.Lot,
AvgPrice = s.AvgPrice,
Currency = curr.CurrencyName,
CurrencyId = s.Currency,
Note = s.Note,
Purchase = s.Purchase,
Sales = s.Sales,
POIn = s.POIn,
FromFactory = s.FromFactory,
OutDeliveryNo = sales != null ? sales.InvoiceNo : "",
Spec = s.Spec,
DesignCode = s.DesignCode
});

LINQ left join, group by and Count generates wrong result

I'm struggling with linq (left join - group - count). Please help me.
Below is my code and it gives me this result.
Geography 2
Economy 1
Biology 1
I'm expecting this...
Geography 2
Economy 1
Biology 0
How can I fix it?
class Department
{
public int DNO { get; set; }
public string DeptName { get; set; }
}
class Student
{
public string Name { get; set; }
public int DNO { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Department> departments = new List<Department>
{
new Department {DNO=1, DeptName="Geography"},
new Department {DNO=2, DeptName="Economy"},
new Department {DNO=3, DeptName="Biology"}
};
List<Student> students = new List<Student>
{
new Student {Name="Peter", DNO=2},
new Student {Name="Paul", DNO=1},
new Student {Name="Mary", DNO=1},
};
var query = from dp in departments
join st in students on dp.DNO equals st.DNO into gst
from st2 in gst.DefaultIfEmpty()
group st2 by dp.DeptName into g
select new
{
DName = g.Key,
Count = g.Count()
};
foreach (var st in query)
{
Console.WriteLine("{0} \t{1}", st.DName, st.Count);
}
}
}
var query =
from department in departments
join student in students on department.DNO equals student.DNO into gst
select new
{
DepartmentName = department.DeptName,
Count = gst.Count()
};
I don't think any grouping is required for answering your question.
You only want to know 2 things:
- name of department
- number of students per department
By using the 'join' and 'into' you're putting the results of the join in the temp identifier gst. You only have to count the number of results in the gst.
var query = from dp in departments
from st in students.Where(stud => stud.DNO == dp.DNO).DefaultIfEmpty()
group st by dp.DeptName into g
select new
{
DName = g.Key,
Count = g.Count(x => x!=null)
};
You want to group the students by the department name but you want the count to filter out null students. I did change the join syntax slightly although that really does not matter to much.
Here is a working fiddle
Well, see what #Danny said in his answer, it's the best and cleanest fix for this case. By the way, you could also rewrite it to the lambda syntax:
var query = departments.GroupJoin(students,
dp => dp.DNO, st => st.DNO,
(dept,studs) => new
{
DName = dept.DNO,
Count = studs.Count()
});
I find this syntax much more predictable in results, and often, shorter.
BTW: .GroupJoin is effectively a "left join", and .Join is "inner join". Be careful to not mistake one for another.
And my answer is similar to #Igor
var query = from dp in departments
join st in students on dp.DNO equals st.DNO into gst
from st2 in gst.DefaultIfEmpty()
group st2 by dp.DeptName into g
select new
{
DName = g.Key,
Count = g.Count(std => std != null)
};
g.Count(std => std != null) is only one change you should take.

LINQ Left Join with group by and count

I am having fun with LINQ query and kind of stuck at figuring out the correct method to get Count of associated entries.
I have below LINQ query and
var result = (from OR in orders
join OE in order_entries on OR.id equals OE.order_id into temp
from LOE in temp.DefaultIfEmpty()
group LOE by new {OR.user_id, OR.site } into g
select new {
col1 = g.Key.user_id,
col2 = g.Key.site,
count = g.Count() ,
cost = g.Sum( oe => oe.cost)
}
);
this turns to
SELECT
1 AS [C1],
[GroupBy1].[K1] AS [user_id],
[GroupBy1].[K2] AS [site],
[GroupBy1].[A1] AS [C2],
[GroupBy1].[A2] AS [C3]
FROM ( SELECT
[Extent1].[user_id] AS [K1],
[Extent1].[site] AS [K2],
COUNT(1) AS [A1],
SUM([Extent2].[cost]) AS [A2]
FROM [dbo].[orders] AS [Extent1]
LEFT OUTER JOIN [dbo].[order_entries] AS [Extent2] ON [Extent1].[id] = [Extent2].[order_id]
GROUP BY [Extent1].[user_id], [Extent1].[site]
) AS [GroupBy1]
What I am trying to acheive here is replace Count(1) with Count([Extent2].[id]) so in case when there is no entries associated with the order I want to show 0 instead of 1.
Can someone please help me with updating the LINQ query to achieve this ?
UPDATE :
replace with below will return the outcome for what I wanted but this also turns my sql query to perform slower..
g.Where(i => i.orders != null).Count(),
The simplest way is to use subqueries:
var qry = from o in orders
select new {
oid = o.ID,
uid = o.UserId,
site = o.Site,
count = order_entries.Where(oe=>oe.OrderId == o.ID).Count(),
cost = order_entries.Where(oe=>oe.OrderId == o.ID).Sum(oe=>oe.Cost)
};
But if you would like to join two data sets, use this:
var qry = (from o in orders join oe in order_entries on o.ID equals oe.OrderId into grp
from g in grp.DefaultIfEmpty()
select new{
oid = o.ID,
uid = o.UserId,
site = o.Site,
count = grp.Count(),
cost = grp.Sum(e=>e.Cost)
}).Distinct();
I strongly believe in it that second query could be writen in simplest way by using group statement.
Here is a complete LinqPad sample:
void Main()
{
List<TOrder> orders = new List<TOrder>{
new TOrder(1, 1, "Site1"),
new TOrder(2, 1, "Site1"),
new TOrder(3, 2, "Site2"),
new TOrder(4, 2, "Site2"),
new TOrder(5, 3, "Site3")
};
List<TOrderEntry> order_entries = new List<TOrderEntry>{
new TOrderEntry(1, 1, 5.5),
new TOrderEntry(2, 1, 6.2),
new TOrderEntry(3, 1, 4.9),
new TOrderEntry(4, 1, 55.15),
new TOrderEntry(5, 1, 0.97),
new TOrderEntry(6, 2, 2.23),
new TOrderEntry(7, 2, 95.44),
new TOrderEntry(8, 2, 3.88),
new TOrderEntry(9, 2, 7.77),
new TOrderEntry(10, 3, 25.23),
new TOrderEntry(11, 3, 31.13),
new TOrderEntry(12, 4, 41.14)
};
// var qry = from o in orders
// select new {
// oid = o.ID,
// uid = o.UserId,
// site = o.Site,
// count = order_entries.Where(oe=>oe.OrderId == o.ID).Count(),
// cost = order_entries.Where(oe=>oe.OrderId == o.ID).Sum(oe=>oe.Cost)
// };
// qry.Dump();
var qry = (from o in orders join oe in order_entries on o.ID equals oe.OrderId into grp
from g in grp.DefaultIfEmpty()
//group g by g into ggg
select new{
oid = o.ID,
uid = o.UserId,
site = o.Site,
count = grp.Count(),
cost = grp.Sum(e=>e.Cost)
}).Distinct();
qry.Dump();
}
// Define other methods and classes here
class TOrder
{
private int iid =0;
private int uid =0;
private string ssite=string.Empty;
public TOrder(int _id, int _uid, string _site)
{
iid = _id;
uid = _uid;
ssite = _site;
}
public int ID
{
get{return iid;}
set{iid = value;}
}
public int UserId
{
get{return uid;}
set{uid = value;}
}
public string Site
{
get{return ssite;}
set{ssite = value;}
}
}
class TOrderEntry
{
private int iid = 0;
private int oid = 0;
private double dcost = .0;
public TOrderEntry(int _iid, int _oid, double _cost)
{
iid = _iid;
oid = _oid;
dcost = _cost;
}
public int EntryId
{
get{return iid;}
set{iid = value;}
}
public int OrderId
{
get{return oid;}
set{oid = value;}
}
public double Cost
{
get{return dcost;}
set{dcost = value;}
}
}

Extensible relational division in LINQ

In this example class IcdPatient represents a many-to-many relationship between a Patient table (not shown in this example) and a lookup table Icd.
public class IcdPatient
{
public int PatientId { get; set; }
public int ConditionCode { get; set; }
public static List<IcdPatient> GetIcdPatientList()
{
return new List<IcdPatient>()
{
new IcdPatient { PatientId = 100, ConditionCode = 111 },
new IcdPatient { PatientId = 100, ConditionCode = 222 },
new IcdPatient { PatientId = 200, ConditionCode = 111 },
new IcdPatient { PatientId = 200, ConditionCode = 222 },
new IcdPatient { PatientId = 3, ConditionCode = 222 },
};
}
}
public class Icd
{
public int ConditionCode { get; set; }
public string ConditionName { get; set; }
public static List<Icd> GetIcdList()
{
return new List<Icd>()
{
new Icd() { ConditionCode =111, ConditionName ="Condition 1"},
new Icd() { ConditionCode =222, ConditionName ="Condition 2"},
};
}
}
I would like for the user to be able to enter as many conditions as they want, and get a LINQ object back that tells them how many PatientIds satisfy that query. I've come up with:
List<string> stringFilteredList = new List<string> { "Condition 1", "Condition 2" };
List<int> filteringList = new List<int> { 111,222 };
var manyToMany = IcdPatient.GetIcdPatientList();
var icdList = Icd.GetIcdList();
/*Working method without joining on the lookup table*/
var grouped = from m in manyToMany
group m by m.PatientId into g
where g.Count() == filteringList.Distinct().Count()
select new
{
PatientId = g.Key,
Count = g.Count()
};
/*End*/
foreach (var item in grouped)
{
Console.WriteLine(item.PatientId);
}
Let's say that IcdPatient has a composite primary key on both fields, so we know that each row is unique. If we find the distinct number of entries in filteringList and do a count on the number of times a PatientId shows up, that means we've found all the people who have all conditions. Because the codes can be esoteric, I would like to do something like
let the user table in the ConditionName in type Icd and perform the same operation. I've not used LINQ this way a lot and I've gathered:
List<int> filteringList = new List<int> { 111,222 };
List<string> stringFilteredList= new List<string>{"Condition 1","Condition 2" };
filteringList.Distinct();
var manyToMany = IcdPatient.GetIcdPatientList();
var icdList = Icd.GetIcdList();
/*Working method without joining on the lookup table*/
var grouped = from m in manyToMany
join i in icdList on
m.ConditionCode equals i.ConditionCode
//group m by m.PatientId into g
group new {m,i} by new { m.ConditionCode }into g
where g.Count() == filteringList.Distinct().Count()
select new
{
Condition = g.Key.ConditionCode
};
/*End*/
but can't get anything to work. This is essentially a join on top of my first query, but I'm not getting what I need to group on.
You don't need to group anything in this case, just use a join and a contains:
List<string> stringFilteredList= new List<string>{"Condition 1","Condition 2" };
var patients =
from icd in Icd.GetIcdList()
join patient in IcdPatient.GetIcdPatientList() on icd.ConditionCode equals patient.ConditionCode
where stringFilteredList.Contains(icd.ConditionName)
select patient.PatientId;
Let's say that IcdPatient has a composite primary key on both fields, so we know that each row is unique. If we find the distinct number of entries in filteringList and do a count on the number of times a PatientId shows up, that means we've found all the people who have all conditions. Because the codes can be esoteric, I would like to do something like let the user table in the ConditionName in type Icd and perform the same operation.
I believe you're asking:
Given a list of ConditionCodes, return a list of PatientIds where every patient has every condition in the list.
In that case, the easiest thing to do is group your IcdPatients table by Id, so that we can tell every condition that a patient has by looking once. Then we check that every ConditionCode we're looking for is in the group. In code, that looks like:
var result = IcdPatient.GetIcdPatientList()
// group up all the objects with the same PatientId
.GroupBy(patient => patient.PatientId)
// gather the information we care about into a single object of type {int, List<int>}
.Select(patients => new {Id = patients.Key,
Conditions = patients.Select(p => p.ConditionCode)})
// get rid of the patients without every condition
.Where(conditionsByPatient =>
conditionsByPatient.Conditions.All(condition => filteringList.Contains(condition)))
.Select(conditionsByPatient => conditionsByPatient.Id);
In query format, that looks like:
var groupedInfo = from patient in IcdPatient.GetIcdPatientList()
group patient by patient.PatientId
into patients
select new { Id = patients.Key,
Conditions = patients.Select(patient => patient.ConditionCode) };
var resultAlt = from g in groupedInfo
where g.Conditions.All(condition => filteringList.Contains(condition))
select g.Id;
Edit: If you'd also like to let your user specify the ConditionName rather than the ConditionId then simply convert from one to the other, storing the result in filteringList, like so:
var conditionNames = // some list of names from the user
var filteringList = Icd.GetIcdList().Where(icd => conditionNames.Contains(icd.ConditionName))
.Select(icd => icd.ConditionCode);

Categories

Resources