Dynamics 365 OrganizationServiceContext Linq Query with multiple Joins - c#

I am running into an issue running most of my queries. I can generate a link query with a join to only one related entity at a time. But when I run a LINQ query with multiple joins as in the example below, I get the "Sequence contains no elements" error.
var query =
(
from permission in context.CreateQuery<ServiceModel.Types.idoe_permission>()
join contact in context.CreateQuery<ServiceModel.Types.Contact>()
on permission.idoe_contact_idoe_permission.Id equals contact.Id
join corporation in context.CreateQuery<ServiceModel.Types.idoe_corporation>()
on permission.idoe_idoe_corporation_idoe_permission.Id equals corporation.Id
join role in context.CreateQuery<ServiceModel.Types.idoe_role>()
on permission.idoe_idoe_role_idoe_permission.Id equals role.Id
where contact.idoe_ADB2CID == request.UserId
select new { Corporation = corporation, Role = role }
).ToList();
I am only able to "join" one entity at a time. Other examples I have seen allow multiple joins, but I have not been able to get this to work.
Any suggestions?

It looks like you're using the relationship names rather than the lookup field names in the joins. Using the lookup field names instead might look something like this:
var query = (from permission in context.CreateQuery<ServiceModel.Types.idoe_permission>()
join contact in context.CreateQuery<ServiceModel.Types.Contact>() on permission.idoe_contactid.Id equals contact.Id
join corporation in context.CreateQuery<ServiceModel.Types.idoe_corporation>() on permission.idoe_corporationid.Id equals corporation.Id
join role in context.CreateQuery<ServiceModel.Types.idoe_role>() on permission.idoe_roleid.Id equals role.Id
where contact.idoe_ADB2CID == request.UserId
select new { Corporation = corporation, Role = role }).ToList() ;

Related

C# & Entity Framework : return specific columns from a 3 table join

I have a C# application where I am using Entity Framework to pull data from a database. This is the code I am executing:
var person = new List<Person>();
using (DevTestEntities db = new DevTestEntities())
{
person = (from p in db.People
join e in db.PersonEmails on p.Id equals e.Id
join t in db.PersonPhones on p.Id equals t.Id
where t.Phone == phoneNumber
select p).ToList();
}
var str = Newtonsoft.Json.JsonConvert.SerializeObject(person);
return str;
When the code runs, it fails on the select. I assume it is failing because there is a table within the database that is not part of the model. And because there is just a generic select, I assume Entity Framework is selecting all columns from all tables and doesn't know what to do with some of the columns.
What I really want to do is to be able to specify the columns that I want to return to the calling function. How do I specify what columns Entity Framework should select?
Thanks for any assistance.
EF will not fail because of tables in the DB that are not in model. It would help if you provided the error. Also, your query will result in selecting all columns from the People table but not the others.
An example answer to your question is this, it selects three columns from different tables and puts them in a new anonymous type:
var onlySomeColumns = (from p in db.People
join e in db.PersonEmails
on p.Id equals e.Id
join t in db.PersonPhones
on p.Id equals t.Id
where t.Phone == phoneNumber
select new {p.Id, e.email, t.phonenumber}).ToList();

Apache Ignite Group By Linq Query Issue

