NHibernate selecting from a many to many collection - nhibernate

I would like NHibernate to generate the following SQL but I'm stuggling with the code below
SELECT rt.Id
FROM ClientContact cc
JOIN ClientContact_DefaultRequest d on d.ClientContactID = cc.Id
JOIN RequestType rt on d.RequestTypeId = rt.Id
WHERE
cc.Id = ContactId
public class GetDefaultRequestsForContact : SimpleQuery<IEnumerable<RequestType>>, IGetDefaultRequestsForContact
{
public int ContactId { private get; set; }
public GetDefaultRequestsForContact(ISession session) : base(session) { }
public override IEnumerable<RequestType> Execute()
{
var x = Session.QueryOver<ClientContact>()
.Where(c => c.Id == ContactId)
.JoinQueryOver<RequestType>(c => c.DefaultRequests)
.Select(c => c.DefaultRequests)
.List();
return null;
}
}
The generated SQL is only selecting the parent id (which is the parameter) I want all the child Ids.
SELECT this_.Id as y0_
FROM ClientContact this_
inner join ClientContact_DefaultRequest defaultreq3_
on this_.Id = defaultreq3_.ClientContactID
inner join RequestType requesttyp1_
on defaultreq3_.RequestTypeID = requesttyp1_.Id
WHERE this_.Id = 313706 /* #p0 */
This works but it is not strongly typed (HQL).
var x = Session.CreateQuery("SELECT R.Id FROM ClientContact cc JOIN cc.DefaultRequests R WHERE cc.Id = :contactId")
.SetParameter("contactId",ContactId)
.List<int>();
return x;

Try using Aliases like:
ClientContact Cont = null;
RequestType Req = null;
var x = session.QueryOver<ClientContact>(() => Cont)
.Where(() => Cont.ID == ContactId)
.JoinAlias(() => Cont.DefaultRequests, ()=> Req, JoinType.LeftOuterJoin)
.Select(ignore => Req.Id) // <-- Select wants a parameter
.List<int>();

You have to turn it around. I believe Faber's answer will also work well. So you do not have to turn it around. (But you could.)
ClientContact cAlias = null;
var x = Session.QueryOver<RequestType>()
.JoinAlias(rt => rt.ClientContacts, () => cAlias)
.Where(() => cAlias.Id == ContactId)
.Select(rt => rt.Id) // not sure what you want returned here, Id or the object)
.List();

Related

.Net Core EntitiyFramework Top Row Of Joined Table

I'm trying to get all students last attended class from database. I wrote tsql code and it works very well howevery i couldn't do the same on entityframework core.
This code below works for me.
select a.student, b.class, b.attend from students a inner join class b on b.id = (
select top 1 id
from class
where student = a.student order by attend desc
)
Also this is my attemp on writing EF query.
datavalue = db.students.join(
db.class, m => m.student,
s => s.student, (m, s) => new joinedstudentswclass { class = s.class, student = m.student }
).tolist();
u should use firstOrDefault to get a single record.
if u wanna order something, you should be used orderBy methods.
.OrderBy(x => x.City) for ascending students
.OrderByDescending(x => x.City) for descending students
the code should be something like
OrderByDescending(x=>x.Students).FirstOrDefault();
Without navigation properties:
var query =
from s in db.students
from c in db.Where(c => c.student == s.student).OrderByDescending(c => c.attend).Take(1)
select new joinedstudentswclass
{
class = c,
student = s
};
var datavalue = query.ToList()
If you have navigation property Classes in student class:
var query =
from s in db.students
from c in s.Classes.OrderByDescending(c => c.attend).Take(1)
select new joinedstudentswclass
{
class = c,
student = s
};
var datavalue = query.ToList()

How use a select in simple QueryOver

I have a query by QueryOver In Nhibernate 3.1
var q = SessionInstance.QueryOver<Person>()
.Where(p => p.Code == code);
.Select(p => p.Name,p => p.Code);
return q.SingleOrDefault();
This query without select is correct, but with select has a runtime error by this message: Unable to cast object of type 'System.String' to type 'MyNameSpace.Domain.Entities.Person'. How can i select some field of Person?
you need to tell NHibernate explicitly what is the type of your return data since you choose to select some specific properties of your original entity p.Name,P.Code these properties must by returned as of a new type of entity so the new type would be a new object
var query = SessionInstance.QueryOver<Person>()
.Where(p => p.Code == code)
.Select(
p => p.Name,
p => p.Code
)
.List<object>();
this will solve your problem
or if you defined a new entity to hold the new return data as following :
public newPeople
{
public Id { get; set; }
public Nam { get; set; }
}
you can write it like that:
var query = SessionInstance.QueryOver<Person>()
.Where(p => p.Code == code)
.Select(
p => p.Name,
p => p.Code
)
.Select( list =>
new newPeople()
{
Id = (int) list[0],
Name = (string) list[1]
})
.ToList<newPeople>();

