Nhibernate Join 2 QueryOver - nhibernate

I can't find how to join two different QueryOver, group by and perform a substraction in the select.
Say you have :
public class EntityA
{
public virtual int Id;
public virtual string Reference;
public virtual int Quantity;
[Some properties]
}
public class EntityB
{
public virtual int Id;
public virtual int EntityAId;
[Some properties]
}
If i translate my query in pseudo-SQL, i would like to have :
SELECT A.Id, A.Reference, A.Quantity - COALESCE(DERIV_B.TOTAL, 0)
FROM EntityA A
LEFT JOIN (
SELECT B.EntityAId, COUNT(B.Id) AS TOTAL
FROM EntityB B
GROUP BY B.EntityAId) DERIV_B
ON A.Id = DERIV_B.EntityAId
WHERE (A.Quantity - COALESCE(DERIV_B.TOTAL, 0)) >= 0
I can have the subquery on EntityB via QueryOver, but i can't join on EntityA :
var entitiesB = GetCurrentSession().QueryOver<EntityB>().SelectList(select => select.SelectGroup(x => x.EntityAId).SelectCount(x => x.Id));
var entitiesA = GetCurrentSession().QueryOver<EntityA>(). ???
I tried to store the entitiesB in an alias and the perform a JoinAlias on it but i have an exception because it can't retrieve my alias.
Do you have any solution ?
I don't want to create a reference between these two entites.

Short answer is not , you can't do QueryOver if your entities are not connected through model.
One solution would be to use NHibernate.Linq and subqueries
var session = GetCurrentSession();
var entityBQuery = session.Query<EntityB>();
var entityAQuery = session.Query<EntityA>()
.Select(eA=>new { Id = eA.Id,
Description = eA.Description,
Quantity = eA.Quantity - entityBQuery.Where(eb=>eb.EntityAId = eA.Id).Count()
});

Related

Ef core search on child , order on parent

I have two model Parent and Child and I want to retrieve all Distinct parent that has unitid = 5 and order by family
public class Parent
{
public int Id { get; set; }
public string Family { get; set; }
public List<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public int UnitId { get; set; }
public Parent parent { get; set; }
}
parent table has 500000 record and child table has 2000000 record . I use SelectMany And Distinct but it take 8s to retrieve records
var parents= Context.Set<Parent>()
.SelectMany(x => x.Children.DefaultIfEmpty(),
(u, r) => new {
Parent = u,
Child = r })
.Where(x=>x.Child.UnitId == 5)
.Select(m => new Parent{
Id = m.Parent.Id,
Family= m.Parent.Family
})
.Distinct()
.OrderBy(x=>x.Family)
.Take(30).Skip(0).ToList();
Tranlate to :
SELECT [t].[Id], [t].[Family] FROM (
SELECT distinct [a].[Id], [a].[Family]
FROM [Parent] AS [a]
LEFT JOIN [Children] AS [r] ON [a].[Id] = [r].[ParentId]
WHERE ([r].[unitid] = 5) AS [t] ORDER BY [t].[family] OFFSET 0 ROWS FETCH next 30 ROWS ONLY
Or
var parents = Context.Set<Children>()
.Include(x=>x.Parent)
.Where(x=>x.UnitId ==5)
.Select(m => new Parent{
Id = m.ParentId ,
Family = m.Parent.Family})
.Distinct()
.OrderBy(x=>x.Family)
.Take(30).Skip(0).ToList();
Translatr to :
SELECT [t0].[Id],[t0].[Family] FROM ( SELECT DISTINCT [t].[Id], [t].[Family]
FROM [Children] AS [r]
INNER JOIN (
SELECT [a].[Id] , [a].[Family]
FROM [Parent] AS [a]
) AS [t] ON [r].[ParentId] = [t].[Id]
WHERE ([r].[UnitId] = 5) AS [t0] ORDER BY [t0].[Family] OFFSET 0 ROWS FETCH next 30 ROWS ONLY
1 . When use order by id it is very fast
2 . when use order by family and take,skip it is slow
3 . family is indexed
4 . Ef Core 3.1
Which solution is Correct or better ?
Check these two queries for performance:
var parents =
from p in Context.Set<Parent>()
where p.Children.Any(c => c.UnitId == 5)
orderby p.Family
select p;
parents = parents
.Take(30)
.Skip(0)
.ToList();
And another one:
var parentIds =
from c in Context.Set<Child>()
where c.UnitId == 5
select c.ParentId
var parents =
from p in Context.Set<Parent>()
join id in parentIds.Distinct() on p.Id equals id
orderby p.Family
select p;
parents = parents
.Take(30)
.Skip(0)
.ToList();

What is the Linq Query of the following SQL

I am new to nHibernate. I have following SQL inner join query,
SELECT e.name,
d.deptname
FROM demo_employee AS e
INNER JOIN demo_department AS d
ON e.departmentid = d.deptid
What is the linq expression using Query Over of the following sql query.
I have written the following query but it is qiving me error at following place "c.Department.DeptId".
var Query =
Session.QueryOver<Employee>()
.JoinQueryOver<Department>(c => c.Department.DeptId)
.Where(k => k.Name == k.DeptId);
Here is the QueryOver version.
We are using aliasing, ie: Declaration of the null reference Employee employee = null to be used for fully-typed access to all properties. NHibernate Expression parser will convert them into strings (column names) later based on mapping.
Also we could get references to FROM parts of the QueryOver. The query represents Employee, and the joined query represents the Department (depQuery), which we can directly filter.
Finally we can use List() to get (SELECT) all mapped properties, or do some projection: .Select() or .SelectList(). With projection, we should be working with some DTO.
// aliasing, see the Projections of the SELECT clause
Employee employee = null;
Department department = null;
// the Employee query, with alias
var query = session.QueryOver<Employee>(() => employee);
// this way we can have reference to department query, if needed
var depQuery = query.JoinQueryOver<Department>(() => employee.Department, () => department);
// WHERE
// filtering the Employee
query.Where(e => e.Name == "Undefined");
// the department filtering
depQuery.Where(d => d.DeptName == "Management");
// paging, if needed
query.Skip(100);
query.Take(10);
1) select all properties
var list = query.List<Employee>();
var employeeName = list.ElementAt(0).Name;
var departmentName = list.ElementAt(0).Department.DeptName;
2) projection
// The DTO class to be projected into
public class MyDTO
{
public virtual string EmployeeName { get; set; }
public virtual string DepartmentName { get; set; }
}
// Select with projection of just two columns
MyDTO dto = null;
// SELECT
// projection, explicit property/column to be selected only
query.SelectList(l => l
// the full power of aliasing
.Select(() => employee.Name).WithAlias(() => dto.EmployeeName)
.Select(() => department.DeptName).WithAlias(() => dto.DepartmentName)
);
var list = query
.TransformUsing(Transformers.AliasToBean<MyDTO>())
.List<MyDTO>();

