We have several classes with multiple 1:1 Relationships for quick joins, and while this works fine for anonymous types for tabular display, I’m unsure how to fully populate the type in a single linq query.
We have these properties either because it’s an off 1:1, or we don’t want to query through a child collection to find a “primary” every display, we instead incur the cost by setting these Primary IDs on save.
A stripped down example for the context of this post:
public class Contact
{
public long Id { get; set; }
public EntitySet<Address> Addresses { get; set; }
public EntityRef<Address> PrimaryAddress { get; set; }
public long? PrimaryAddressId { get; set; }
public EntitySet<Email> Emails { get; set; }
public EntityRef<Email> PrimaryEmail { get; set; }
public long? PrimaryEmailId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Address
{
public long Id { get; set; }
public EntitySet<Contact> Contacts { get; set; }
public bool IsPrimary { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class Email
{
public long Id { get; set; }
public EntitySet<Contact> Contacts { get; set; }
public bool IsPrimary { get; set; }
public string Address { get; set; }
}
The problem is when displaying a list of contacts, the PrimaryAddress and PrimaryEmail have to be lazy loaded. If we do DataLoadOptions it doesn’t give the desired effect either since it’s a 1:1, example:
var DB = new DataContext();
var dlo = new DataLoadOptions();
dlo.LoadWith<Contact>(c => c.PrimaryAddress);
dlo.LoadWith<Contact>(c => c.PrimaryEmail);
DB.LoadOptions = dlo;
var result = from c in DB.Contacts select c;
result.ToList();
The above code results in a INNER JOIN since it treats it like a parent relationship, it doesn’t respect the nullable FK relationship and left join the 1:1 properties. The desired query would be something like:
Select t1.*, t.2*, t3.*
From Contact t1
Left Join Address t2 On t1.PrimayAddressId = t2.Id
Left Join Email On t1.PrimaryEmailId = t3.Id
Is there a way to do this and get a IQueryable with these nullable 1:1 properties populated, or even a List? Due to other constraints, we need the type to be Contact, so anonymous types won’t work. Pretty open to options, anything would be better than lazy loading n*(number of 1:1s)+1 queries for the number of rows we display.
Update: Finally got around to updating this, the devart guys have fixed the behavior in later versions to work perfectly. There’s no need for
DataLoadOptionsat all, just using fields off the table works, for example:This correctly performs a single left outer join to the related
AddressandEmailtables. Now the fix is specific to the situation here of getting this anonymous type…but they also fixed theDataLoadOptionsbehavior where we do need it, correctly keyed off the foreign key type now. Hope this update helps others on an older version…I highly recommend upgrading, there are lots of new enhancements in versions since 5.35 (many making life much easier).Original:
What we ended up with was a different approach. This may be specific behavior to the devart: dotConnect for Oracle provider (as of version 5.35.62, if this behavior changes I’ll try and update this question).
This results in a single query. While calling a child object in the select, e.g.
c.PrimaryAddressdoes not cause a join to occur (resulting in a lot ofselect ... from address where id = nlazy loads, one per row of tabular data we’re displaying), calling a property on it however, e.g.c.PrimaryAddress.Street1DOES cause a correct left join in the address table in the query query. The linq above works only in linq-to-sql, it would fail with null reference on linq-to-entities, but…in the case we’re dealing with that’s fine.The good:
The Bad: