How can I use a compound condition in a join in Linq? - linq

Let's say I have a Customer table which has a PrimaryContactId field and a SecondaryContactId field. Both of these are foreign keys that reference the Contact table. For any given customer, either one or two contacts may be stored. In other words, PrimaryContactId can never be NULL, but SecondaryContactId can be NULL.
If I drop my Customer and Contact tables onto the "Linq to SQL Classes" design surface, the class builder will spot the two FK relationships from the Customer table to the Contact table, and so the generated Customer class will have a Contact field and a Contact1 field (which I can rename to PrimaryContact and SecondaryContact to avoid confusion).
Now suppose that I want to get details of all the contacts for a given set of customers.
If there was always exactly one contact then I could write something like:
from customer in customers
join contact in contacts on customer.PrimaryContactId equals contact.id
select ...
...which would be translated into something like:
SELECT ...
FROM Customer
INNER JOIN Contact
ON Customer.FirstSalesPersonId = Contact.id
But, because I want to join on both the contact fields, I want the SQL to look something like:
SELECT ...
FROM Customer
INNER JOIN Contact
ON Customer.FirstSalesPersonId = Contact.id OR Customer.SecondSalesPersonId = Contact.id
How can I write a Linq expression to do that?

It's rarely correct to use join in LINQ to SQL.
Since you want contacts, why not start your selection there? Presuming the association between Customer and Contact is two-way, you should be able to write something like:
IEnumerable<Guid> customerIds = // ...
var q = from contact in Context.Contacts
where customerIds.Contains(contact.Customer.Id)
select contact;

Use anonymous classes. EG
new { A.Foo, B.Bar } equals new { Foo = B.Baz, Bar = C.Ork }

Related

Dynamics CRM 2011 Linq Left Outer Join

I am trying to get all records from an entity that do not join to another entity.
This is what I am trying to do in SQL:
SELECT * from table1
LEFT join table2
ON table1.code = table2.code
WHERE table2.code IS NULL
It results in all table1 rows that did not join to table2.
I have it working with Linq when joining on one field, but I have contact records to join on firstname, dob, and number.
I have a "staging" entity that is imported to; a workflow processes the staging records and creates contacts if they are new.
The staging entity is pretty much a copy of the real entity.
var queryable = from staging in linq.mdo_staging_contactSet
join contact in linq.ContactSet
on staging.mdo_code equals contact.mdo_code
into contactGroup
from contact in contactGroup.DefaultIfEmpty()
// all staging records are selected, even if I put a where clause here
select new Contact
{
// import sequence number is set to null if the staging contact joined to the default contact, which has in id of null
ImportSequenceNumber = (contactContactId == null) ? new int?(subImportNo) : null,
/* other fields get populated */
};
return queryable // This is all staging Contacts, the below expressions product only the new Contacts
.AsEnumerable() // Cannot use the below query on IQuerable
.Where(contact => contact.ImportSequenceNumber != null); // ImportSequenceNumber is null for existing Contacts, and not null for new Contacts
Can I do the same thing using method syntax?
Can I do the above and join on multiple fields?
The alternatives I found were worse and involved using newRecords.Except(existingRecords), but with IEnumerables; is there a better way?
You can do the same thing with method calls, but some tend to find it harder to read since there are some LAMBDA expressions in the middle. Here is an example that shows how the two are basically the same.
I've seen others ask this same questions and it boils down to choice by the developer. I personally like the LINQ approach since I also write a bunch of SQL and I can read the code easier.

Active Record Join with most recent association object attribute

I have a Contact model which has many Notes. On one page of my app, I show several attributes of a table of contacts (name, email, latest note created_at).
For the note column, I'm trying to write a joins statement that grabs all contacts along with just their latest note (or even just the created_at of it
What I've come up with is incorrect as it limits and orders the contacts, not their notes:
current_user.contacts.joins(:notes).limit(1).order('created_at DESC')
If you just want the created_at value for the most recent note for each contact, you can first create a query to find the max value and then join with that query:
max_times = Note.group(:contact_id).select("contact_id, MAX(created_at) AS note_created_at").to_sql
current_user.contacts.select("contacts.*, note_created_at").joins("LEFT JOIN (#{max_times}) max_times ON contacts.id = max_times.contact_id")
If you want to work with the Note object for the most recent notes, one option would be to select the notes and group them by the contact_id. Then you can read them out of the hash as you work with each Contact.
max_times = Note.group(:contact_id).select("contact_id, MAX(created_at) AS note_created_at").to_sql
max_notes = Note.select("DISTINCT ON (notes.contact_id) notes.*").joins("INNER JOIN (#{max_times}) max_times ON notes.contact_id = max_times.contact_id AND notes.created_at = note_created_at").where(contact_id: current_user.contact_ids)
max_notes.group_by(&:contact_id)
This uses DISTINCT ON to drop dups in case two notes have exactly the same contact_id and created_at values. If you aren't using PostgreSQL you'll need another way to deal with dups.

