Creating a LINQ select from multiple tables - c#

This query works great:
var pageObject = (from op in db.ObjectPermissions
join pg in db.Pages on op.ObjectPermissionName equals page.PageName
where pg.PageID == page.PageID
select op)
.SingleOrDefault();
I get a new type with my 'op' fields. Now I want to retrieve my 'pg' fields as well, but
select op, pg).SingleOrDefault();
doesn't work.
How can I select everything from both tables so that they appear in my new pageObject type?

You can use anonymous types for this, i.e.:
var pageObject = (from op in db.ObjectPermissions
join pg in db.Pages on op.ObjectPermissionName equals page.PageName
where pg.PageID == page.PageID
select new { pg, op }).SingleOrDefault();
This will make pageObject into an IEnumerable of an anonymous type so AFAIK you won't be able to pass it around to other methods, however if you're simply obtaining data to play with in the method you're currently in it's perfectly fine. You can also name properties in your anonymous type, i.e.:-
var pageObject = (from op in db.ObjectPermissions
join pg in db.Pages on op.ObjectPermissionName equals page.PageName
where pg.PageID == page.PageID
select new
{
PermissionName = pg,
ObjectPermission = op
}).SingleOrDefault();
This will enable you to say:-
if (pageObject.PermissionName.FooBar == "golden goose") Application.Exit();
For example :-)

If you don't want to use anonymous types b/c let's say you're passing the object to another method, you can use the LoadWith load option to load associated data. It requires that your tables are associated either through foreign keys or in your Linq-to-SQL dbml model.
db.DeferredLoadingEnabled = false;
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<ObjectPermissions>(op => op.Pages)
db.LoadOptions = dlo;
var pageObject = from op in db.ObjectPermissions
select op;
// no join needed
Then you can call
pageObject.Pages.PageID
Depending on what your data looks like, you'd probably want to do this the other way around,
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Pages>(p => p.ObjectPermissions)
db.LoadOptions = dlo;
var pageObject = from p in db.Pages
select p;
// no join needed
var objectPermissionName = pageObject.ObjectPermissions.ObjectPermissionName;

You must create a new anonymous type:
select new { op, pg }
Refer to the official guide.

If the anonymous type causes trouble for you, you can create a simple data class:
public class PermissionsAndPages
{
public ObjectPermissions Permissions {get;set}
public Pages Pages {get;set}
}
and then in your query:
select new PermissionsAndPages { Permissions = op, Page = pg };
Then you can pass this around:
return queryResult.SingleOrDefault(); // as PermissionsAndPages

change
select op)
to
select new { op, pg })

Related

How to access a list in another method

