NHibernate 3.3 Right Join QueryOver One-to-One Relationship - c#

So, this is the situation. I have a C# project that uses NHibernate 3.3 (upgrading is not an option), with the following classes:
public class Person{
public List<PersonAddress> PersonAddresses {get;set;}
}
public class NaturalPerson : Person{
public NaturalProperty NaturalProperty {get;set;}
}
public class LegalPerson : Person{
public LegalProperty LegalProperty {get;set;}
}
public class Quarantine{
public Person QuarantinePerson {get;set;}
public QuarantineProperty QuarantineProperty {get;set;}
}
What I need to do is to obtain Persons that may or not "be in quarantine" and that satisfy different conditions that involve Quarantine's properties, and legal or natural properties. Basically what I need is a Left Join from Persons, or a Right Join from Quarantines:
Select *
From Quarantine q
Right Join Persons p on p.ID = q.QuarantinePersonID
Left Join NaturalPersons np on p.ID = np.ID
Left Join LegalPersons lp on p.ID = lp.ID
Where q.Property = 1
and np.Property = 1
This is what I have now. I have managed to do the "from" clause that I need, but I'm having serious problems with the "select" and "where" clauses:
Person p = null;
Quarantine q = null;
var results = this.Session.QueryOver<Quarantine>(() => q)
.JoinQueryOver<Person>(() => q.QuarantinePerson, () => p, JoinType.RightOuterJoin)
.SelectList(list => list
.Select(() => p.Id))
.TransformUsing(Transformers.AliasToBean<Person>())
.List<Person>();
This code generates the following query:
`SELECT p1_.PersonID as y0_
FROM BUP.Quarantines this_
right outer join BUP.Persons p1_
on this_.QuarantinePersonID = p1_.PersonID
left outer join BUP.LegalPersons p1_1_
on p1_.PersonID = p1_1_.PersonID
left outer join BUP.NaturalPersons p1_2_
on p1_.PersonID = p1_2_.PersonID`
As I said, the "from" clause is OK. Now the problem is with the Select and Where clauses.
The main problem with the Select is that I need the whole Person object, wether it's a legal or a natural person. I have tried removing the .SelectList from the query, but it throws a PropertyNotFoundException: "Could not find a setter for property 'p' in class 'Person'".
And the problem with the Where clause is that i don't know how to add conditions based on NaturalPerson and LegalPerson's properties. I have no problem filtering Quarantine and Person properties, but haven't yet succedeed to do the same with the other classes.
Also, this query should be as performant as possible, because timeout is a serious problem. I have managed to do other solutions with subqueries and such, but they took too long.
Any help, with any of the two issues, will be very appreciated!
Thanks!!

You can use subqueries mixing with future to execute both queries in one row trip:
var subquery = QueryOver.Of<Quarantine>()
.Select(x => x.QuarantinePerson.Id);
var naturalPersons = Session.QueryOver<NaturalPerson>()
.WithSubquery.WhereProperty(x => x.Id).NotIn(subquery)
//.Where(x => x.NaturalProperty == somehing)
.Future();
var legalPersons = Session.QueryOver<LegalPerson>()
.WithSubquery.WhereProperty(x => x.Id).NotIn(subquery)
//.Where(x => x.LegalProperty == somehing)
.Future();
var persons = naturalPersons.Cast<Person>().Union(legalPersons);

Related

Write a LINQ query for 2 tables

