Outer Join tables without foreign key using linq on NHibernate - linq

Im trying to join two tables having a one to many relation but no foreign key.
Only one Order have Comments in this case.
I want all Orders and there Comments, if any.
SELECT O.OrderNo, O.OrderExpeditionDate, C.Comment
FROM [Order] O
LEFT OUTER JOIN OrderComment C
ON O.OrderNo = C.OrderNo
Result
OrderNo OrderExpeditionDate Comment
----------------------------------------------
2222 2008-01-01 Comment 1
2222 2008-01-01 Comment 2
23232 1900-01-12 NULL
77 1900-01-01 NULL
How can I achive this in NHibernate using LINQ/fluent-NHibernate
Class Map:
public class OrderCommentMap : ClassMap<OrderCommen‌​t>
{
public OrderCommentMap()
{
Id(c => c.Id).GeneratedBy.Gu‌​idComb();
Map(c => c.OrderNo);
Map(c => c.Comment);
Map(c => c.Comment2);
}
}

If you can add a navigational property to your OrderComment class:
public virtual Order Order {get;set;}
Then in your mapping replace Map(c => c.OrderNo) with:
References(x => x.Order).Column("orderno");
You then can use Fetch to create an outer join assuming you have a Order navigational property in your OrderComments class:
var results = session.Query<OrderComments>().Fetch(x => x.Order).Where(...);

Related

Linq selecting from multiple joins and returning all rows even if a table doesnt return a match