I am new to .NET and I would like to be able to solve this small problem that I am facing. I want to access a list that is in Info method to be able to use it in InfoCurricular method. I do not know how I can do it, if I create a method if doing everything in one is the best. The methods are separated because they have different attributes which result in different results after the action taken.
Info Method
public async Task<IActionResult> Info(string anoLetivo) {
var nomeuc = new List<NomeUC>();
Main main = new Main();
main.NomeUC = nomeuc;
//user
var user = await GetCurrentUserAsync();
//docente
var IDdocente = _context.Docente.SingleOrDefault(x => x.UserId == user.Id);
var nomeporano = (from nome in _context.NomeUC
join unidadecurric in _context.UnidadeCurricular on nome.NomeUCId equals unidadecurric.NomeUCId
join depart in _context.Departamento on unidadecurric.DepartamentoId equals depart.DepartamentoId
join doc in _context.Docente on depart.DepartamentoId equals doc.DepartamentoId
join nalunos in _context.NAlunos on unidadecurric.UnidadeCurricularId equals nalunos.UnidadeCurricularId
join anoletivo in _context.AnoLetivo on nalunos.AnoLetivoId equals anoletivo.AnoLetivoId
where anoletivo.Ano == anoLetivo && doc.DepartamentoId == IDdocente.DepartamentoId
select new {
nome=nome.Nome
}).ToList();
foreach (var item in nomeporano) {
nomeuc.Add(new NomeUC {
Nome = item.nome
});
}
return View("InfoCurricular", main);
}
InfoCurricular Method
public async Task<IActionResult> InfoCurricular(int ano, int semestre) {
var nomeuc = new List<NomeUC>();
//var docente = new List<Docente>();
var unidadecurr = new List<UnidadeCurricular>();
Main main = new Main();
//main.Docente = docente;
main.UnidadeCurricular = unidadecurr;
main.NomeUC = nomeuc;
//user
var user = await GetCurrentUserAsync();
//docente
var IDdocente = _context.Docente.Where(x => x.UserId == user.Id).ToList();
var uc = (from nome in *nomeporano*
join unidadecurric in _context.UnidadeCurricular on nome.NomeUCId equals unidadecurric.NomeUCId
where unidadecurric.AnoCurricular == ano && unidadecurric.Semestre == semestre
select new {
nome = nome.Nome
}).ToList();
foreach(var item in uc) {
nomeuc.Add(new NomeUC {
Nome = item.nome
});
}
return View(main);
}
In the InfoCurrilar method in the uc variable I want to use in the query the values of the other list that is in the Info method and from these values get a new list after executing the query.
The idea is not to get the list in the two methods but use the nomeporano list as a way of going there to seek values.
But this "nomeporano" have attributes that are different in this two methods. I'm still a little bit confused on how to build the method. In Info method the attribute is one, but in InfoCurricular there are two attributes which is different from the other method. How to build a method to retrieve this "nomeporano" list and use that list in methods Info and InfoCurricular? I can undestrand the ways but i'm stuck on the way to build the method .....
There are 2 ways of doing it:
1.) Add a property in the Model class "Main" for retaining complete value of nomeporano, and since you are passing the model to method InfoCurricular, it can easily be used.
2.) Move the below code in the separate method and call the method in methods Info and InfoCurricular:
Cons: Hitting the database multiple times for same data.
var nomeporano = (from nome in _context.NomeUC
join unidadecurric in _context.UnidadeCurricular on nome.NomeUCId equals unidadecurric.NomeUCId
join depart in _context.Departamento on unidadecurric.DepartamentoId equals depart.DepartamentoId
join doc in _context.Docente on depart.DepartamentoId equals doc.DepartamentoId
join nalunos in _context.NAlunos on unidadecurric.UnidadeCurricularId equals nalunos.UnidadeCurricularId
join anoletivo in _context.AnoLetivo on nalunos.AnoLetivoId equals anoletivo.AnoLetivoId
where anoletivo.Ano == anoLetivo && doc.DepartamentoId == IDdocente.DepartamentoId
select new {
nome=nome.Nome
}).ToList();
Regards,
Rajiv
Happy Coding

MVC - Joining multiple table using LINQ / lambda - Using EF

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});

returning multiple class model data from linq in list via return method in C#

I have LINQ output which I am trying to pass in list but I am getting following error
in linq result I am trying to pass data from two class model, if I do one class model (listOfCoursesWithoutURL ) then it work but I need to pass processedCourseInstance. I have created ModelView of two classes but not sure what I am missing in this picture
ViewModel
public class CoursesInstanceStudyLevel_ViewModel
{
public CourseInstanceModel _CourseInstanceModel { get; set; }
public StudyLevelModel _StudyLevelModel { get; set; }
}
My Class
public List<CoursesInstanceStudyLevel_ViewModel> ProcessAllCoursesApplicationURL(CourseApplicationsURLFeed_Model _obj)
{
using(var _uof = new Courses_UnitOfWork())
{
_uof.CourseInstances_Repository.GetAll();
var _listOfCoursesWithoutURL = (from b in ListOfCoursesInstances
where b.ApplicationURL == null
select b).ToList();
var processedCourseInstance = (from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in _uof.StudyLevel_Repository.GetAll() on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new { _courseInstances, _studylevel }).ToList();
return processedCourseInstance; // it doesn't work ... refer to screen shot
// return _listOfCoursesWithoutURL //it works
}
}
Error
here:
select new { _courseInstances, _studylevel })
you are defining an anonymous object. You have a type ready, so use that one:
select new CoursesInstanceStudyLevel_ViewModel
{
_CourseInstanceModel = _courseInstances,
_StudyLevelModel = _studylevel
}
assuming CourseInstanceModel and StudyLevelModel are the correct types
With highlighted line in following code snippet, you are selecting an anonymous object instead of a concrete CourseIntaceStudyLeve_ViewModel
select new { _courseInstances, _studylevel }
You will have to change your query to following..
var processedCourseInstance = (from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in _uof.StudyLevel_Repository.GetAll() on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new CoursesInstanceStudyLevel_ViewModel(){
_CourseInstanceModel = _courseInstances.FirstOrDefault(),
StudyLevelModel = _studylevel.FirstOrDefault()}).ToList();
I have assumed you would need only first course and first study level based on your view model definition and there for applied FirstOrDefault. You can choose to go along with this or change your view model definition.
here is my answer and it works
var processedCourseInstance =
(from _courseInstances in _uof.CourseInstances_Repository.GetAll()
join _courses in _uof.Courses_Repository.GetAll() on _courseInstances.CourseID equals _courses.CourseID
join _studylevel in _uof.StudyLevel_Repository.GetAll() on _courses.StudyLevelId equals _studylevel.StudyLevelID
orderby _courseInstances.CourseCode
select new CoursesInstanceStudyLevel_ViewModel() {
_CourseInstanceModel = _courseInstances,
_StudyLevelModel = _studylevel
}).ToList();