I have got 2 tables, Student and CourseTaken. I need to write a LINQ code that displays all CourseTaken, that has Active student status set as true.
I wrote part of the LINQ statement that will display all CourseTaken for a particular Id. How can I further filter it by showing the coursetaken for Active students? (S_ID in CourseTaken contains the student Id.)
List<CourseTaken> courseTakenList =
await dbcont
.CourseTaken
.Where(c => c.CId == courseId)
.ToListAsync();
public class Student
{
public int Id;
public string Name;
public string School;
public bool Active;
}
public class CourseTaken
{
public int CId;
public string CourseName;
public int S_Id;
}
Note: I need to use LINQ and Lambda expressions.
This will give you a list of all courses that has an active student, this assumes you have a navigation property from courses to student called Students
var result = dbcont.CourseTaken.Where(c => c.Students.Any(s => s.Active));
If this is not correct, i think you need to explain your structure better, whether this is Entity framework and you have the appropriate navigation property, and some example data
Update
No, I don't have navigation properties in place. Is there another way
I could get this done ?
Well you probably should, as you are going to have to query the database twice now.
var ids = dbcont.Students.Where(s => s.Active)
.Select(x => x.id)
.ToList();
var result = dbcont.CourseTaken.Where(c => ids.Contains(c.S_Id));
Lastly, take a look at a few entity framework tutorials, your column naming is a little weird, and you really need to hook this up in the spirit of EF. with navigation properties
It sounds to me that you need this query:
from ct in dbcont.CourseTaken
where ct.CId == courseId
join s in dbcont.Student.Where(s => s.Active) on ct.S_Id equals s.Id into gsc
where gsc.Any()
select ct
This is only returning a CourseTaken once, regardless of how many active students are taking the course, as long as their is at least one, of course.
int[] StudentsId =( from s in dbcont.Students
where s.Active ==true
select s.Id).ToArray<int>();
List<CourseTaken> courseTakenList = dbcont.CourseTaken.
Where(c=> StudentsId.Contains(c.S_Id) )
.ToList();
var result =
(from C in db.CourseTakens
join S in db.Students.Where(s => s.Active == true) on C.S_Id equals S.Id
select C
).ToList();
This can get only CourseTaken data. You can add Student data to select clause.

Lambda Syntax for Joins via Navigation Property?

