I am trying to convert this inefficient query into one that projects into a dto.
Original query looks like this:
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(x => x.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(x => x.ClientPolicy, () => clientPolicyAlias)
.Where(y => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.List()
.Select(x => new FlatChargeAccessFeeInfo()
{
FlatChargeAccessFeeId = x.Id,
ClientName = x.ClientPolicy.Bid.Client.Name,
PolicyNumber = x.ClientPolicy.PolicyNumber,
ClientPolicyId = x.ClientPolicy.Id,
AgreementAccessFeeId = x.AgreementAccessFee.Id,
ShouldCheckBeGenerated = x.ShouldCheckBeGenerated,
MonthlyFee = x.MontlyFeeAmount.Amount.ToString(),
PolicyYear = x.ClientPolicy.PolicyNumber.Year
})
.ToList();
I tried it like this:
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(x => x.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(x => x.ClientPolicy, () => clientPolicyAlias)
.Where(y => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => feeInfo.FlatChargeAccessFeeId)
.Select(x => x.ClientPolicy.Bid.Client.Name).WithAlias(() => feeInfo.ClientName)
.Select(x => x.ClientPolicy.PolicyNumber).WithAlias(() => feeInfo.PolicyNumber)
.Select(x => x.ClientPolicy.Id).WithAlias(() => feeInfo.ClientPolicyId)
.Select(x => x.AgreementAccessFee.Id).WithAlias(() => feeInfo.AgreementAccessFeeId)
.Select(x => x.ShouldCheckBeGenerated).WithAlias(() => feeInfo.ShouldCheckBeGenerated)
.Select(x => x.MontlyFeeAmount.Amount.ToString()).WithAlias(() => feeInfo.MonthlyFee)
.Select(x => x.ClientPolicy.PolicyNumber.Year).WithAlias(() => feeInfo.PolicyYear)
)
.TransformUsing(Transformers.AliasToBean<FlatChargeAccessFeeInfo>())
.List<FlatChargeAccessFeeInfo>();
and I am getting an error that variable "x" has been referenced in scope but was not defined. What is the proper syntax to convert this?
After help from Andrew, here is the correct version that works
ClientPolicy clientPolicyAlias = null;
Client clientAlias = null;
Bid bidAlias = null;
AgreementAccessFee agreementAccessFeeAlias = null;
FlatChargeAccessFee flatChargeAccessFeeAlias = null;
FlatChargeAccessFeeInfo feeInfo = null;
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(a => a.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(b => b.ClientPolicy, () => clientPolicyAlias)
.JoinAlias(b=>b.Bid,()=>bidAlias)
.JoinAlias(b=>b.Client, ()=>clientAlias)
.Where(c => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.SelectList(list => list
.Select(d => d.Id).WithAlias(() => feeInfo.FlatChargeAccessFeeId)
.Select(e => clientAlias.Name).WithAlias(() => feeInfo.ClientName)
.Select(e => clientAlias.Number).WithAlias(() => feeInfo.ClientNumber)
.Select(f => bidAlias.OptionNumber).WithAlias(() => feeInfo.BidOptionNumber)
.Select(f => bidAlias.Year).WithAlias(()=>feeInfo.PolicyYear)
.Select(g => clientPolicyAlias.Id).WithAlias(() => feeInfo.ClientPolicyId)
.Select(h => agreementAccessFeeAlias.Id).WithAlias(() => feeInfo.AgreementAccessFeeId)
.Select(j => j.ShouldCheckBeGenerated).WithAlias(() => feeInfo.ShouldCheckBeGenerated)
.Select(k => k.MontlyFeeAmount.Amount).WithAlias(()=>feeInfo.MonthlyFee)
)
.TransformUsing(Transformers.AliasToBean<FlatChargeAccessFeeInfo>())
.List<FlatChargeAccessFeeInfo>();
You're close, a few things though:
This select:
.Select(x => x.MontlyFeeAmount.Amount.ToString()).WithAlias(() => feeInfo.MonthlyFee)
will not work. QueryOver attempts to turn your code directly into SQL. If the property does not exist as a column in the database, the query won't work properly (unless you're using a mapped custom type, QueryOver can handle those)
Nested property access won't work either:
.Select(x => x.ClientPolicy.Bid.Client.Name).WithAlias(() => feeInfo.ClientName)
for a similar reason listed above. QueryOver will attempt to turn your property access directly into SQL. You'll need to explicitly join from ClientPolicy to Bid to Client.
In general, remember that you're writing code that's going to be turned into SQL. In fact, I normally write the SQL I want to generate first and then write the QueryOver that corresponds to that. Hope that helps!
Related
For example:
Tree treeAlias = null;
var nonRottenApples = QueryOver.Of<Apple>()
.Where(a => !a.IsRotten)
.Where(a => a.Tree.Id == treeAlias.Id)
.Select(x => x.Id); // <- optional
return NHibernateSession.QueryOver(() => treeAlias)
.Where(t => t.Id.IsIn(ListOfTreeId))
.WithSubquery.WhereExists(nonRottenApples)
.List();
How do SubQuery not collection in Apple ?
I'm researching at various locations, but i not find results for my question.
All results are collections.
My interpretation of your question is that you're trying to query for the trees that don't have rotten apples.
I believe this will work:
var rottenTreeIds = QueryOver.Of<Apple>()
.Where(apple => apple.IsRotten)
.Select(a => a.Tree.Id);
return NHIbernateSession.QueryOver<Tree>()
.Where(t => t.Id.IsIn(ListOfTreeId))
.WithSubquery.WhereProperty(t => t.Id).NotIn(rottenTreeIds)
.List();
// load the client and any many to one relationships
var clientRootQuery = session.QueryOver(() => clientAlias);
clientRootQuery.Left.JoinAlias(() => clientAlias.Person, () => personAlias)
.Left.JoinAlias(() => personAlias.SexType, () => sexTypeAlias)
.Left.JoinAlias(() => personAlias.EyeColorType, () => eyeColorTypeAlias)
.Left.JoinAlias(() => clientAlias.Organization, () => organizationAlias)
.Left.JoinAlias(() => clientAlias.ClientStatusType, () => clientStatusTypeAlias)
.Left.JoinAlias(() => clientAlias.Notes, () => noteAlias)
.Left.JoinAlias(() => noteAlias.Comments, () => commentAlias)
.Where(() => clientAlias.Id == clientId)
.Future<Client>();
// load the note collection into the nhibernate session
// todo ******************** this doesn't work. nhibernate is still firing off queries in the adapter fill method rather than using the values pulled here.
var notes = session.QueryOver(() => clientAlias)
.Left.JoinAlias(() => clientAlias.Notes, () => noteAlias)
.Left.JoinAlias(() => noteAlias.Comments, () => commentAlias)
.Where(() => clientAlias.Id == clientId)
.Future<Client>();
return clientRootQuery.Take(1).SingleOrDefault();
This does not return many addresses on the client object nor does it return many notes. This should work. There is a client with many notes and many addresses.
Any ideas?
the reason why clientAddresses are not initialized is because you set a filter on them so NHibernate assumes that not all clientAddresses are loaded to fully initialize the collection.
var clientRootQuery = session.QueryOver(() => clientAlias)
.Left.JoinAlias(() => clientAlias.Person, () => personAlias)
.Left.JoinAlias(() => personAlias.SexType, () => sexTypeAlias)
.Left.JoinAlias(() => personAlias.EyeColorType, () => eyeColorTypeAlias)
.Left.JoinAlias(() => clientAlias.Organization, () => organizationAlias)
.Left.JoinAlias(() => clientAlias.ClientStatusType, () => clientStatusTypeAlias)
.Where(() => clientAlias.Id == clientId)
.Future();
var notes = session.QueryOver(() => clientAlias)
.Left.JoinAlias(() => clientAlias.Notes, () => noteAlias)
.Where(() => noteAlias.Client.Id == clientId)
.Future();
var list = clientRootQuery.ToList();
return list.FirstOrDefault();
It needs to be a left join.
I am trying to project a single value from an entity's association with no luck. Only want the city name from the TrainerAddress in the query below. Trainer address is mapped as a component of Trainer.
session.QueryOver<Trainer>()
.JoinAlias(x=>x.TrainerAddress.City, ()=> cityAlias, JoinType.LeftOuterJoin)
.OrderBy(x => x.Name).Asc
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => dto.Id)
.Select(x => x.Name).WithAlias(() => dto.Name)
.Select(x => x.TrainerAddress.City.Name).WithAlias(() => dto.City))//issue projecting here
.TransformUsing(Transformers.AliasToBean<TrainerDTO>())
.List<TrainerDTO>();
Is this possible?
You're specifying an alias for TrainerAddress.City but you don't use that alias anywhere?
I'm not 100% sure if NHibernate supports components well in QueryOver, I know there were some issues with it using Criteria, but maybe this will work:
session.QueryOver<Trainer>()
.JoinAlias(x=> x.TrainerAddress.City, ()=> cityAlias, JoinType.LeftOuterJoin)
.OrderBy(x => x.Name).Asc
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => dto.Id)
.Select(x => x.Name).WithAlias(() => dto.Name)
.Select(x => cityAlias.Name).WithAlias(() => dto.City))
.TransformUsing(Transformers.AliasToBean<TrainerDTO>())
.List<TrainerDTO>();
im trying to query using QueryOver and im trying to do some transformation with fetching.
But im hitting error
HSPTransactionDto hspTransactionDto = null;
var hspTransactionDtoList =
Session.QueryOver<Transaction>()
.Where(x => x.TransactionStatus == TransactionStatus.Draft)
.Fetch(x => x.HealthServiceProvider).Eager
.Fetch(x => x.HealthMaintenanceOrganization).Eager
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => hspTransactionDto.Id)
.Select(x => x.Version).WithAlias(() => hspTransactionDto.Version)
.Select(x => x.HealthServiceProvider.Id).WithAlias(() => hspTransactionDto.HSPId)
.Select(x => x.HealthServiceProvider.Name).WithAlias(() => hspTransactionDto.HSPName)
.Select(x => x.HealthMaintenanceOrganization.Id).WithAlias(() => hspTransactionDto.HMOId)
.Select(x => x.HealthMaintenanceOrganization.Name).WithAlias(() => hspTransactionDto.HMOName)
.Select(x => x.HSPPatientCode).WithAlias(() => hspTransactionDto.HSPPatientCode)
.Select(x => x.PatientFirstName).WithAlias(() => hspTransactionDto.PatientFirstName)
.Select(x => x.PatientMiddleName).WithAlias(() => hspTransactionDto.PatientMiddleName)
.Select(x => x.PatientLastName).WithAlias(() => hspTransactionDto.PatientLastName)
)
.TransformUsing(Transformers.AliasToBean<HSPTransactionDto>()).List<HSPTransactionDto>();
return hspTransactionDtoList;
anyone can help?
actually found the answer just need to put some alias so i ended up with
HSPTransactionDto hspTransactionDto = null;
HealthMaintenanceOrganization hmoAlias = null;
HealthServiceProvider hspAlias = null;
var hspTransactionDtoList =
Session.QueryOver<Transaction>()
.Where(a => a.TransactionStatus == TransactionStatus.Draft)
.Fetch(b => b.HealthServiceProvider).Eager
.Fetch(c => c.HealthMaintenanceOrganization).Eager
.JoinAlias(x=> x.HealthMaintenanceOrganization,() => hmoAlias)
.JoinAlias(x => x.HealthServiceProvider, () => hspAlias)
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => hspTransactionDto.Id)
.Select(x => x.Version).WithAlias(() => hspTransactionDto.Version)
.Select(x => hspAlias.Id).WithAlias(() => hspTransactionDto.HSPId)
.Select(x => hspAlias.Name).WithAlias(() => hspTransactionDto.HSPName)
.Select(x => hmoAlias.Id).WithAlias(() => hspTransactionDto.HMOId)
.Select(x => hmoAlias.Name).WithAlias(() => hspTransactionDto.HMOName)
.Select(x => x.HSPPatientCode).WithAlias(() => hspTransactionDto.HSPPatientCode)
.Select(x => x.PatientFirstName).WithAlias(() => hspTransactionDto.PatientFirstName)
.Select(x => x.PatientMiddleName).WithAlias(() => hspTransactionDto.PatientMiddleName)
.Select(x => x.PatientLastName).WithAlias(() => hspTransactionDto.PatientLastName)
)
.TransformUsing(Transformers.AliasToBean<HSPTransactionDto>()).List<HSPTransactionDto>();
return hspTransactionDtoList;
anyone know a cleaner way?
I think you don't need to use Fetch, because you've used Select for selective properties and it's grouped with DTO:
HSPTransactionDto hspTransactionDto = null;
HealthMaintenanceOrganization hmoAlias = null;
HealthServiceProvider hspAlias = null;
var hspTransactionDtoList =
Session.QueryOver<Transaction>()
.Where(a => a.TransactionStatus == TransactionStatus.Draft)
.JoinAlias(x=> x.HealthMaintenanceOrganization,() => hmoAlias)
.JoinAlias(x => x.HealthServiceProvider, () => hspAlias)
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => hspTransactionDto.Id)
.Select(x => x.Version).WithAlias(() => hspTransactionDto.Version)
.Select(x => hspAlias.Id).WithAlias(() => hspTransactionDto.HSPId)
.Select(x => hspAlias.Name).WithAlias(() => hspTransactionDto.HSPName)
.Select(x => hmoAlias.Id).WithAlias(() => hspTransactionDto.HMOId)
.Select(x => hmoAlias.Name).WithAlias(() => hspTransactionDto.HMOName)
.Select(x => x.HSPPatientCode).WithAlias(() => hspTransactionDto.HSPPatientCode)
.Select(x => x.PatientFirstName).WithAlias(() => hspTransactionDto.PatientFirstName)
.Select(x => x.PatientMiddleName).WithAlias(() => hspTransactionDto.PatientMiddleName)
.Select(x => x.PatientLastName).WithAlias(() => hspTransactionDto.PatientLastName)
)
.TransformUsing(Transformers.AliasToBean<HSPTransactionDto>()).List<HSPTransactionDto>();
return hspTransactionDtoList;
I have a very basic need to get some data from the database and return a DTO. I found that joining multiple tables using nHibernate and "projecting" so to say, to a DTO to be quite a bit of code. After looking at several examples, most which didn't work leaving me a DTO with null values, I cam up with the following and was wondering if you nHibernate ninja's out there could tell me if there is a better way.
public IOpenIdUser GetOpenIdUser(string claimedIdentifier, IOpenIdUser openIdUserDto)
{
User user = null;
OpenIdUser openIdUser = null;
Profile profile = null;
UserType userType = null;
return
SessionWrapper.Session.QueryOver(() => user).JoinAlias(() => user.Profiles, () => profile).
JoinAlias(() => user.OpenIdUsers, () => openIdUser).JoinAlias(() => user.UserType, () => userType)
.Where(() => user.UserName == claimedIdentifier)
.SelectList(l => l
.Select(x => openIdUser.OpenIdUserId).WithAlias(() => openIdUser.OpenIdUserId)
.Select(x => user.UserId).WithAlias(() => openIdUserDto.UserId)
.Select(x => openIdUser.OpenIdClaimedIdentifier).WithAlias(
() => openIdUserDto.ClaimedIdentifier)
.Select(x => openIdUser.OpenIdFriendlyIdentifier).WithAlias(
() => openIdUserDto.FriendlyIdentifier)
.Select(x => openIdUser.OpenIdEndPoint).WithAlias(
() => openIdUserDto.OpenIdEndPoint)
.Select(x => user.UserName).WithAlias(() => openIdUserDto.UserName)
.Select(x => userType.Type).WithAlias(() => openIdUserDto.UserType)
.Select(x => profile.DisplayName).WithAlias(() => openIdUserDto.DisplayName)
.Select(x => profile.EmailAddress).WithAlias(() => openIdUserDto.EmailAddress)
.Select(x => openIdUser.DateCreated).WithAlias(() => openIdUserDto.DateCreated)
.Select(x => openIdUser.LastUpdated).WithAlias(() => openIdUserDto.LastUpdated)
.Select(x => openIdUser.UsageCount).WithAlias(() => openIdUserDto.UsageCount)
).TransformUsing(Transformers.AliasToBean<OpenIdUserDto>()).Future<OpenIdUserDto>().Single();
}
This method sits in my UserRepository and is called by my UserService. Please not that this actually works, I just think it is overkill for such a simple task. Also please note that I am new to this so if this code is crappy I apologize in advance.
If you use Queryover then this is the only way.
If you really think less lines of code are preferable, more intuitive, or sits better with you then you can do either:-
Create a DB view and create mapings files for the view with
mutable="false" in your class definition and use protected set; on your class properties
Use the LINQ provider instead e.g. .Query (see
this blog post for more info)