Consider this structure with 4 tables. childinfo, motherinfo, fatherinfo and guardianinfo.
childinfo table also has motherid, fatherid and guardianid
I am trying to get all data for a child based on ID even if there is nothing in motherinfo, fatherinfo or guardianinfo tables.
My query is like this:
var joined = (from c in _context.ChildInfo.Where(c => c.ChildDob >= dobDtRange.DobStart && c.ChildDob <= dobDtRange.DobEnd)
.OrderByDescending(c => c.ChildId)
join m in _context.MotherInfo.DefaultIfEmpty() on c.MotherId equals m.MotherId into cm
from cmm in cm.DefaultIfEmpty()
join f in _context.FatherInfo.DefaultIfEmpty() on c.FatherId equals f.FatherId into cf
from cff in cf.DefaultIfEmpty()
join g in _context.Guardian.DefaultIfEmpty() on c.GuardianId equals g.GuardianId into cg
from cgg in cg.DefaultIfEmpty()
select new { c, cmm, cff, cgg })
This is not working, what am I doing wrong ?
It seems you have a One-to-Zero-or-One Relationship If your Child has no mother its foreign key MotherId is zero. Some systems have a foreign key value null instead of zero.
To fetch a Child with its Mother, or null Mother if the child has a zero value foreign key MotherId, and similar for Father, etc:
var result = dbContext.Children.Select(child => new
{
// select only the Child properties you actually plan to use
Id = child.Id,
Name = chid.Name,
...
Mother = dbContext.Mothers
.Where(mother => mother.Id == child.MotherId)
.Select(mother => new
{ // select only the mother properties you actually plan to use
Id = mother.Id,
Name = mother.Name,
...
})
.FirstOrDefault(), // will return null if there is no mother for this foreign key
Father = dbContext.Fathers
.Where(father => father.Id == child.FatherId)
.Select(father => new
{ // select only the father properties you actually plan to use
Id = father.Id,
Name = father.Name,
...
})
.FirstOrDefault(), // will return null if there is no father for this foreign key
... etc

How to use condition in LINQ take() method

How to join two tables and get the records based on the class B variable values
Class A
{
RecordID int {get;set;}
Name string {get;set;}
}
Class B
{
Name string {get;set;}
DataToPick int {get;set;}
}
My inputs are
Class A
---------------------
RecordID name
1 Sathish
2 Sathish
3 Kumar
4 Kumar
5 Ajay
Class B
--------------------------------------
Name DataToPick
Sathish 1
kumar 2
Now i want to join two tables and get the values from Class A i.e 3 records, based on the Class B variable (Datatopick)
Please help to write it in LINQ like below
var qry= (from c in classA
join d in ClassB on c.name equals d.name
select c).take(d.datatopick)
You can group ClassA and then select the DataToPick from the other list.
var result =
classA.GroupBy(a => a.Name)
.SelectMany(g => g.Take(classB.Where(b => b.Name == g.Key)
.Select(b => b.DataToPick)
.DefaultIfEmpty(0)
.Single()));
Even better join first and the take:
var result = classA.Join(classB, a => a.Name, b => b.Name, (a, b) => new {a, b.DataToPick})
.GroupBy(arg => new {arg.a.Name, arg.DataToPick})
.SelectMany(g => g.Select(arg => arg.a).Take(g.Key.DataToPick));
You could join classA and classB, group by Name, DataToPick and use Take like that
(from a in listA
join b in listB on a.Name equals b.Name
group new { a, b } by new { a.Name, b.DataToPick } into gr
select new
{
Name = gr.Key.Name,
ListA = gr.Select(x=>x.a).Take(gr.Key.DataToPick)
}
).SelectMany(x => x.ListA).ToList();

EntityFramework Sorting a subset

I have a list of messages. In each message, there are a list of response types. I need to pull the query in such a way that the messages are ordered by ID and the response types are also ordered by ID. They are NOT ordered by ID in the database.
messageResponse.Messages = (from m in db.Messages.Include("ResponseType")
.SomeMagicSubSortThing("ResponseType.ID")
select m).OrderBy(m1 => m1.ID).ToList<Message>();
This should result in:
Message ID Col1 Col2
-- ResponseType ID Col1 Col2
...like so:
1 MessageA MessageB
-- 1 ResponseTypeC ResponseTypeD
-- 2 ResponseTypeQ ResponseTypeR
-- 3 ResponseTypeX ResponseTypeZ
-- 4 ResponseTypeL ResponseTypeM
2 MessageE MessageF
-- 1 ResponseTypeG ResponseTypeH
-- 2 ResponseTypeI ResponseTypeJ
-- 3 ResponseTypeB ResponseTypeS
-- 4 ResponseTypeL ResponseTypeC
Right now, I can get the messages in order, but the response types are in whatever order the database has them. How can I sub-sort the response types?
I think what you are looking for is ThenBy():
messageResponse.Messages = (from m in db.Messages.Include("ResponseType")
.SomeMagicSubSortThing("ResponseType.ID")
select m).OrderBy(m1 => m1.ID).ThenBy(n=>n.ResponseType.ID).ToList<Message>()
UPDATE
In your entity can you simply define a calculated property like:
class Message {
public ICollection<Response> Responses {get; set;}
public ICollection<Response> SoretedResponses {
get { return this.Responses.OrderBy(n=>n.Response); }
}
}
Or am I still missing the issue?
EF doesn't do this nicely, but you do have two options:
1) Use a Select clause in your Linq to Entities query:
var messages = db.Messages
.OrderBy( o => o.ID )
.Select( o => new
{
Message = o,
ResponseTypes = o.ResponseTypes.OrderBy( x => x.ID ),
}
)
.ToList()
;
This is the only way to get EF to sort child properties on the database server.
Your output List will have elements of the anonymous type in the Select
2) Use Linq to Objects on the output List<Message> to sort the collections of ResponseType:
List<Message> messages = db.Messages
.Include( o => o.ResponseTypes )
.OrderBy( o => o.ID )
.ToList()
;
foreach( var message in messages )
{
message.ResponseTypes = message.ResponseTypes.OrderBy( x => x.ID ).ToList();
}
This gets all the records from the database and then sorts them in memory.
The output list will have elements of type Message with the navigation property populated.

NHibernate Fetch / ThenFetch for joined siblings