Entity framework - how to filter eager loaded navigational/relational properties?

I have the following Entity Framework 4.1 entities and relationships
Concert ConcertId, AdministratorUserId, Name, IsDeleted
Booking
BookingId, ConcertId, UserId, IsDeleted
UserId, UserId, Name, IsDeleted
Relationships
Concert 1.....M Booking 1....1 User
Now I am trying to select all the Concerts for a particular AdminstratorUserId but also include all the Bookings for each Concert and also the User details for each booking. I would also like to apply a filter where IsDeleted == false for each Concert, Booking and User. I Would like to return a List of Concerts which have their Booking and User details maintained as navigational properties.
In SQL, this is what I am trying to achieve:
SELECT *
FROM concert c, booking b, user u
WHERE c.ConcertId = b.ConcertId AND b.UserId = u.UserId AND c.AdministratorId = 10
AND c.IsDeleted = false AND b.IsDeleted = false AND u.IsDeleted = false
As far as I am aware, using the "Include" method to eager load, doesn't allow filtering or subqueries of the child entity it loads in; it returns all records for that join, so I tried to use an anonymous projection as follows:
int adminId = 10;
var concerts = _context.Concerts
.Where(p => p.AdministratorId == adminId && p.IsDeleted == false)
.Select(p => new {
Concerts = p,
Bookings = p.Bookings
.Where(q => q.IsDeleted == false && q.User.IsDeleted == false)
.Select(r => new {
Bookings = r,
User = r.User
})
.AsEnumerable()
.Select(q => q.Bookings)
})
.AsEnumerable()
.Select(p => p.Concerts)
.ToList();
However, this is still returning all records and not filtering out the ones where IsDeleted = true. Anyone have any ideas, or suggestions how I can clean up this monstrous query?
I have also tried a method similar to this (http://blogs.msdn.com/b/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx), which again fails (returns all bookings even deleted ones):
var concertsQuery = (ObjectQuery<Concert>)_context.Concerts
.Where(p => p.UserId == userId
&& p.IsDeleted == false
&& p.Bookings.Any(q => q.IsDeleted == false && q.User.IsDeleted == false)
);
var concerts = concertsQuery.Include("Bookings").Include("Bookings.User").ToList();
Get rid of those AsEnumerable they will turn your query to linq-to-objects:
var concerts = _context.Concerts
.Where(p => p.AdministratorId == adminId && p.IsDeleted == false)
.Select(p => new {
Concerts = p,
Bookings = p.Bookings
.Where(q => q.IsDeleted == false && q.User.IsDeleted == false)
.Select(r => new {
Bookings = r,
User = r.User
})
})
.ToList();
So, the way I've managed to resolve this issue is by running the LINQ query as per Ladislav's answer, but then using the anonymous object to re-attach the individual objects to their correct relational properties as below:
var concertResult = new List<Concert>();
foreach (var concertObject in concerts)
{
var concert = concertObject.Concert;
foreach (var bookingObject in concertObject.Bookings)
{
var booking = bookingObject.Booking;
booking.User = bookingObject.User;
concert.Bookings.Add(booking);
}
concertResult.Add(concert);
}

Nhibernate join with select columns from both tables

Ok after several attempts I am stuck on this one!
I am using NHibernate with QueryOver as below. I have a Product and ProductReview as
public class Product
{
....
public virtual IList<ProductReview> CustomerReviews {get;set;}
....
}
public class ProductReview
{
....
public virtual Product Product {get;set;}
....
}
Mapping on Product side is
HasMany(x => x.CustomerReviews).KeyColumn("ProductId").Inverse().Cascade.None().LazyLoad();
The Query is
Product px = null;
ProductReview rev = null;
var result = CurrentSession
.QueryOver<ProductReview>()
.Where(r => r.IsActive && !r.IsDraft)
.Select(
Projections.Property<ProductReview>(r => r.Id).WithAlias(() => rev.Id),
Projections.Property<ProductReview>(r => r.Title).WithAlias(() => rev.Title)
)
.OrderBy(r => r.ReviewDate).Desc()
.TransformUsing(Transformers.AliasToBean<ProductReview>())
.JoinAlias(r => r.Product, () => px)
.Select(
Projections.Property(() => px.UPC).WithAlias(() => px.UPC),
Projections.Property(() => px.FullName).WithAlias(() => px.FullName)
)
.TransformUsing(Transformers.AliasToBean<Product>())
.Take(5)
.List();
The error is:
The value "Reviews.Models.Product" is not of type "Reviews.Models.ProductReview" and cannot be used in this generic collection. Parameter name: value
I really do not want to create another DTO. I would like to get the list of Last 5 new reviews and have it's Product populated (only a few required fields on both entities should be filled).
Is this possible by any means (except raw sql) in NHibernate 3.0?
Product px = null;
ProductReview rev = null;
var result = CurrentSession.QueryOver<ProductReview>()
.Where(r => r.IsActive && !r.IsDraft)
.JoinQueryOver(r => r.Product)
.OrderBy(r => r.ReviewDate).Desc()
.Take(5)
.List();

Left Outer Join in Linq to Entities / SQL

How can I write the following SQL in LINQ to Entities?
SELECT r.rolename,
( CASE
WHEN ur.username IS NULL THEN 0
ELSE 1
END ) AS isinrole
FROM bgt.roles r
LEFT OUTER JOIN bgt.usersinroles ur
ON ur.rolename = r.rolename
AND ur.username = 'ADMIN'
This worked for me. Thanks for all the suggestions.
var query =
from r in Roles
from ur in UsersInRoles
.Where(v => v.Rolename == r.Rolename && v.Username == "ADMIN")
.DefaultIfEmpty()
select new { Rolename = r.Rolename, IsInRole = (ur.Username != null) };
The generated SQL is as follows
SELECT
1 AS [C1],
[Extent1].[Rolename] AS [Rolename],
CASE WHEN ([Extent2].[Username] IS NOT NULL) THEN cast(1 as bit) WHEN ([Extent2].[Username] IS NULL) THEN cast(0 as bit) END AS [C2]
FROM [bgt].[Roles] AS [Extent1]
LEFT OUTER JOIN [bgt].[UsersInRoles] AS [Extent2] ON ([Extent2].[Rolename] = [Extent1].[Rolename]) AND ('ADMIN' = [Extent2].[Username])
I would do it like this:
from role in db.Roles
let isInRole = role.UsersInRoles.Any(u => u.UserName == "ADMIN")
select new { role.RoleName, isInRole }
Althought the generated SQL is not as nice as yours.
Extension method for Left join (linq to entities):
public static class DbSetExtensions
{
public static IQueryable<TResult2> LeftJoin<TOuter, TInner, TKey, TResult2>(
this IQueryable<TOuter> outerList,
IEnumerable<TInner> innerList,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<LeftJoinTemp<TOuter, TInner>, TInner, TResult2>> resultSelector2)
{
// (tr, historicPrices) => new { tr, historicPrices }
Expression<Func<TOuter, IEnumerable<TInner>, LeftJoinTemp<TOuter, TInner>>> myResultSelector1
= (tr, historicPrices) => new LeftJoinTemp<TOuter, TInner> { outer = tr, inners = historicPrices };
// e => e.historicPrices.DefaultIfEmpty()
Expression<Func<LeftJoinTemp<TOuter, TInner>, IEnumerable<TInner>>> myCollectionSelector
= e => e.inners.DefaultIfEmpty();
//var a = outerList.GroupJoin(innerList, outerKeySelector, innerKeySelector, resultSelector1);
var a = outerList.GroupJoin(innerList, outerKeySelector, innerKeySelector, myResultSelector1);
//return a.SelectMany(collectionSelector, resultSelector2);
var b = a.SelectMany(myCollectionSelector, resultSelector2);
return b;
}
}
public class LeftJoinTemp<TOuter, TInner>
{
public TOuter outer;
public IEnumerable<TInner> inners;
}
example calling, left join of Transaction and HistoricPrice, for transaction you have to write parent.outer
.LeftJoin(db.HistoricPrices,
transaction => new { transaction.InstrumentId, DateId = transaction.Date2Id },
historicPrice => new { historicPrice.InstrumentId, historicPrice.DateId },
(parent, historicPrice) => new
{
parent.outer.Id,
parent.outer.OpeningDate,
parent.outer.InstrumentName,
historicPrice.DateId,
historicPrice.InstrumentId
})