i'm trying to join three caches with two of them are left outer joined,also has group by clause. With the below query i ran into strange issue, the generated sql was not correct.
var query = (from service in serviceCache
join meetingService in meetingServiceCache.DefaultIfEmpty() on service.Value.ServiceId equals meetingService.Value.ServiceId
join meeting in meetingCache.DefaultIfEmpty() on meetingService.Value.MeetingId equals meeting.Value.MeetingId
group new { service, meeting } by service.Value.ServiceId into g
select new {
service.Value.ServiceId,
lastMeeting = g.Select(x=>x.meeting.Value.CreatedDate).Max()
}).ToCacheQueryable().GetFieldsQuery.Sql;
The generated query looks like below
select _T0.SERVICEID,min(_T0.CreatedDate) from MEETINGSCHEMA.SERVICES as _T0
left outer join (select _T1.*,_T1.KEY,_T1.VAL from MEETINGSCHEMA.MEETINGSERVICE as _T1) as _T2 on (_T2.SERVICEID= _T0.SERVICEID)
left outer join (select _T3.*,_T3.KEY,_T3.VAL from MEETINGSCHEMA.MEETINGS as _T3) as _T4 on (_T4.MEETINGID= _T3.MEETINGID)
group by (_T0.SERVICEID)
In selected columns the createdDate should be selected from _T4 reference,But it's always selected from first table alias thus the query was failing always reporting CreatedDate as invalid column.I Suspect something wrong with linq to sql translation.
Please let me know if i'm doing any mistake. Also the code snippet was typed by hand with out intellisense, pardon me incase of typos.
After grouping you can select only Key property or aggregation functions. Also LEFT JOIN should be written in different way.
var query =
from service in serviceCache
join meetingService in meetingServiceCache on service.Value.ServiceId equals meetingService.Value.ServiceId into j
from meetingService in j.DefaultIfEmpty()
join meeting in meetingCache on meetingService.Value.MeetingId equals meeting.Value.MeetingId into j
from meeting in j.DefaultIfEmpty()
group new { service, meeting } by service.Value.ServiceId into g
select new
{
ServiceId = g.Key,
lastMeeting = g.Max(x => x.meeting.Value.CreatedDate)
};
This seems to be a bug in Ignite, I've filed a ticket.
The workaround is to reorder the joined tables and put meeting first, something like this:
var query = (from meeting in meetingCache
join meetingService in meetingServiceCache.DefaultIfEmpty() on meeting.Value.MeetingId equals meetingService.Value.MeetingId
join service in serviceCache service.Value.ServiceId equals meetingService.Value.ServiceId
group new { service, meeting } by service.Value.ServiceId into g
select new {
service.Value.ServiceId,
lastMeeting = g.Select(x=>x.meeting.Value.CreatedDate).Max()
}).ToCacheQueryable().GetFieldsQuery.Sql;
Another workaround is to use raw SQL for this query.

How to join a list and large lists/tables using LINQ

Initially I have such a list :
List<Car> cars = db.Car.Where(x => x.ProductionYear == 2005).ToList();
Then I'm trying to join this list with two large tables using LINQ like this :
var joinedList = (from car in cars
join driver in db.Driver.ToList()
on car.Id equals driver.CarId
join building in db.Building.ToList()
on driver.BuildingId equals building.Id
select new Building
{
Name = building.Name;
Id = building.Id;
City = building.City;
}).ToList();
Both Driver and Building tables have about 1 million rows. When I run this join I get out of memory exception. How can I make this join work? Should I make the join operation on database? If yes, how can I carry cars list to the db? Thanks in advance.
Even if you remove the .ToList() calls inside your join, you code will still pull all the data and perform the join in-memory and not in SQL server. This is because you're using a local list cars in your join. The below should solve your problem:
var joinedList = (from car in db.Car.Where(x => x.ProductionYear == 2005)
join driver in db.Driver
on car.Id equals driver.CarId
join building in db.Building
on driver.BuildingId equals building.Id
select new Building
{
Name = building.Name;
Id = building.Id;
City = building.City;
}).ToList();
You can remove the last .ToList() and do some paging if you expect to get too many records in the results.
even If You have removed .ToList() replace in .AsQueryable()
AsQueryable Faster then ToList And AsEnumerable
If you create an IQueryable, then the query may be converted to sql
and run on the database server
If you create an IEnumerable, then all rows will be pulled into
memory as objects before running the query.
In both cases if you don't call a ToList() or ToArray() then query
will be executed each time it is used, so, say, you have an
IQueryable and you fill 4 list boxes from it, then the query will be
run against the database 4 times.
so following Used Linq query
var joinedList = (from car in db.Car.Where(x => x.ProductionYear == 2005).AsQueryable()
join driver in db.Driver.AsQueryable()
on car.Id equals driver.CarId
join building in db.Building.AsQueryable()
on driver.BuildingId equals building.Id
select new Building
{
Name = building.Name,
Id = building.Id,
City = building.City,
}).ToList();
First don't ever try ToList() while using LINQ(you can) but make sure that you use ToList() as less as possible in a very rare scenarios only.
Every time you will get OutOfMemoryException when the table contains many rows.
So, here is the code for your question:
var joinedList = (from car in db.Car.GetQueryable().Where(x => x.ProductionYear == 2005)
join driver in db.Driver.GetQueryable() on car.Id equals driver.CarId
join building in db.Building.GetQueryable() on driver.BuildingId equals building.Id
select new Building
{
Name = building.Name;
Id = building.Id;
City = building.City;
}).ToList();

