LINQ association - Entity Framework - c#

I have three tables and I have used edmx designer to add associations between them. Below is how they are linked.
(table1) Loans - (table 2) Investor : Many to One relationship
(Table2) Investor - (Table3) InvestorInfo : One to Many relationship
I want to get [1] Total loans count sold to one investor, [2] Investor name and [3] investor's service fee which is stored in Table3 at idx = 2005 for each investor ("investor id & idx" is primary key of table3 - InvestorInfo table).
How do I do that in below query? I am forced to select 'FirstOrDefault()' to access any column in Table3 (See commented lines). If I use FirstOrDefualt, I get a record where idx = 1 and not 2005.
var loanPurchaseData = (from cd in entity.Table1
//where cd.Table2.Table3.Select(x => x.IDX == 2005)
//where cd.ULDD_SET_POOLS.ULDD_SET_POOLDT.FirstOrDefault().SORT_ID == 2005
group cd by new { cd.Table4.PurchaseDate, cd.Number } into grp
select new
{
investor = grp.FirstOrDefault().Investor,
no_of_loans = grp.Count(),
sort_id = grp.FirstOrDefault().Table2.Table3.FirstOrDefault().SORT_ID,
service_fee_rate = grp.FirstOrDefault().Table2.Table3.FirstOrDefault().DT_REAL_PERC_VALUE
}).ToList();

Your question is not very clear - I don't understand whether idx is in Table3 or Table1, and what you want to select, but I will assume you have a many-one-many schema, where an Investor has zero or more Loans & zero or more InvestorInfos. You want to get, say, all of the loans which join to investor info with idx = 2005. Correct me if I'm wrong, and correct your question if I'm right!
Starting with your InvestorInfo object, you know that there's only one Investor but there will be zero or more Loans.
// only one InvestorInfo for idx, but this isn't clear in your question
var investorInfo = context.InvestorInfos.SingleOrDefault(i => i.idx == 2005);
var loans = investorInfo.Investor.Loans;
The crux of your problem is that you cannot get 'the loan service fee' for an investor info. Why not? Because that investor has 5 loans. Which one do you want?
-- we can get the maximum, minimum, sum, etc...
var max = loans.Max(l => l.DT_REAL_PERC_VALUE);
var min = loans.Min(l => l.DT_REAL_PERC_VALUE);
var min = loans.Sum(l => l.DT_REAL_PERC_VALUE);
Again it's not clear what you are trying to do, nor what your data actually looks like, but in a one-many relationship you will necessarily have more than one of the 'many' side for each of the 'one' side.
To get the max, use the Max operator.
service_fee = grp.Max(l => l.Table2.Table3.Max(t => t.DT_REAL_PERC_VALUE))

Related

Get the id of the latest record in group by with linq