LINQ TO Entitites : Combining Two Non Related Tables in Query

I have two tables:
Relationships (ID, UserID, Type, Contact ID)
Contacts (ID, Name, Address)
When the type is 5 in Relationship table, Contact ID is the ID in the Contacts table.
I want to get all the contacts information for a particular user
Here is what I have :
IEnumerable<Relationship> rels = user.Relationships.Where(r => r.Type==5)
foreach (Relationship r in rels)
{
contact = contactRepository.Find(r.ContactID); // Returns Contact Object
Relation relation = new Relation(r, contact);
RelationList.Add(relation);
}
Is this correct way to do this?
I have seen other post mentioning TPC. However, I did not quite understand all that and it seemed TPC only works for code first process.
You can user followng linq statement to get the contacts of a given userID (let's say UserID=15) by using relatonships and contacts tables:
var contacts=from r in Relationships
join c in Contacts on r.ContactID equals c.ID
where r.Type=5 and r.UserID=15
select c;

Linq query that results in one field in parent entity being a collection of child entities

I am trying to write a Linq query that results in a parent entity with one of the fields being a collection of entities of its related children. For example, I have a collection of all customers entities and a collection of all orders entities. The orders entities have a field called customerPK which contains the link to the related parent customer entity. I want to create a Linq query that joins the two collections and results in all the fields of the customer entity plus an additional field which is the collection object of all the related order entities for that specific customer entity.
Hopefully this should do the trick;
EDIT: Code updated to perform a Left Outer Join, based on this example; http://smehrozalam.wordpress.com/2009/06/10/c-left-outer-joins-with-linq/. This now includes Customers who have no orders.
var query = from c in customers
join o in orders on c.ID equals o.CustomerPK into joined
from j in joined.DefaultIfEmpty()
group j by c into g
select new { Customer = g.Key, Orders = g.Where(x => x != null) };
Note, the use of Where on the selection of the Orders grouping is so that null orders are filtered out at this point, instead of ending up with a grouping containing a single null Order for customers who don't have orders.
Then some example usage;
foreach (var result in query)
{
Console.WriteLine("{0} (ID={1})", result.Customer.Name, result.Customer.ID);
foreach (var order in result.Orders)
{
Console.WriteLine(order.Description);
}
}
This example results in an object with two fields, the Customer and then a group of related Orders, but there's no reason why you can't select the individual fields of your customer object in the query as you specified in your post.

Dynamics CRM 2011 - Filtering LINQ query with outer joins

I have a requirement to query for records in CRM that don't have a related entity of a certain type. Normally, I would do this with an Left Outer Join, then filter for all the rows that have NULLs in the right-hand side.
For example:
var query = from c in orgContext.CreateQuery<Contact>()
join aj in orgContext.CreateQuery<Account>()
on c.ContactId equals aj.PrimaryContactId.Id
into wonk
from a in wonk.DefaultIfEmpty()
where a.Name == null
select new Contact
{
FirstName = c.FirstName,
LastName = c.LastName,
};
This should return me any Contats that are not the Primary Contact of an account. However, this query ends up returning all contacts...! When you look at the SQL that gets generated in SQL Profiler it comes out like this:
SELECT cnt.FirstName, cnt.LastName
FROM Contact as cnt
LEFT OUTER JOIN Account AS acct
ON cnt.ContactId = acct.PrimaryContactId AND acct.Name is NULL
So, I get the Left Join OK, but the filter is on the Join clause, and not in a WHERE clause.and not as it should, like this:
SELECT cnt.FirstName, cnt.LastName
FROM Contact as cnt
LEFT OUTER JOIN Account AS acct
ON cnt.ContactId = acct.PrimaryContactId
WHERE acct.Name is NULL
Clearly, the results from this query are very different! Is there a way to get the query on CRM to generate the correct SQL?
Is this a limitation of the underlying FetchXML request?
Unfortunately, this is a limitation of CRM's LINQ and FetchXML implementations. This page from the SDK states outer joins are not supported:
http://technet.microsoft.com/en-us/library/gg328328.aspx
And while I can't find an official document, there are a lot of results out there for people mentioning FetchXML does not support left outer joins, for example:
http://gtcrm.wordpress.com/2011/03/24/fetch-xml-reports-for-crm-2011-online/
Try this:
var query = from c in orgContext.CreateQuery<Contact>()
where orgContext.CreateQuery<Account>().All(aj => c.ContactId != aj.PrimaryContactId.Id)
select new Contact
{
FirstName = c.FirstName,
LastName = c.LastName,
};
If you don't need to update the entity (e.g. to process all the corresponding validation rules and workflow steps), you can write less-ugly and more efficient queries by hitting the SQL Server directly.
Per CRM's pattern, the views take care of most of the common joins for you. For instance, the dbo.ContactBase and dbo.ContactExtensionBase tables are already joined for you in the view dbo.Contact. The AccountName is already there (called AccountIdName for some bizarre reason, but at least it's there).

Resources