I can perform a join using a navigation property, which to me is more DRY because I'm not repeating the join criteria everywhere:
(from c in db.Companies
from e in c.Employees
select new { Employee = e, Company = c}).ToList();
Because the ORM knows how Companies is related to Employees, the c.Employees navigation property is used to infer the FK criteria for the join.
What is the direct translation to extension/lambda syntax of a multiple from clause via navigation property?
I know there is a Join extension method, but requires you explicitly name the FK's being compared, rather than it imply that criteria by the navigation property. This is non-working but hopefully expresses my intent:
db.Companies
.Join(c.Employees, /* don't want to explicitly name FKs*/)
.Select(x => new { Employee = x.e, Company = x.c}).ToList();
Of course Join(c.Employees doesn't work because there is no c in this context, but idea being somehow use the Companies.Employees navigation property to imply the join criteria.
I know I can do:
db.Companies.Select(c => new { Employees = c.Employees, Company = c })
but that is a different result set, as it returns one record per company, and then the list of employees as a nested property. Rather than the first which is a join, thus there is a record for every related combination, and the result has a Employee property instead of a Employees collection.
I'm not sure, but guessing .SelectMany is the direct translation. You don't get a c reference to the parent, so if you do multiple of these:
db.Companies.SelectMany(c=>c.Employees).SelectMany(e=>e.VacationDays).Select(v => new { VacationDay = v, Employee = v.Employee, Company = v.Employee.Company })
You have to walk backwards across the relationships to flatten out the join. In linq it's much simpler because you would have c, e and v all in the context of the select. I don't know if you can express the same thing in extension methods, such that all three alias/references are passed down. Maybe just a consequence of the extension method syntax, but hoping someone will provide a better equivalent.
SelectMany is indeed what multiple from clauses are mapped into.
In order to keep variables in scope the projection each SelectMany needs to project the sequence into a new anonymous object that keeps all of the appropriate variables in scope:
var query = db.Companies.SelectMany(company => company.Employees,
(company, employee) => new
{
company,
employee
});
To add additional projections for additional nested navigation properties, simply repeat the pattern with a subsequent call to SelectMany:
var query = db.Companies.SelectMany(company => company.Employees,
(company, employee) => new
{
company,
employee
}).SelectMany(pair => pair.employee.VacationDays,
(pair, vactionDay) => new
{
pair.company,
pair.employee,
vactionDay,
});
See this blog post for some more details and an in-depth description of this transformation, and how it scales.
Wouldn't it just something like:
db.Employees.Select(m => new { Employee = m, Company = m.Company });
As each employee has a Company, why don't just add navigation property "Company" to Employee entity?
To get vacations, just change it to the following:
db.Employees.SelectMany(
employee => employee.VacationDays,
(employee, vacationDay) => new
{
Employee = employee,
Company = employee.Company,
VacationDay = vacationDay
});
Update:
Actually, there is no difference between:
(from c in db.Companies
from e in c.Employees
select new { Employee = e, Company = c}).ToList();
and:
(from e in c.Employees
from c in db.Companies
select new { Employee = e, Company = c}).ToList();

sub linq query is making this take a very long time, how can I make this faster?

I have a list of employees that I build like this:
var employees = db.employees.Where(e => e.isActive == true).ToList();
var latestSales = from es in db.employee_sales.Where(x => x.returned == false);
Now what I want is a result like this:
int employeeId
List<DateTime> lastSaleDates
So I tried this, but the query takes a very very long time to finish:
var result =
(from e in employees
select new EmployeeDetails
{
EmployeeId = e.employeeId,
LastSaleDates =
(from lsd in latestSales.Where(x => x.EmployeeId == e.EmployeeId)
.Select(x => x.SaleDate)
select lsd).ToList()
};
The above works, but literally takes 1 minute to finish.
What is a more effecient way to do this?
You can use join to get all data in single query
var result = from e in db.employees.Where(x => x.isActive)
join es in db.employee_sales.Where(x => x.returned)
on e.EmployeeId equals es.EmployeeId into g
select new {
EmployeeId = e.employeeId,
LastSaleDates = g.Select(x => x.SaleDate)
};
Unfortunately you can't use ToList() method with Linq to Entities. So either map anonymous objects manually to your EmployeeDetails or change LastSalesDates type to IEnumerable<DateTime>.
Your calls to ToList are pulling things into memory. You should opt to build up a Linq expression instead of pulling an entire query into memory. In your second query, you are issuing a new query for each employee, since your are then operating in the Linq-to-objects domain (as opposed to in the EF). Try removing your calls to ToList.
You should also look into using Foreign Key Association Properties to makes this query a lot nicer. Association properties are some of the most powerful and useful parts of EF. Read more about them here. If you have the proper association properties, your query can look as nice as this:
var result = from e in employees
select new EmployeeDetails
{
EmployeeId = e.employeeId,
LastSaleDates = e.AssociatedSales
}
You might also consider using a join instead. Read about Linq's Join method here.
Is there an association in your model between employees and latestSales? Have you checked SQL Profiler or other profiling tools to see the SQL that's generated? Make sure the ToList() isn't issuing a separate query for each employee.
If you can live with a result structure as IEnumerable<EmployeeId, IEnumerable<DateTime>>, you could consider modifying this to be:
var result = (from e in employees
select new EmployeeDetails
{
EmployeeId = e.employeeId,
LastSaleDates = (from lsd in latestSales
where e.employeeId equals lsd.EmployeeId
select lsd.SaleDate)
};
I have some more general recommendations at http://www.thinqlinq.com/Post.aspx/Title/LINQ-to-Database-Performance-hints to help track issues down.

Entity Framework with Include / Joining Issues

I've implemented an Table Inheritance functionality demonstrated at http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server.
All the foreign keys and constraints are in place.
Now I am using Entity Framework to pull back my People, Students, Teachers, and Parents where the model looks something like the following (without all the EF specific attributes etc).
public partial class People : EntityObject
{
public guid PeopleID { get; set; }
public int Age { get; set; } /Added for an example query
public PeopleParent Parent { get; set; }
public PeopleStudent Student { get; set; }
public PeopleTeacher Teacher { get; set; }
}
Now I need to get all People regardless of type, who are 25 years old, no more than 100 records, and I want to include all the referenced data. I create my EF Query like:
IQueryable<People> query = Entities.People.Include("PeopleParent")
.Include("PeopleStudent")
.Include("PeopleTeacher");
query.Where(x => x.Age == 25)
.Take(100);
IEnumerable<People> results = query.ToList();
Seems simple enough, but whatever table/entityset I've set to include first creates an INNER JOIN instead of the LEFT OUTER JOIN, which is not producing the right results.
Generated TSQL (incorrect for my needs):
SELECT
[Limit1].[C1] AS [C1],
<A bunch of Limit1 Columns>
FROM (
SELECT TOP (100)
[Extent1].[PeopleID] AS [PeopleID],
<A bunch of Extent1 Columns>
[Extent2].[PeopleID] AS [PeopleID1],
<A bunch of Extent2 Columns>
[Extent3].[PeopleID] AS [PeopleID2],
<A bunch of Extent3 Columns>
[Extent4].[PeopleID] AS [PeopleID3],
<A bunch of Extent4 Columns>
1 AS [C1]
FROM [rets].[People] AS [Extent1]
INNER JOIN [rets].[PeopleParent] AS [Extent2]
ON [Extent1].[PeopleID] = [Extent2].[PeopleID]
LEFT OUTER JOIN [rets].[PeopleStudent] AS [Extent3]
ON [Extent1].[PeopleID] = [Extent3].[PeopleID]
LEFT OUTER JOIN [rets].[PeopleTeacher] AS [Extent4]
ON [Extent1].[PeopleID] = [Extent4].[PeopleID]
) AS [Limit1]
Why is the first Include used as an INNER JOIN, and is there a soluion to my problem?
** UPDATE 1**
Assuming I use Ladislav Mrnka's Answer, there are two additional requirements due to the significant change in Linq and Lambda querying.
Question: How do I search for specific People which have specific properties?
(All Students with a Grade of "A")
Answer:
context.People.OfType<Student>().Where(s => s.Grade == "A");
Question: How do I search for any People that have a specific property?
(All Students or Teachers who's PrimaryFocus = "Math")
Answer:
List<People> result = new List<People>();
result.AddRange(context.People.OfType<Student>()
.Where(x => x.PrimaryFocus == "Math")
.ToList());
result.AddRange(context.People.OfType<Teacher>()
.Where(x => x.PrimaryFocus == "Math")
.ToList());
The obvious solution for you should be using native EF support for inheritance. In your case TPT inheritance. Once you have inheritance you will simply call:
IEnumerable<People> results = Entities.People
.Where(x => x.Age == 25)
.Take(100)
.ToList();
And it will return you instances of Student, Teachers, Parents etc.
In your solution the only advice is check that the relation is optional (1 - 0..1) - . If it is required it will use INNER JOIN. If it is optional and it still uses INNER JOIN it can be some bug or other problem in your model.

How to do joins in LINQ on multiple fields in single join

I need to do a LINQ2DataSet query that does a join on more than one field (as
var result = from x in entity
join y in entity2
on x.field1 = y.field1
and
x.field2 = y.field2
I have yet found a suitable solution (I can add the extra constraints to a where clause, but this is far from a suitable solution, or use this solution, but that assumes an equijoin).
Is it possible in LINQ to join on multiple fields in a single join?
EDIT
var result = from x in entity
join y in entity2
on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
is the solution I referenced as assuming an equijoin above.
Further EDIT
To answer criticism that my original example was an equijoin, I do acknowledge that, My current requirement is for an equijoin and I have already employed the solution I referenced above.
I am, however, trying to understand what possibilities and best practices I have / should employ with LINQ. I am going to need to do a Date range query join with a table ID soon, and was just pre-empting that issue, It looks like I shall have to add the date range in the where clause.
Thanks, as always, for all suggestions and comments given
var result = from x in entity
join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }
var result = from x in entity1
join y in entity2
on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
You need to do this, if the column names are different in two entities.
The solution with the anonymous type should work fine. LINQ can only represent equijoins (with join clauses, anyway), and indeed that's what you've said you want to express anyway based on your original query.
If you don't like the version with the anonymous type for some specific reason, you should explain that reason.
If you want to do something other than what you originally asked for, please give an example of what you really want to do.
EDIT: Responding to the edit in the question: yes, to do a "date range" join, you need to use a where clause instead. They're semantically equivalent really, so it's just a matter of the optimisations available. Equijoins provide simple optimisation (in LINQ to Objects, which includes LINQ to DataSets) by creating a lookup based on the inner sequence - think of it as a hashtable from key to a sequence of entries matching that key.
Doing that with date ranges is somewhat harder. However, depending on exactly what you mean by a "date range join" you may be able to do something similar - if you're planning on creating "bands" of dates (e.g. one per year) such that two entries which occur in the same year (but not on the same date) should match, then you can do it just by using that band as the key. If it's more complicated, e.g. one side of the join provides a range, and the other side of the join provides a single date, matching if it falls within that range, that would be better handled with a where clause (after a second from clause) IMO. You could do some particularly funky magic by ordering one side or the other to find matches more efficiently, but that would be a lot of work - I'd only do that kind of thing after checking whether performance is an issue.
Just to complete this with an equivalent method chain syntax:
entity.Join(entity2, x => new {x.Field1, x.Field2},
y => new {y.Field1, y.Field2}, (x, y) => x);
While the last argument (x, y) => x is what you select (in the above case we select x).
I think a more readable and flexible option is to use Where function:
var result = from x in entity1
from y in entity2
.Where(y => y.field1 == x.field1 && y.field2 == x.field2)
This also allows to easily change from inner join to left join by appending .DefaultIfEmpty().
var result = from x in entity
join y in entity2
on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
select new
{
/// Columns
};
you could do something like (below)
var query = from p in context.T1
join q in context.T2
on
new { p.Col1, p.Col2 }
equals
new { q.Col1, q.Col2 }
select new {p...., q......};
Using the join operator you can only perform equijoins. Other types of joins can be constructed using other operators. I'm not sure whether the exact join you are trying to do would be easier using these methods or by changing the where clause. Documentation on the join clause can be found here. MSDN has an article on join operations with multiple links to examples of other joins, as well.
If the field name are different in entities
var result = from x in entity
join y in entity2 on
new {
field1= x.field1,
field2 = x.field2
}
equals
new {
field1= y.field1,
field2= y.myfield
}
select new {x,y});
As a full method chain that would look like this:
lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
(a, b) => new ResultItem
{
Id = a.Id,
ATotal = a.Total,
BTotal = b.Total
}).ToList();
I used tuples to do that, this is an example for two columns :
var list= list1.Join(list2,
e1 => (e1.val1,e1.val2),
e2 => (e2.val1,e2.val2),
(e1, e2) => e1).ToList();
from d in db.CourseDispatches
join du in db.DispatchUsers on d.id equals du.dispatch_id
join u in db.Users on du.user_id equals u.id
join fr in db.Forumreports on (d.course_id + '_' + du.user_id) equals (fr.course_id + '_'+ fr.uid)
this works for me
Declare a Class(Type) to hold the elements you want to join. In the below example declare JoinElement
public class **JoinElement**
{
public int? Id { get; set; }
public string Name { get; set; }
}
results = from course in courseQueryable.AsQueryable()
join agency in agencyQueryable.AsQueryable()
on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper }
equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

Categories