I'm trying to get the latest record for a group in linq but I want the id, not the date as sometimes the dates can be exactly the same as other records.
The following code gives me the key and the last date
var latestPositions = from bs in Scan
group bs by bs.Asset into op
select new
{
Asset = op.Key,
LastSeen = op.Max(x => x.LastSeen)
};
Returns something like this...
Asset LastSeen
BS1 2020-05-10
BS2 2020-07-10
Which is what I need, but I then need to get to the rest of the data from that table row, but if I join it on the two columns I can get multiple rows, is there a way for me to return the id column in the group by, so I can join on that?
Thanks
GroupBy cannot help here because of SQL limitation. But you can write workaround
var latestPositions = from bs in Scan
where !Scan.Any(s => s.Asset == bs.Asset && s.LastSeen > bs.LastSeen)
select bs;
But I have to mention that fastest way is using window functions which are not available in EF Core:
SELET
sc.Id
FROM (
SELECT
s.Id,
ROW_NUMBER() OVER (PARTITION BY s.Asset ORDER BY s.LastSeen DESC) AS RN
FROM Scan s
) sc
WHERE sc.RN == 1
But there is exists EF Core extension linq2db.EntityFrameworkCore which makes it possible via LINQ (I assume that Asset is just ID, not a navigation property)
var queryRn = from bs in Scan
select new
{
Entity = bs,
RN = Sql.Ext.RowNumber().Over()
.PartitionBy(bs.Asset).OrderByDesc(bs.LastSeen).ToValue()
};
// switch to alternative translator
queryRn = queryRn.ToLinqToDB();
var latestPositions = from q in queryRn
where q.RN == 1
select q.Entity;
I think I did what you wanted above and I wrote the full code on this link
If it's not what you want, can you write what you want a little more clearly.
var temp = from l in list
group l by l.Asset into lg
orderby lg.Key
select new { Asset = lg.Key, LastSeen = lg.Max(x => x.LastSeen), ID = lg.Where(x => x.LastSeen == lg.Max(y => y.LastSeen)).Single().ID };
So every Scan has a property Asset, a DateTime property LastSeen, and zero or more other properties.
Requirement: given a sequence of Scans, give me per Asset (all or some of the ) properties of the LastSeen Scan.
The following will do the trick:
var lastSeenScan = dbContext.Scans.GroupBy(scan => scan.Asset,
// parameter resultSelector: take every Asset, and all Scans that have this Asset value
// and order these Scans by descending value of lastSeen:
(asset, scansWithThisAssetValue) => scansWithThisAssetValue
.OrderByDescending(scan => scan.LastSeen)
// Select the scan properties that you plan to use.
// Not needed if you want the complete Scan
.Select(scan => new
{
Id = scan.Id,
Operator = scan.Operator,
...
})
// and keep only the First one, which is the Last seen one:
.FirstOrDefault();
In words: divide your table of of Scans into groups of scans that have the same value for property Asset. Order all scans in each group by descending value of LastSeen. This will make the Scan that has last been seen the first one.
From all scans in the group select only the properties that you plan to use, and take the first one.
Result: for every used Asset, you get the (selected properties of the) scan that has the highest value of LastSeen.

Conditional LINQ query on self-joining table with 2 sets of data

I am trying to figure out how to perform a conditional query on an Employees table to bring back all of their assigned Projects, but the caveat I don't quite understand on how to implement is that for every Employee, there is 0 to 1 EmployeeAssistant (self-joining entity). So when I select EmployeeID=2 and it has an EmployeeAssistantID=5, I would like to display all of the Projects for both of these individuals, i.e. the main Employee (EmployeeID=2) and their assistant (EmployeeID=5).
The tables look like:
Employees
- EmployeeID -- (Pkey)
- EmployeeAssistantID -- (Fkey to Employees.EmployeeID)
- other fields
-
Projects
- ProjectID -- (PKey)
- EmployeeID -- (Fkey to Employees.EmployeeID)
- other fields
I attempted the following in LINQ:
var projects = Projects.Include(proj => proj.Employee)
.Select(x => new
{
proj.ProjectID,
proj.ProjectName,
proj.Employee.Name
// ... not sure how to bring back another layer of projects for the EmployeeAssistant?
})
You can use the following Linq query to get the data you want:
var query = from e in Employees
join a in Employees on e.EmployeeAssistantID equals a.EmployeeID
where e.EmployeeID == 2
select new
{
EmployeeID = e.EmployeeID,
AssistantID = a.EmployeeID,
EmployeeProjects = Projects.Where(p => p.EmployeeID == e.EmployeeID),
AssistantProjects = Projects.Where(p => p.EmployeeID == a.EmployeeID)
};
The anonymous type returned by the query contains all of the data from both employee entities, as well as all of the Project data of each employee (some can be the same, others might differ).
I believe you mean to do the following:
var employeeIDs = new[] { myEmployee.EmployeeID, myEmployee.EmployeeAssistantID };
var projects = Projects.Where(p => employeeIds.Contains(p.EmployeeID));
This will grab all projects that both the employee and the assistant have done, given a previously grabbed Employee record (which I've called myEmployee).

Linq select Item where it is equal to ID in another table

I am not sure how possible this is but I have two tables and I want to grab a value from table 2 via the value of table 1.
Table 1 has the a Foreign Key called "rank" which is an int. Table 2 has a value called "name" which is a string. Now Table 1's "rank" correlates to Table 2's "ID".
So when I say
var result =
db.Table1.Select(x => new { x.name, x.rank }).ToList();
//Bob - 2
I really want to say something like
var result =
db.Table1.Select(x => new { x.name, Table2.rank.Where(ID == x.rank) }).ToList();
//Bob - Gold
I am still new to LINQ though and I am not sure how to get rank's string value from the other table within a query like this.
EDIT
Tables I am using and their relational values.
User: ID (PK), s1elo (FK to PastElos), champ (FK to ChampionList), elo (FK to EloList)
PastElo: ID (PK), Rank
ChampionList: ID (PK), name
EloList: ID (PK), Rank
Working example for Users and PastElo
var result =
db.Users.Join(db.PastEloes,
x => x.s1elo, y => y.ID, (x, y)
=> new { y.Rank, x.name, x.other_items_in_Users }).ToList();
Note: PastElo is PastEloe's due to EF making everything plural when I synced up my DB, thus why User is also Users, I think that is referred to as the "context".
You could try something like the following:
var result = db.Table1.Join(db.Table2,
x=>x.rank,
y=>y.ID,
(x,y) => new { x.rank, y.Name }).ToList();
In the above linq query we make a Join between the two tables, Table1 and Table2 based on the association and then we select that we want.
Another way you could try to write this query would be the following:
var result = (from t1 in db.Table1
join t2 in db.Table2
on t1.rank equals t2.ID
select new { t1.rank, t2.Name, }).ToList();
Another way to do this would be to include your Database relationships in your C# entities. You could use EntityRef here. See the following documentation:
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql/linq/how-to-map-database-relationships

Linq List Contains Method

Im newbie to linq and im using linq query to retrieve data from the table.My idea is to list all the cashsafes corresponding to a particular user and show it in dropdownlist.
The table structure is shown below
Table 1
cashsafeid cashsafename
1 cashsafe1
2 cashsafe2
3 cashsafe3
Table 2
Id UserId Cashsafeid
1 100 1,2,3
2 101 1,3
I've to get the cashsafename of a particular user say 100.How can i achieve it
The below code is the one i've tried but am stuck
List<Cashsafe> cashsafes=(from c in db.Table 1
where c.CashsafeId contains() )--Cannot go further
You store User's Cachsafeid column in very inefficient way - it doesn't allow to generate efficient SQL for LINQ provider. So the following solution has bad performance - if you care about that - change your table structure.
var user = db.Table2.Single(u => u.UserId == 100);
var cachfeIds = user.Cashsafeid.Split(',').Select(int.Parse).ToArray();
var cachefes = db.Table1.Where(c => cachfeIds.Contains(c.Id)).ToList();
Basically you need to join to tables, but foreign key is "virtual" - it is only in your mind. To retrieve foreign key values we must split the Cachsafeid column's value of every user to retrieve linked cachefes. And only then retrieve the cachefes with separate request (I think LINQ will retrieve all values from table and the execute Where part in C# code).
if you have no idea of join you can use
int x = 0;
List<int> Users = db.table2.FirstOrDefault(m => m.UserId == 100).Cashsafeid.Split(',').ToList().Where(str => int.TryParse(str, out x)).Select(str => x).ToList(); ;
var content = db.table1.Where(m => Users.Contains(m.cashsafeid)).ToList();

Get the next and previous sql row by Id and Name, EF?

Assume we have the following data in a SQL Server table (sorted by name) :
Id Name LName
-------------------------
5 Abbas Dayyan
3 Mohammad KD
4 Nima Ahmad
1 Omid Zangene
2 Pedram Ahmadi
we have an Id query string and we wanna get the next and previous row (if exists) from Id.
e.g :
the Id query string is 4, so we wanna get Mohammad KD as previous row and Omid Zangene as next row.
Could you please guide me how can do it with LINQ to Entity Framework.
Edit:
In practice the number of table rows is around 1 million.
Table rows didn't sort by Name by default, wa need to sort them by Name for the result.
How about this?
var result = (from person in db.People
where person.Id == id
let ordered = db.People.OrderBy(p => p.Name)
let reversed = db.People.OrderByDescending(p => p.Name)
let previous = reversed.SkipWhile(p => p.Id != id).Skip(1).FirstOrDefault()
let next = ordered.SkipWhile(p => p.Id != id).Skip(1).FirstOrDefault()
select new { previous, next }).First();
Edit: Took the new specifications into account.
Edit 2: Modified the code to not use LastOrDefault, which doesn't work with LINQ to Entities.
Try this:
int id = 4;
var list = (from x in ctx.Table
where x.id >= id - 1 && x.id <= id + 1
select x).OrderBy(o -> o.name).ToList();
edit: this will return elements with ID 3,4 and 5. I dont know if you are actually ordering the table in the database by name, but if you are i think it would be easier to just use a loop instead of using linq to get the previous and next element.
update: sorted the result by name

Categories