SQL to LINQ inner JOIN

Could somebody help me form the below SQL query in Entity framework linq?
The AccountApplication-table has got two FK's:
AccountApplication[AccountId]->Account[AccountId]
and
AccountApplication [ApplicationId] -> Application[ApplicationId]
select * from Invoice a inner join AccountApplication b
on b.AccountId = 3 and b.ApplicationId = a.ApplicationId
I do not have the AccountApplication entity table available in EF.
What I want is a list of invoice who belongs to the application(s) what the given account id has rights to. And the relation between accountid's applicationid's are in the AccountApplication-table.
Screenshot of EF classes with navigation properties
With navigation properties it's quite simple
var query = db.Invoices.Where(invoice => invoice.Application.Account
.Any(account => account.AccountId == 3));
I hope you see now why they are called navigation properties. Inside the query you use them to "navigate" (access) the data needed, and the EF uses them as metadata for generating the necessary joins.
var records= (from a in context.Invoices join b in context.AccountApplication
on a.accountId equals b.accountId
where b.accountId==3
select new {a.Id,b.Id,....etc});

Linq one-to-many Union

I'm developing a MVC web application using asp.net C# and VS2012 Express.
I have a table (Organizations) with one-to-many relationships with two other tables (Comments and Proposals). All three tables contain an OrganizationID field to maintain the relationships. All three tables have an AddedBy string field.
I want to find all Organizations where either the Organization.AddedBy="Joe" or Comments.AddedBy="Joe" or Proposals.AddedBy="Joe".
These queries do a join, but I'm looking for a union that contains only the Organizations' fields.
// Find organizations created by this person.
IQueryable<Organization> org = from m in context.Organizations
where m.AddedBy.Equals("Joe")
select m;
// Find Comments created by this person.
IQueryable<Comment> comment = from m in context.Comments
where m.AddedBy.Equals("Joe")
select m;
// Join our two queries.
IQueryable<Comment> organizations = (from item in org
join c in comment on item.OrganizationID equals c.OrganizationID
select item).Distinct();
// Find Proposals created by this person.
IQueryable<Proposal> proposal = from m in context.Proposals
where m.AddedBy.Equals("Joe")
select m;
// Join our two queries.
organizations = (from item in organizations
join c in proposal on item.OrganizationID equals c.OrganizationID
select item).Distinct();
Thanks for your help.
If you are using Entity Framework you can do either:
var orgs = context.Organizations
.Where(O => O.AddedBy.Equals("Joe") ||
O.Comments.Any(C => C.AddedBy.Equals("joe")) ||
O.Proposals.Any(P => P.AddedBy.Equals("joe")));
As EF maintaining the parent-child relationship with navigation properties.
Hope this will help !!
So you're looking for three different sets, all combined. Just query each of those three things and then combine them using Union:
string user = "Joe";
var addedOrganizations = context.Organizations.Where(org => org.AddedBy == user);
var orgsWithUsersComments = from org in context.Organizations
join c in context.Comments
on org.OrganizationID equals c.OrganizationID
where c.AddedBy == user
select org;
var orgsWithUsersProposals = from org in context.Organizations
join p in context.Proposals
where p.AddedBy == user
select org;
var combinedResults = addedOrganizations.Union(orgsWithUsersComments)
.Union(orgsWithUsersProposals);

Categories