Empty result set when Joining two table with Non-match Foreign key

I am joining two tables using a foreign key. TABLE_1 might have a row with a null for the foreign key. Which means that when I join the two tables based on that foreign key I won't get results for it. My problem is that when I use JOIN two tables in LINQ, I get an empty result set.
I want to be able to get the row in TABLE_1 even if the JOIN result with no match with TABLE_2.
I tried to use DefaultIfEmpty in the join of TABLE_2, but I still get an empty result set. How can I join two tables and still get a result even if TABLE_1 contains a null in the foreign key which I use to JOIN the two table?
Thanks
Hi try left join from Table2 to Table1
class Program
{
static void Main(string[] args)
{
List<Table1> Table_1 = new List<Table1>();
Table_1.Add(new Table1() { Id = 1, Name = "Lion" });
Table_1.Add(new Table1() { Id = 2, Name = "Elephant" });
List<Table2> Table_2 = new List<Table2>();
Table_2.Add(new Table2() { Id = 1, Class = "Carnivorous" });
Table_2.Add(new Table2() { Id = 2, Class = "Herbivorous" });
Table_2.Add(new Table2() { Id = 3, Class = "Mammal" });
Table_2.Add(new Table2() { Id = 4, Class = "Aquarious" });
var result = (from a in Table_2
join b in Table_1
on a.Id equals b.Id into leftJoin
from c in leftJoin.DefaultIfEmpty()
select new { Id = a.Id, Name = c == null ? string.Empty : c.Name, Class = a.Class }
).ToList();
var abc = result;
}
}
public class Table1
{
public int Id;
public string Name;
}
public class Table2
{
public int Id;
public string Class;
}
Try RIGHT JOIN if LEFT JOIN doesn't work.

Linq to sql How to select elements from a list

I have two classes:
public class Artikel
{
int id{get;set;};
string name{get;set;};
}
public class NewArtikel
{
int id{get;set;};
List<Artikel> artikels{get;set;};
}
Now, I have a list of the second class
List<NewArtikel> myList = new List<NewArtikel>()
Is there are any posibility in LINQ to SQL to select all elements from myList with the needed Artikel.name
Yes
var allRequiredArtikels = myList.SelectMany(n => n.artikels)
.Where(a => a.name == "requiredName");
The SelectMany flattens all Artikels from all the NewArtikel elements, then the Where filters them by name.
Okay so, if you want the NewArtikels that have an Artikel with the required name you could do.
var newArtikels = myList.Where(n => n.artikels
.Any(a => a.name == "requiredName"));
List<NewArtikel> myFinalList = (from artikel in myList.artikels where artikel.name = "requiredName"
select new NewArtikel(){
Id= artikel.Id
}).ToList();

