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)
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();
Is it possible to transform component into a dto when creating projections via SelectList.
I have a value object Name, and I need to project it into a Dto NameInfo. Below is the query.
I need a step here on this line
.Select(c=> coveredInsured.Name).WithAlias(()=>info.Name)
something along the lines
.Select(c => new NameInfo { Last= c.Last, First=c.First, Middle = c.Middle}).WithAlias(()=>info.Name)
This is the actual query that fails becuase it cannot convert Name into NameInfo. So. other then exposing three properties on my dto, is there a way to project?
.SelectList(list => list
.Select(c => c.Id).WithAlias(() => info.CoverageId)
.Select(c => bid.Year).WithAlias(() => info.PolicyYear)
.Select(c => bid.OptionNumber).WithAlias(() => info.OptionNumber)
.Select(c=> ((ClientBase)client).Number).WithAlias(()=>info.ClientNumber)
.Select(c => c.PrimaryInsured.Id).WithAlias(() => info.PrimaryInsuredId)
.Select(c => c.CoveredInsured.Id).WithAlias(() => info.CoveredInsuredId)
.Select(c=> coveredInsured.Name).WithAlias(()=>info.Name)
)
.TransformUsing(Transformers.AliasToBean<InsuredCoverageInfo>())
.List<InsuredCoverageInfo>()
.ToList();
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!
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>();
Having problems with the combining of two statements in a controller. Both statements work but cannot get them working as one element.
First statement is to show data for a user when his username matches the logged on usersname
This works fine.
public ViewResult Index()
{
var myrecords = db.Customers.Where(UserData => UserData.UserName.Equals
(User.Identity.Name)).ToList();
return View(myrecords);
}
Second statement is for connecting tables and showing the data. This works fine
var caradverts = db.CarAdverts.Include(c => c.BodyType).Include(c => c.Car).Include(c => c.Colour).Include(c => c.Customer).Include(c => c.EngineSize).Include(c => c.fuel).Include(c => c.SalesPlan).Include(c => c.Transmission).Include(c => c.year);
return View(caradverts.ToList());
Now the trouble is connecting both statements into one so it only shows the data for the user logged on.
Any advice welcome
How about this:
var caradverts = db.CarAdverts
.Include(c => c.BodyType)
.Include(c => c.Car)
.Include(c => c.Colour)
.Include(c => c.Customer)
.Include(c => c.EngineSize)
.Include(c => c.fuel)
.Include(c => c.SalesPlan)
.Include(c => c.Transmission)
.Include(c => c.year)
.Where(c => c.Customer.UserName == User.Identity.Name)
.ToList();
Do you really need all of these Include statements though? It looks like most of them refer to plain primitive properties and not related entities.
Also because most likely in the first case you want data from a single user I would rewrite your query like this:
var userRecord = db.Customers
.SingleOrDefault(u => u.UserName == User.Identity.Name);
Then update your model/view accordingly to deal with a single Customer record and not a collection.