I have the following (simplified) hierarchy of entities:
RootClass
->DescriptorClass
->SomeChild->DescriptorClass
->SomeGrandChild
I would like to fetch everything in a single query, if possible.
Currently I have the following:
Session.Query<RootClass>().Where(/*some expressions here*/)
.Fetch(v => v.DescriptorClass)
.Fetch(v => v.SomeChild).ThenFetch(v => v.SomeGrandChild)
.Fetch(v => v.SomeChild).ThenFetch(v => v.DescriptorClass);
it works fine but it creates an SQL query with two joins on SomeChild. Obviously, I have to get rid of that second Fetch(v => v.SomeChild) but I cannot find how to do it.
I tried:
Session.Query<RootClass>().Where(/*some expressions here*/)
.Fetch(v => v.DescriptorClass)
.Fetch(v => v.SomeChild).ThenFetch(v => v.SomeGrandChild)
.ThenFetch(v => v.DescriptorClass); //<- wrong, tries to find DescriptorClass on SomeGranchild
and
Session.Query<RootClass>().Where(/*some expressions here*/)
.Fetch(v => v.DescriptorClass)
.Fetch(v => v.SomeChild).ThenFetch(v => v.SomeGrandChild)
.Fetch(v => v.DescriptorClass); //<- wrong, loads the same DescriptorClass of RootClass, not on SomeChild
How do I tell NHibernate to create a single join on SomeChild and then fetch SomeGrandChild and DescriptorClass of SomeChild?
For this type of query, switch to using the LINQ query syntax instead of lambdas as it gives you more control and typically outputs more efficient and cleaner SQL.
Take a look at the sample below and notice how I am able to reference the Customer entity multiple times by using the alias 'c'.
var customers =
(
from c in session.Query<Customer>()
from a in c.Addresses
from pn in c.PhoneNumbers
where c.Status == "Active"
&& a.City == "Dallas"
&& pn.AreaCode == "972"
select c )
.ToList();
This will result in the following SQL using NHibernate:
SELECT
customer0_.CustomerId as Customer1_135_0_,
customer0_.Status as Customer1_135_1_
FROM
Customer customer0_
INNER JOIN
CustomerAddresses customeraddr1_
ON customer0_.CustomerId=customeraddr1_.CustomerId
INNER JOIN
CustomerPhoneNumbers customerphon2_
ON customer0_.CustomerId=customerphon2_.CustomerId
WHERE
customer0_.Status='Active' /* #p0 */
AND customeraddr1_.City = 'Dallas' /* #p1 */
AND customerphon2_.AreaCode = '972' /* #p2 */;

ef and linq extension method

I have this sql that i want to have written in linq extension method returning an entity from my edm:
SELECT p.[Id],p.[Firstname],p.[Lastname],prt.[AddressId],prt.[Street],prt.[City]
FROM [Person] p
CROSS APPLY (
SELECT TOP(1) pa.[AddressId],a.[ValidFrom],a.[Street],a.[City]
FROM [Person_Addresses] pa
LEFT OUTER JOIN [Addresses] AS a
ON a.[Id] = pa.[AddressId]
WHERE p.[Id] = pa.[PersonId]
ORDER BY a.[ValidFrom] DESC ) prt
Also could this be re-written in linq extension method using 3 joins?
Assuming you have set the Person_Addresses table up as a pure relation table (i.e., with no data besides the foreign keys) this should do the trick:
var persons = model.People
.Select(p => new { p = p, a = p.Addresses.OrderByDescending(a=>a.ValidFrom).First() })
.Select(p => new { p.p.Id, p.p.Firstname, p.p.LastName, AddressId = p.a.Id, p.a.Street, p.a.City });
The first Select() orders the addresses and picks the latest one, and the second one returns an anonymous type with the properties specified in your query.
If you have more data in your relation table you're gonna have to use joins but this way you're free from them. In my opinion, this is more easy to read.
NOTE: You might get an exception if any entry in Persons have no addresses connected to them, although I haven't tried it out.

Resources