How can I make a nested projection in a Linq query when using the group by clause?

I'm trying to work with grouped data coming back from SQL.
The method I'm writing is to provide the data for a "Case Status Overview" screen.
It must produce a nested XML document.
Now, I could do it the easy way, but I'm trying to learn whether it's possible to use the linq "group by" statement and then to project the data already nested. (the easy way would be just to pull back the data in a tabular fashion from the database and then for-loop through it forming the Xml document for output)
Here is the data hierarchy:
Every Case has a DebtType and every DebtType has a Client.
Here is the SQL that retrieves the data:
SELECT ClientNames.ClientID ,
ClientNames.ClientCode ,
ClientNames.ClientName ,
DebtTypes.DebtTypeID ,
DebtTypes.DebtTypeShortDesc ,
DebtTypes.DebtTypeLongDesc ,
Cases.CurrentStateCode ,
SUM(1 - CAST(Cases.CaseClosed AS INT)) AS OpenCaseCount ,
SUM(CAST(Cases.CaseClosed AS INT)) AS ClosedCaseCount ,
SUM(CAST(Cases.CaseOnHold AS INT)) AS OnHoldCaseCount ,
SUM(CAST(Cases.CaseReferred AS INT)) AS ReferredCaseCount ,
COUNT(Cases.CaseID) AS TotalCaseCount ,
SUM(Cases.CaseTotalPaid) AS TotalAmountPaid ,
SUM(Cases.CaseCurrentOutstandingAmount) AS TotalAmountOutstanding,
SUM(Cases.CaseTotalDebtWrittenOff) AS TotalAmountWrittenOff ,
SUM(Cases.CaseTotalDebtCancelled) AS TotalAmountCancelled
FROM ClientNames
INNER JOIN ClientDebtTypes
ON ClientNames.ClientID = ClientDebtTypes.ClientID
INNER JOIN DebtTypes
ON ClientDebtTypes.DebtTypeID = DebtTypes.DebtTypeID
INNER JOIN Cases
ON ClientDebtTypes.ClientDebtTypeID = Cases.CaseClientDebtTypeID
GROUP BY ClientNames.ClientID ,
ClientNames.ClientCode ,
ClientNames.ClientName ,
DebtTypes.DebtTypeID ,
DebtTypes.DebtTypeShortDesc,
DebtTypes.DebtTypeLongDesc ,
Cases.CurrentStateCode
ORDER BY ClientNames.ClientID,
DebtTypes.DebtTypeID,
CurrentStateCode
Using Linqer it converts it to:
from clientnames in db.ClientNames
join clientdebttypes in db.ClientDebtTypes on clientnames.ClientID equals clientdebttypes.ClientID
join debttypes in db.DebtTypes on clientdebttypes.DebtTypeID equals debttypes.DebtTypeID
join cases in db.Cases on new { ClientDebtTypeID = clientdebttypes.ClientDebtTypeID } equals new { ClientDebtTypeID = cases.CaseClientDebtTypeID }
group new {clientnames, debttypes, cases} by new {
clientnames.ClientID,
clientnames.ClientCode,
clientnames.ClientName1,
debttypes.DebtTypeID,
debttypes.DebtTypeShortDesc,
debttypes.DebtTypeLongDesc,
cases.CurrentStateCode
} into g
orderby
g.Key.ClientID,
g.Key.DebtTypeID,
g.Key.CurrentStateCode
select new {
ClientID = (System.Int32?)g.Key.ClientID,
g.Key.ClientCode,
g.Key.ClientName1,
DebtTypeID = (System.Int32?)g.Key.DebtTypeID,
g.Key.DebtTypeShortDesc,
g.Key.DebtTypeLongDesc,
g.Key.CurrentStateCode,
OpenCaseCount = (System.Int64?)g.Sum(p => 1 - Convert.ToInt32(p.cases.CaseClosed)),
ClosedCaseCount = (Int32?)g.Sum(p => Convert.ToInt32(p.cases.CaseClosed)),
OnHoldCaseCount = (Int32?)g.Sum(p => Convert.ToInt32(p.cases.CaseOnHold)),
ReferredCaseCount = (Int32?)g.Sum(p => Convert.ToInt32(p.cases.CaseReferred)),
TotalCaseCount = (Int64?)g.Count(p => p.cases.CaseID != null),
TotalAmountPaid = (System.Decimal?)g.Sum(p => p.cases.CaseTotalPaid),
TotalAmountOutstanding = (System.Decimal?)g.Sum(p => p.cases.CaseCurrentOutstandingAmount),
TotalAmountWrittenOff = (System.Decimal?)g.Sum(p => p.cases.CaseTotalDebtWrittenOff),
TotalAmountCancelled = (System.Decimal?)g.Sum(p => p.cases.CaseTotalDebtCancelled)
}
Now as I mentioned, I could stop there and right a for loop to create the Xml data.
But I'm trying to create a nested group (IGrouping<ClientName,IGrouping<DebtType,SummaryClass>>)
and then project the data in a nested format.
Now we're using LinqToXsd to create strong type wrappers for out Xml documents, but essentially all this means is that out output type is:
private class ClientSummary
{
public string ClientName { get; set; }
public IList<DebtTypeSummary> DebtTypes { get; set; }
}
private class DebtTypeSummary
{
public string DebtType { get; set; }
public IList<StateCodeSummary> StateCodes { get; set; }
}
private class StateCodeSummary
{
public string StateCode { get; set; }
public int TotalCount { get; set; }
public decimal TotalAmountPaid { get; set; }
//etc
//etc
//etc
}
Now I got as far as writing the following Linq:
var grouping = from cases in db.Cases
join clientdebttypes in db.ClientDebtTypes on cases.CaseClientDebtTypeID equals clientdebttypes.ClientID
join debttypes in db.DebtTypes on clientdebttypes.DebtTypeID equals debttypes.DebtTypeID
group cases by new ClientDebtTypePair() { ClientDebtType = clientdebttypes, DebtType = debttypes } into casesByClientDebtTypes
join clientnames in db.ClientNames on casesByClientDebtTypes.Key.ClientDebtType.ClientName equals clientnames
group casesByClientDebtTypes by clientnames;
var projected = from casesByClientDebtTypes in grouping
let client = casesByClientDebtTypes.Key
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = new Client()
{
ClientID = client.ClientID,
DisplayName = client.ClientName1,
},
DebtTypes = from cases in casesByClientDebtTypes
let debttype = cases.Key.DebtType
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType()
{
DebtType = new DebtType()
{
DebtTypeID = debttype.DebtTypeID,
Description = debttype.DebtTypeLongDesc,
DisplayName = debttype.DebtTypeShortDesc,
},
StatesCodes = from cases2 in cases
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType.StatesCodesLocalType()
{
ClosedCasesCount = cases2.Sum(p => Convert.ToInt32(p.cases.CaseClosed))
which joins and groups the database tables and then tries to project the result a ClientSummary (the class names are different but that's because the above is a simplified view of the output classes). I fail completely when I've drilled all the way down to the Cases table and I find that I don't really understand how to do agregate functions. They appear to only be available on IGrouping<K, T>s and it seems I've just got confused.
I need to also ensure that the summaries are calculated server side, pulling back millions of cases would be bad.
Can anybody help me with this one? Is this even possible?
Regards,
James.
-------### UPDATE 1 ###-------
OK, been working on this again today.
I decided to use Linq2SQL to pull pack 2D data and then reformat it using Linq2Objects.
Here is what I started with:
var sql = from clientnames in db.ClientNames
join clientdebttypes in db.ClientDebtTypes on clientnames.ClientID equals clientdebttypes.ClientID
join debttypes in db.DebtTypes on clientdebttypes.DebtTypeID equals debttypes.DebtTypeID
join cases in db.Cases on new { ClientDebtTypeID = clientdebttypes.ClientDebtTypeID } equals new { ClientDebtTypeID = cases.CaseClientDebtTypeID }
group new { clientnames, debttypes, cases } by new
{
clientnames.ClientID,
clientnames.ClientCode,
clientnames.ClientName1,
debttypes.DebtTypeID,
debttypes.DebtTypeShortDesc,
debttypes.DebtTypeLongDesc,
cases.CurrentStateCode
} into g
orderby
g.Key.ClientID,
g.Key.DebtTypeID,
g.Key.CurrentStateCode
select new
{
Client = new Client{ ClientID = g.Key.ClientID, DisplayName = g.Key.ClientName1 },
DebtType = new DebtType{ DebtTypeID = g.Key.DebtTypeID, DisplayName = g.Key.DebtTypeShortDesc, Description = g.Key.DebtTypeLongDesc },
StateSummary = new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType.StatesCodesLocalType()
{
StateCode = g.Key.CurrentStateCode,
OpenCasesCount = g.Sum(p => 1 - Convert.ToInt32(p.cases.CaseClosed)),
ClosedCasesCount = g.Sum(p => Convert.ToInt32(p.cases.CaseClosed)),
OnHoldCasesCount = g.Sum(p => Convert.ToInt32(p.cases.CaseOnHold)),
ReferredCasesCount = g.Sum(p => Convert.ToInt32(p.cases.CaseReferred)),
TotalCasesCount = g.Count(p => p.cases.CaseID != null),
TotalAmountPaid = g.Sum(p => p.cases.CaseTotalPaid),
TotalAmountOutstanding = g.Sum(p => p.cases.CaseCurrentOutstandingAmount),
TotalAmountWrittenOff = g.Sum(p => p.cases.CaseTotalDebtWrittenOff),
TotalAmountCancelled = g.Sum(p => p.cases.CaseTotalDebtCancelled),
}
};
var res = sql.ToList();
output.Clients = (from results in res
group results by results.Client into resultsByClient
from resultsByDebtType in
(from results in resultsByClient
group results by results.DebtType)
group resultsByDebtType by resultsByClient.Key into resultsByDebtTypeByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = resultsByDebtTypeByClient.Key,
DebtTypes = (from resultsByDebtType in resultsByDebtTypeByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType()
{
DebtType = resultsByDebtType.Key,
StatesCodes = (from results in resultsByDebtType
let summary = results.StateSummary
select results.StateSummary).ToList()
}).ToList()
}).ToList();
That runs, but produces one Client/DebtType/Summary set for every result. So even though there is only one client in this case, I end up with 1300 clients, all identical.
I simplified it to the following:
output.Clients = (from results in res
group results by results.Client into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = resultsByClient.Key,
DebtTypes = null,
}).ToList();
That produces 1300 clients. Next I tried this:
output.Clients = (from results in res
group results by results.Client.ClientID into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = new Client { ClientID = resultsByClient.Key },
DebtTypes = null,
}).ToList();
And THAT produces ONE client (hurray!). Except I loose all the client information (boo!)
Guessing that as it's comparing client by refernce instead of by content I wrote the following:
public partial class Client
{
public static bool operator ==(Client left, Client right)
{
return left.ClientID == right.ClientID;
}
public static bool operator !=(Client left, Client right)
{
return left.ClientID != right.ClientID;
}
public override int GetHashCode()
{
return ClientID;
}
}
That did nothing. It repeatedly calls GetHashCode(), which I fudged to force it to return the same hash code for any matching ClientID, but it still created 1300 Client groups.
Regards,
James.
-------### UPDATE 2 ###-------
OK, I thought I would have a go at making the Linq2Sql output only simple values for grouping by:
g.Key.ClientID,
g.Key.ClientName1,
g.Key.DebtTypeID,
g.Key.DebtTypeShortDesc,
g.Key.DebtTypeLongDesc,
And then changed the test Linq2Objects to:
output.Clients = (from results in res
group results by new { ClientID = results.ClientID, DisplayName = results.ClientName1 } into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = new Client { ClientID = resultsByClient.Key.ClientID, DisplayName = resultsByClient.Key.DisplayName },
DebtTypes = null,
}).ToList();
That works. So anonymous types compare in the way I want them to, by content not reference (apparently)
This does not:
output.Clients = (from results in res
group results by new SiDemClient { ClientID = results.ClientID, DisplayName = results.ClientName1 } into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = resultsByClient.Key,//new Client { ClientID = resultsByClient.Key.ClientID, DisplayName = resultsByClient.Key.DisplayName },
DebtTypes = null,
}).ToList();
That still creates 1300 groups.
So, anonymous types compare in a magical way that I don't understand. How can I make my Client class compare like an anonymous type?
Regards,
James.
-------### SOLUTION FOUND ###-------
-------### MANY THANKS TO Enigmativity ###-------
I needed to override the Equals() method instead of implementing the == operator.
Now the grouping works and I have a wonderful Xml document to reutrn!
public partial class SiDemClient
{
public override bool Equals(object obj)
{
if (obj is SiDemClient)
{
return this.ClientID.Equals(((SiDemClient)obj).ClientID);
}
return false;
}
public override int GetHashCode()
{
return ClientID;
}
}
Many Thanks,
James.
When you override GetHashCode you must also override Equals. The == & != operators are irrelevant.
Try with this:
public partial class Client
{
public override bool Equals(object obj)
{
if (obj is Client)
{
return this.ClientID.Equals(((Client)obj).ClientID);
}
return false;
}
public override int GetHashCode()
{
return this.ClientID.GetHashCode();
}
}
See if that helps.