Unioning two LINQ queries

I just need to make full outer join with Linq, But When i union two quires i get this error:
Instance argument: cannot convert from 'System.Linq.IQueryable' to 'System.Linq.ParallelQuery
And here is my full Code:
using (GoodDataBaseEntities con = new GoodDataBaseEntities())
{
var LeftOuterJoin = from MyCustomer in con.Customer
join MyAddress in con.Address
on MyCustomer.CustomerId equals MyAddress.CustomerID into gr
from g in gr.DefaultIfEmpty()
select new { MyCustomer.CustomerId, MyCustomer.Name, g.Address1 };
var RightOuterJoin = from MyAddress in con.Address
join MyCustomer in con.Customer
on MyAddress.CustomerID equals MyCustomer.CustomerId into gr
from g in gr.DefaultIfEmpty()
select new { MyAddress.Address1, g.Name };
var FullOuterJoin = LeftOuterJoin.Union(RightOuterJoin);
IEnumerable myList = FullOuterJoin.ToList();
GridView1.DataSource = myList;
GridView1.DataBind();
}
The types of your two sequences are not the same, so you can't do a Union.
new { MyCustomer.CustomerId, MyCustomer.Name, g.Address1 };
new { MyAddress.Address1, g.Name };
Try making sure that the fields have the same names and types in the same order.
Why not select it all as one thing? Depending on your setup (i.e., if you have foreign keys properly set up on your tables), you shouldn't need to do explicit joins:
var fullJoin = from MyCustomer in con.Customer
select new {
MyCustomer.CustomerId,
MyCustomer.Name,
MyCustomer.Address.Address1,
MyCustomer.Address.Name
};
Method syntax:
var fullJoin = con.Customers.Select(x => new
{
x.CustomerId,
x.Name,
x.Address.Address1,
x.Address.Name
});
union appends items from one collection to the end of another collection, so if each collection had 5 items, the new collection will have 10 items.
What you seem to want is to end up with 5 rows with more infomration is each. That's not a job for Union. You might be able to do it with Zip(), but you'll really be best with the single query as shown by DLeh.

Why I'm still getting System.NotSupportedException

My code is:
using( var ctxA = new AEntities())
using( var ctxB = new BEntities())
{
var listOfA = (from A in AEntities.AEntity select A).ToList();
var listOfB = (from B in BEntities.BEntity
where listOfA.Select( A = A.Id)contains(B.Id)
select B).ToList();
}
I'm getting the error:
The specified LINQ expression contains references to queries that are associated with different contexts.
However, because of 'ToList' I already did a fetch in AEntity thats makes second query only about one of the contexts, did not?
How can a separate the two queries still using one list to query the other?
Try to store just IDs into listOfA
var listOfA = (from A in AEntities.AEntity select A.Id).ToList();
var listOfB = (from B in BEntities.BEntity
where listOfA.Contains(B.Id)
select B).ToList();
If anyone wants to know, the answer to my question was:
var listOfA = (from A in AEntities.AEntity select A.Id).ToList();
List<long> listOfIdsOfA = listOfA.Select( A=>A.id).ToList();
var listOfB = (from B in BEntities.BEntity
where listOfIdsOfA.Contains(B.Id)
select B).ToList();
The only difference to Selman's answer was the creation of Id's list after the first query. That's why I need the whole entity too.

Categories