Imagine a simple Entity Framework query with a context generated from the database such as:
var q = from a in context.Accounts
join c in context.Contracts
on a.Id equals c.AccountId
select new CustomAccount {
Id = a.Id,
Name = a.Name,
...
Contracts = //How do I easily populate the related contracts?
};
The query looks for accounts and joins to contracts. The relationship is not enforced in the database (I can't change the schema) so I can't use navigational properties. Is there an easy way that I can populate the related objects?
Just use a group by clause. Something like this (untested):
var q = from a in context.Accounts
join c in context.Contracts on a.Id equals c.AccountId
group a by new { a.Id, a.Name, a.Etc } into g
select new CustomAccount
{
Id = g.Key.Id,
Name = g.Key.Name,
Etc = g.Key.Etc,
Contracts = g.SelectMany(x => x.Contracts)
};
I'm not sure if I understand correctly but you can execute a query that return anonymous type objects
EDIT : you can create a custom class to hold the data member of your result and return in linq result.
EDIT : using group by on account name (e.g)
var q = from a in context.Accounts
join c in context.Contracts
on a.Id equals c.AccountId
group a by new { a.Name } into g
select new AccountContracts
{
AccountName = g.Key.Id, // Account name
Contracts = g.SelectMany(x => x.Contracts)
};
Related
I see some related questions on stack, but not sure how to still solve it for my problem as I'm doing a three table left outer join. RoleUser and Role are an entity object
var query = from u in Context.Users
join ru in Context.RoleUsers on u.Id equals ru.UserId into plv
from x in plv.DefaultIfEmpty(new RoleUser())
join r in Context.Roles on x.RoleId equals r.Id into pii
from y in pii.DefaultIfEmpty(new Role())
orderby u.Id, y.Id
select new UserWithRole() { Id = u.Id, User = u, Role = y };
The problem is DefaultIfEmpty(new RoleUser()) and DefaultIfEmpty(new Role()) calls, because EF cannot translate creation of a constant entity object to SQL.
So you need to use the parameterless overload of DefaultIfEmpty() and deal with null objects in the materialized query result.
If you don't need a IQueryable<T> result, you can use intermediate anonymous type projection, which to LINQ to Objects context by using AsEnumerable() and do the final desired projection:
var query = (from u in Context.Users
join ru in Context.RoleUsers on u.Id equals ru.UserId into userRoles
from ru in userRoles.DefaultIfEmpty()
join r in Context.Roles on ru.RoleId equals r.Id into roles
from r in roles.DefaultIfEmpty()
orderby u.Id, r.Id
select new { User = u, Role = r }
)
.AsEnumerable() // db query ends here
.Select(x => new UserWithRole
{
Id = x.User.Id,
User = x.User,
Role = x.Role ?? new Role() // now this is supported
});
As the error explains you cannot create in a linq query they mapped types (RoleUser and Role). Instead use the DefaultIfEmpty(). Something along the following:
var roles = from ru in Context.RoleUsers
join r in Context.Roles on ru.RoleId equals r.Id
select new { ru.UserId, role = r };
var query = from u in Context.Users
join r in roles on u.Id equals r.UserId into plv
from x in plv.DefaultIfEmpty()
select new UserWithRole() { Id = u.Id, User = u, Role = x.role };
However I'd suggest look into navigation properties - that way you will not have to construct the joins and work with it as if it is "normal" objects.
So I have a SQL view that I've created that provides me what I need. Essentially it's a job position billeting system that shows how many positions have been authorized vs filled (or assigned).
SELECT Companies.Name AS Company, Grades.Name AS Grade, Series.Name
AS Series, Positions.Authorized, COUNT(People.PersonId) AS Assigned
FROM Companies INNER JOIN
Positions ON Companies.Id = Positions.CompanyId INNER JOIN
Series ON Positions.SeriesId = Series.Id INNER JOIN
Grades ON Positions.GradeId = Grades.Id INNER JOIN
People ON Positions.CompanyId = People.CompanyId AND
Positions.SeriesId = People.SeriesId AND Positions.GradeId = People.GradeId
GROUP BY Companies.Name, Grades.Name, Series.Name, Positions.Authorized
Now what I'd like to be able to do is recreate this in a LINQ query. I've almost got it where I need it; however, I can't figure out how to add the counted column at the end that's based on the People table.
Here's my current LINQ query:
var query = from a in db.Companies
join b in db.Positions on a.Id equals b.CompanyId
join c in db.Series on b.SeriesId equals c.Id
join d in db.Grades on b.GradeId equals d.Id
join e in db.People on new { b.CompanyId, b.SeriesId, b.GradeId } equals new { e.CompanyId, e.SeriesId, e.GradeId }
group a by new { CompanyName = a.Name, GradeName = d.Name, SeriesName = c.Name, b.Authorized, e.PersonId } into f
select new { Company = f.Key.CompanyName, Grade = f.Key.GradeName, Series = f.Key.SeriesName, f.Key.Authorized, Assigned = /* needs to be Count(People.PersonId) based on last join */ )};
Thanks in advance for any help you can provide!
Figured it out. The reason why it was posting multiple rows and not doing a proper count on the same row was because in my "group by" I added in "e.PersonId" when it should have simply been removed. I also had to add a few things to make it work on the front-end razor views since it's an anonymous type (this doesn't have anything to do with the original question, but thought I'd give reason to the changes). So the person who removed their answer, you were partially right, but the reason it wasn't working was because of the additional fieldin the group by:
dynamic query = (from a in db.Companies
join b in db.Positions on a.Id equals b.CompanyId
join c in db.Series on b.SeriesId equals c.Id
join d in db.Grades on b.GradeId equals d.Id
join e in db.People on new { b.CompanyId, b.SeriesId, b.GradeId } equals new { e.CompanyId, e.SeriesId, e.GradeId }
group a by new { CompanyName = a.Name, GradeName = d.Name, SeriesName = c.Name, b.Authorized } into f
select new { Company = f.Key.CompanyName, Grade = f.Key.GradeName, Series = f.Key.SeriesName, Authorized = f.Key.Authorized, Assigned = f.Count()}).AsEnumerable().Select(r => r.ToExpando());
And what it looks like on the page:
For example i have 2 model.
ASpnetUserRoles and ASPnetRoles
i want to select ASPnetRoles.Name,ASPnetROles.ID where ASPnetRoles.ID in ASPnetUserRoles.
i only know how to write in SQL
select * from modalA where modelA.id in(select modelB.id from modelB)
if you enable "Lazy Loading" , you can use this linq query :
using(var db = ..."context"..)
{
var q = db.AspNetUserRoles.where(c=>c.UserID = userIdVal)
.select(z=> new { RoleId = z.RoleId
,userId = z.UserId
,RoleName = z.AspNetRole.RoleName
})
.toList();
}
This is what i did eventually.
IEnumerable<IdentityRole> ro;
ro = (from p in haha join ur in aspNetUser.AspNetRoles on p.Id equals ur.Id select p);
Here a generic sample you must create a class (no EF), and store the result to it.
IQueryable<ResultClass> result=from t1 in db.Table1
join t2 in db.Table2
//Here the relation fields
on t1.IdTable1 equals t2.IdTable2
//Here where conditios and/or orderby
select new ResultClass()
{
Field1=t1.SomeField,
Field2=t2.SomeField,
//all need fields
}
Use the result
result.ToList()
I want to write a linq query which retrieves the data in the following format.
Assume that there are two tables - Department and Employee
Department
Dept_id| Manager_id
Employee
Emp_id| Dept_id| Emp_data
The relationship between Department and Employee table is one to many. The Department table also stores the manager employee id in that particular department. So Manager_id is nothing but the employee_id.
Now I want to get the data as follows
Dept_id, Manager_id, Emp_data(data of Manager_id), Count(employees in that department)
If I do the join on two tables i can get data of Manager but unable to get the count of all employees in that department. Any help would be appreciated.
You could do something like this:
var result=
(
from dep in db.Department
join emp in db.Employee
on dep.Manager_id equals emp.Emp_id
select new
{
dep.Dept_id,
emp,
NumberOfEmp= db.Employee.Count(c=>c.Dept_id==dep.Dept_id)
}
).ToList();
Where db is the linq data context
You could do a group join. If you have a preferred handcoded SQL statement that you would like to be generated from your LINQ query. You may want to experiment a bit with different formats to see which one allows you to generate the "best" SQL.
var result = from d in context.Departments
join e in context.Employees on d.dept_id equals e.dept_id into g
select new {
dept_id = d.dept_id,
manager_id = d.manager_id,
manager_data = g.Single(x => x.emp_id == d.manager_id).emp_data,
employee_count = g.Count()
};
or
var result = from d in context.Departments
join e in context.Employees on d.dept_id equals e.dept_id into g
from m in g where m.emp_id == d.manager_id
select new {
dept_id = d.dept_id,
manager_id = d.manager_id,
manager_data = m.emp_data,
employee_count = g.Count()
};
I would like to use my entity class properties in my linq query that return some value with it.
so this my linq query;
List<PvmBarChartData> BaseofSegmentIPPGMR = (from si in db.ScoreItem
join s in db.Score on si.ScoreId equals s.Id
join prg in db.ProjectResearchGroup on si.ProjectResearchGroupId equals prg.Id
join rg in db.RgClone on prg.RgCloneId equals rg.Id
join sp in db.SalesPoint on s.SalesPointId equals sp.Id
join c in db.Channel on sp.ChannelId equals c.Id
where (si.ResearchGroupType == ResearchGroupType.ScoreCard && spIds.Contains(s.SalesPointId))
group si by c.Name into g
select new PvmBarChartData
{
GroupName = g.Key,
DataValues = new List<CvmNameValuePair>{ new CvmNameValuePair{
Name = "",
Value = g.Average(x => x.TotalScore)
}
}
})
.ToList();
so for instance I would like to set Name properties with my entity framework model class' properties' value, Name = s.Name,
How can I implement this on my code?
A way that you can access s.Name is by doing another linq query on g, since g is now a data set of grouped objects.
Name = (from gx in g select gx.Name).FirstorDefault();