I am implementing a controller and I need to get all staff members which have a certain RiskTypeID, which will be selected by the user when they click on Navigation Item.
Here is how I would create the joins in SQL
SQL
Select
RTHG.RiskTypeID,
SM.FullName
From RiskTypeHasGroup RTHG
Inner join RiskGroup RG On RTHG.RiskGroupID = RG.ID
Inner join RiskGroupHasGroupMembers RGHGM ON RG.ID = RGHGM.RiskGroupID
Inner Join GroupMember GM ON RGHGM.GroupMemberID = GM.ID
Inner Join GroupMemberHasStaffMember GMHSM ON GM.ID = GMHSM.GroupMemberID
Inner Join StaffMember SM ON GMHSM.StaffMemberID = SM.ID
Where RTHG.RiskTypeID = 1
I’ve pulled back data before using Linq and lambda but only using simple expressions, I now need to be able to make a call which will bring back the same data as the sql outlined above, I’ve searched online but can’t find anything similar to my requirement.
Here is my Controller, I placed comments inside as guidance
Controller
public ActionResult ViewRiskTypes(int SelectedRiskTypeID)
{
var RiskTypes = _DBContext.RiskTypes.ToList(); // Get all of the current items held in RiskTypes tables, store them as a List in Var RiskTypes
var ViewModel = new List<RiskTypeWithDetails>(); // Create colletion which holds instances of RiskTypeWithDetails and pass them to the ViewModel
var Details = new RiskTypeWithDetails(); // Create a new instance of RiskType with details and store the instance in var Details
foreach (var RiskType in RiskTypes) // Loop through each Item held in var RiskTypes
{
Details.RiskTypes.Add(new RiskTypesItem { ID = RiskType.ID, Description = RiskType.Description }); // assign each items ID & Description to the same feilds in a new
// instance of RiskTypeItems (which is a property of RiskTypeWithDetails)
}
foreach (var RiskType in RiskTypes) // Loop through each item in RiskTypes
{
if (RiskType.ID == SelectedRiskTypeID) // Check Item ID matches SelectedRiskTypeID value
{
//var Details = new RiskTypeWithDetails();
Details.RiskTypeDescription = RiskType.Description; //assign the Risk type Descripton to RiskTypeWithDetails RiskTypeDescription Property
Details.RiskDetails = _DBContext
.RiskTypeHasGroups
//.GroupMemberTypeHasGroupMembers
.Where(r => r.RiskTypeID == SelectedRiskTypeID) // Where RiskTypeId matches Selected ID bring back following data from Db
.Select(r => new RiskDetails
{
RiskGroupDescription = r.RiskGroup.Description,
GroupMembers = r.RiskGroup.RiskGroupHasGroupMembers
.Select(v => v.GroupMember).ToList(),
//StaffMembers = r.RiskGroup.RiskTypeHasGroups
// .Join(r.RiskGroup.RiskTypeHasGroups,
// a => a.RiskGroupID , b => b.RiskGroup.ID,
// (a, b) => new {a, b})
// .Join(r.RiskGroup.RiskGroupHasGroupMembers,
// c => c.) // Dosent join as I would expect... no idea what to do here
}).ToList();
ViewModel.Add(Details); //Add all data retrieved to the ViewModel (This creates one item in the collection)
}
}
return View(ViewModel);
}
As you will see I want to get all Staff Members with a match for the selected RiskTypeID. I need some assistance in converting the above SQL to work within my controller as a lambda expression
Thanks in advance
You were on the right track with your commented out code! For starters, LINQ has two different sytaxes: query and method chain. You were using the method chain syntax and it can get really unmaintainable really quickly.
For an instance like this, query syntax is where it's at.
Here's the result:
from rhtg in _dbContext.RiskTypeHasGroup
where rhtg.RiskTypeID == 1
join rg in _dbContext.RiskGroup
on rhtg.RiskGroupID equals rg.ID
join rghgm in _dbContext.RiskGroupHasGroupMembers
on rg.ID equals rhtg.ID
join gm in _dbContext.GroupMember
on rg.ID equals gm.ID
join gmhsm in _dbContext.GroupMemberHasStaffMember
on gm.ID equals gmhsm.GroupMemberID
join sm in _dbContext.StaffMember
on gmhsm.StaffMemberID equals sm.ID
select new
{
rhtg.RiskTypeId,
sm.FullName
};
Do note, that I used .Net conventions for the different variables.
Here's some documentation on the query syntax:
https://msdn.microsoft.com/en-us/library/gg509017.aspx
You can write the exact same query in linq as follows:
var query = (from RTHG in _DBContext.RiskTypeHasGroup RTHG
join RG in _DBContext.RiskGroup on RTHG.RiskGroupID equals RG.ID
join RGHGM in _DBContext.RiskGroupHasGroupMembers on RG.ID equals RGHGM.RiskGroupID
join GM in _DBContext.GroupMember on RGHGM.GroupMemberID = GM.ID
join GMHSM in _DBContext.GroupMemberHasStaffMember on GM.ID equals GMHSM.GroupMemberID
join SM in _DBContext.StaffMember on GMHSM.StaffMemberID equals SM.ID
where RTHG.RiskTypeID == 1
select new {RTHG.RiskTypeID,SM.FullName});
Related
I'm having problem translating a query to LINQ in C# this is my query
select PDF.Name,PDF.Name
from PDF inner join PC
on PDF.Id=PC.Ref_PDF
having pc.Ref_Customer=_id
you should know that _id is something that i send to my method so I can find something with it
so far I did this which I don't think would work(cuase lot's of errors poped up)
Invalid expression term 'select'
and
Expected contextual keyword 'equals'
both at end of here join p in Context.PDFs on c.Ref_PDF
internal List<EF_Model.PDF> Customers_File(int _id)
{
using (var Context = new EF_Model.CoolerEntities())
{
var q = from c in Context.PCs
where c.Ref_Customer == _id
join p in Context.PDFs on c.Ref_PDF
select new { c.PDF.Id, c.PDF.Name, c.PDF.File };
return q;
}
}
How can we make it into a linq statement?
Fix the syntax for the query
List<EF_Model.PDF> Customers_File(int _id) {
using (var Context = new EF_Model.CoolerEntities()) {
var q = from c in Context.PCs
join p in Context.PDFs on c.Ref_PDF equals p.Id
where c.Ref_Customer == _id
select new EF_Model.PDF { Id = c.PDF.Id, Name = c.PDF.Name, File = c.PDF.File };
return q.ToList();
}
}
and the method expects to return a list so use the ToList() on the query when returning from the method.
UPDATE:
If the intention was just to return the PDF model then no need to create the anonymous object just return c.PDF
List<EF_Model.PDF> Customers_File(int _id) {
using (var Context = new EF_Model.CoolerEntities()) {
var q = from c in Context.PCs
join p in Context.PDFs on c.Ref_PDF equals p.Id
where c.Ref_Customer == _id
select c.PDF;
return q.ToList();
}
}
This should do the job for you
from pc in context.PCs where pc.Ref_Customer == _id
join p in context.PDFs on pc.Ref_PDF equals p.Ref_PDF
select new {pc.PDF.Id, pc.PDF.Name, pc.PDF.File }
Probably when you said errors, I assume you saw synactical errors
If you set up a navigation property, the query is:
var q =
from pc in Context.PCs
where pc.Ref_Customer == _id
from pdf in pc.PDFs
select pdf;
If you don't:
var q =
from pc in Context.PCs
where pc.Ref_Customer == _id
join pdf in Context.PDFs on pc.Ref_PDF equals pdf.Id
select pdf;
The main thing to know about the join syntax, it has the form
" join (a) in (b) on (c) equals (d) "
(a): the new range variable for a member of (b)
(b): the source of items you are joining to - the right side of the join.
(c): an expression in which the item from the left side of the join is in scope.
(d): an expression in which the item from the right side of the join is in scope - (a).
i am trying to access data from multiple model in repository pattern using Ling subquery. but when i try to access data in inner query using .GetQueryable() i receive following error.
ERRORLINQ to Entities does not recognize the method 'System.Collections.Generic.List1[<>f__AnonymousType1702[System.Int32,System.Int32]] ToList[<>f__AnonymousType1702](System.Collections.Generic.IEnumerable1[<>f__AnonymousType170`2[System.Int32,System.Int32]])' method, and this method cannot be translated into a store expression.
var query = (from i in
(from student in _tblStudents.GetQueryable()
join Sections in _tblStudentsSections.GetQueryable() on student.StudentID equals Sections.StudentID
join Class in _tblClasses.GetQueryable() on Sections.ClassID equals Class.ClassID
join Program in _tblPrograms.GetQueryable() on Class.ProgramID equals Program.ProgramID
//where student.IsForeign == false
select new
{
ProgramID = Program.ProgramID,
program = Program.ProgramName,
ClassIDs = Sections.ClassID,
TotalSeats = Program.NoOfAdmissions,
IsForeign = student.IsForeign
})
group i by new { i.ProgramID, i.IsForeign, i.TotalSeats, i.program } into grp
select new AdmissionSummaryReportModel
{
program = grp.Key.program,
TotalSeats = grp.Key.TotalSeats,
//SeatsFilled = grp.Select(m => m.ClassIDs).Count(),
AvailableForeignSeats = 22,
SeatsFilled = (int)(from student in _tblStudents.GetQueryable()
join StudentSections in _tblStudentsSections.GetQueryable() on student.StudentID equals StudentSections.StudentID
join Class in _tblClasses.GetQueryable() on StudentSections.ClassID equals Class.ClassID
join Program in _tblPrograms.GetQueryable() on Class.ProgramID equals Program.ProgramID
where student.IsForeign == false && Program.ProgramID == grp.Key.ProgramID
select new
{
StudentSections.ClassID
}).ToList().Count(),
ForeignSeatsFilled = (int)(from student in _tblStudents.GetQueryable()
join StudentSections in _tblStudentsSections.GetQueryable() on student.StudentID equals StudentSections.StudentID
join Class in _tblClasses.GetQueryable() on StudentSections.ClassID equals Class.ClassID
join Program in _tblPrograms.GetQueryable() on Class.ProgramID equals Program.ProgramID
where student.IsForeign && Program.ProgramID == grp.Key.ProgramID
select new
{
StudentSections.ClassID
}).ToList().Count()
}).ToList();
how to overcome this error with .GetQueryable() or provide me any alternative mean
The problem is using the ToList function in your LINQ query, that is not what you want to do, since that cannot be translated to a proper SQL query. You want to use ToList only outside the actual LINQ query. To get the count inside, use the LINQ Count function instead, for instance :
select new
{
StudentSections.ClassID
}).Count()
Queryable gets translated into sql query at runtime, in the subquery you are parsing with int which is known to c# compiler not to query translation.
Either user IEnumerable or remove int as count will return the int.
er have the following query in linq...
Whenever I try to run it I get a No comparison operator for type System.Int[] exception.
It's got something to do with the dictionary I am sure, but I don't understand why this isn't valid and was wondering if someone could explain?
// As requested... not sure it will help though.
var per = (
from p in OtherContext.tblPeriod
where activeContractList.Select(c => c.DomainSetExtensionCode).Contains(p.DomainSetExtensionCode)
select p).ToArray();
var com = (
from c in MyContext.tblService
join sce in MyContext.tblServiceExtension
on c.ServiceExtensionCode equals sce.ServiceExtensionCode
join sc in MyContext.tblServiceContract
on sce.ServiceContractCode equals sc.ContractCode
group sc by c.Period into comG
select new
{
PeriodNumber = comG.Key,
Group = comG,
}).ToArray();
var code =
(from c in com
join p in per on c.PeriodNumber equals p.PeriodNumber
select new
{
p.Code,
c.Group
}).ToArray();
var payDictionary = new Dictionary<int, int[]>();
// This is another linq query that returns an anonymous type with
// two properties, and int and an array.
code.ForEach(c => payDictionary.Add(c.Code, c.Group.Select(g => g.Code).ToArray()));
// MyContext is a LINQ to SQL DataContext
var stuff = (
from
p in MyContext.tblPaySomething
join cae in MyContext.tblSomethingElse
on p.PaymentCode equals cae.PaymentCode
join ca in MyContext.tblAnotherThing
on cae.SomeCode equals ca.SomeCode
where
// ca.ContractCode.Value in an int?, that should always have a value.
payDictionary[p.Code].Contains(ca.ContractCode.Value)
select new
{
p.Code,
p.ExtensionCode,
p.IsFlagged,
p.Narrative,
p.PayCode,
ca.BookCode,
cae.Status
}).ToList();
You won't be able to do this with a dictionary. The alternative is to join the three linq queries into one. You can do this with minimal impact to your code by not materializing the queries with ToArray. This will leave com and code as IQueryable<T> and allow for you compose other queries with them.
You will also need to use a group rather than constructing a dictionary. Something like this should work:
var per = (
from p in OtherContext.tblPeriod
where activeContractList.Select(c => c.DomainSetExtensionCode).Contains(p.DomainSetExtensionCode)
select p.PeriodNumber).ToArray(); // Leave this ToArray because it's materialized from OtherContext
var com =
from c in MyContext.tblService
join sce in MyContext.tblServiceExtension on c.ServiceExtensionCode equals sce.ServiceExtensionCode
join sc in MyContext.tblServiceContract on sce.ServiceContractCode equals sc.ContractCode
group sc by c.Period into comG
select new
{
PeriodNumber = comG.Key,
Group = comG,
}; // no ToArray
var code =
from c in com
where per.Contains(c.PeriodNumber) // have to change this line because per comes from OtherContext
select new
{
Code = c.PeriodNumber,
c.Group
}; // no ToArray
var results =
(from p in MyContext.tblPaySomething
join cae in MyContext.tblSomethingElse on p.PaymentCode equals cae.PaymentCode
join ca in MyContext.tblAnothThing on cae.SomeCode equals ca.SomeCode
join cg in MyContext.Codes.GroupBy(c => c.Code, c => c.Code) on cg.Key equals p.Code
where cg.Contains(ca.ContractCode.Value)
select new
{
p.ContractPeriodCode,
p.DomainSetExtensionCode,
p.IsFlagged,
p.Narrative,
p.PaymentCode,
ca.BookingCode,
cae.Status
})
.ToList();
Side Note: I also suggest using navigation properties where possible instead of joins. It makes it much easier to read and understand how objects are related and create complex queries.
Work on entity frame work vs2010
After execute my linq query get a list of records ,want to put this record in session .Now from session Want to get back my record list ,what to do how to get back record from a session
Linq query
public IEnumerable GetSearchUserGroupPermissionData(int userID = 0)
{
var query = from p in this.Context.CmnPermissionGroupUsers
join q in this.Context.CmnPermissionGroups on p.PermissionGroupID equals q.PermissionGroupID
join r in this.Context.CmnPermissionGroupDocs on p.PermissionGroupID equals r.PermissionGroupID
join s in this.Context.CmnUserInfoes on p.UserID equals s.UserID
join t in this.Context.CmnDocLists on r.DocListID equals t.DocListID
//join u in this.Context.CmnModuleFormCompanies on t.ModuleID equals u.ModuleID
//join v in this.Context.CmnModuleLists on u.ModuleID equals v.ModuleID
//join w in this.Context.CmnFormLists on u.FormID equals w.FormID
where p.IsDeleted == false
select new
{
RecordID = p.PermissionGroupUserRecordID,
s.UserID,
s.UserFirstName,
q.PermissionGroupName,
p.EffectiveDate,
p.StatusID,
t.DocListID,
t.DocName,
t.ModuleID,
// v.ModuleName,
// u.FormID,
// t.FormName,
// w.FormName,
t.ParentID,
t.Sequence,
t.IsApprovalRequired,
t.CompanyCategoryID,
t.DocTypeID
//p.CreateBy,
//p.CreateOn,
//p.CreatePc,
//p.UpdateBy,
//p.UpdateOn,
//p.UpdatePc,
//p.IsDeleted,
//p.DeleteBy,
//p.DeleteOn,
//p.DeletePc,
//p.Transfer
};
return query.WhereIf(userID != 0, w => w.UserID == userID).ToList();
}
Put result in session
Session["UserPermission"] = new PermissionGroupUserController().GetSearchUserGroupPermissionData(objEntity.UserID);
Now ,want to get back the record set from session.bellow foreach syntax area as a item contain each row all properties and values but can not assign in a variable just like bellow ,why can not assign an AnonymousType variable value to a variable.
var o = Session["UserPermission"] as IEnumerable; //use casting
foreach (var area in o)
{
//int a = area.UserID;
}
Note:sabove syntax how me error
message:foreach statement cannot operate on variables of type 'object'
because 'object' does not contain a public definition for
'GetEnumerator'
If have any query please ask.
Did you try typecasting oto IEnumerable?
Apart from that, in your foreach loop, you have to use dynamic instead of var. This is required because your type is anonymous.
But i would still strongly suggest you to use normal types instead of Anonumous ones atleast for two reasons
Code reusability.
Better code readability.
I have a Linq query that looks something like the following
var query3 = from c in Session.CreateLinq<AccountTransaction>()
join a in Session.CreateLinq<Account>() on c.Account equals a
where c.DebitAmount >= 0
select new { a.Name, c.DebitAmount }
;
The Session object interacts with a datasource behind the scenes but it also has an internal cached state which may have changes. When I run a query I would like to query the both the internal cached state AND the datasource and then merge the results together, with the internal cached state taking precendence.
I am using re-linq for the generation of the query against the datasource which is working fine. What I am not sure about is how to also do the query against the internal state using the same Linq query.
There's a call GetAllCached() on Session that I can use instead of Session.CreateLinq if I just wanted to query the internal state. But I'm not sure at which point in my custom provider I can handle handing off to the datasource AND the internal state using GetAllCached().
Any suggestions appreciated from any Linq gurus.
// From Database
var query1 = from c in Session.CreateLinq<AcccountTransaction>()
join a in Session.CreateLinq<Account>()
on c.Account equals a
where c.DebitAmount >= 0
select new { Account = a, AccountTrans = c };
//select new { a.Name, c.DebitAmount };
// From Cache
var query2 = from c in Session.GetAllCached<AcccountTransaction>()
join a in Session.GetAllCached<Account>()
on c.Account equals a
where c.DebitAmount >= 0
select new { Account = a, AccountTrans = c };
//select new { a.Name, c.DebitAmount };
//var query3 = query2.Union(query1.Except(query2));
var query4 = query2.Union(query1);
Modified: 04:51 AM Singapore Time
If I understand correctly, you have a single custom LINQ provider for your datasource, and a (presumably type-safe) way of getting cached results as well.
In this case, I recommend just using LINQ to Objects to access your cached set. You can use AsEnumerable to "step out" of your custom LINQ provider into LINQ to Objects.
The join brings up a problem, though. Since either of these types may exist in the cache, it's not possible to push logic to the DB. For example, is it possible to have an AccountTransaction in the cache without its Account also being in the cache?
If you allow any situation in the cache (e.g., AccountTransaction without associated Account records), then you have to do the join in memory and not in the db:
var allDebitAccountTransactions = Session.GetAllCached<AccountTransaction>()
.Where(x => x.DebitAmount >= 0)
.Union(Session.CreateLinq<AccountTransaction>()
.Where(x => x.DebitAmount >= 0));
var allAccounts = Session.GetAllCached<Account>()
.Union(Session.CreateLinq<Account>());
var query3 = from c in allDebitAccountTransactions
join a in allAccounts where c.Account equals a
select new { a.Name, c.DebitAmount };
However, if you have more control over your cache, and only allow AccountTransaction objects to be present if their associated Account objects are present, then you can push the join operation to the datasource and do another one in memory, merging the results:
var datasourceResults = from c in Session.CreateLinq<AccountTransaction>()
join a in Session.CreateLinq<Account>() on c.Account equals a
where c.DebitAmount >= 0
select new { a.Name, c.DebitAmount, c.Id };
var cacheResults = from c in Session.GetAllCached<AccountTransaction>()
join a in Session.GetAllCached<Account>() on c.Account equals a
where c.DebitAmount >= 0
select new { a.Name, c.DebitAmount, c.Id };
var query3 = cacheResults.Union(datasourceResults)
.Select(x => new { x.Name, x.DebitAmount });
I think. I am not an expert in LINQ, so I'm curious to see other responses.