nhibernate joinalias doesnt build right query - c#

I get SqlException (The multi-part identifier "packageali1_.PackageID" could not be bound.) whenever I try to execute this query (QueryOver):
var brandsFromBrandsInManufacturer2 =
session.QueryOver<Brand>(() => brandAlias)
.JoinAlias(brand => brand.Package, () => packageAlias)
.Where(
brand =>
brand.ArtificialBrand == 0
)
.And(brand => packageAlias.PackageID
.IsIn(branchPackagesProductGroupShortName.Keys))
.Select(brand => brand.BrandName,
brand => packageAlias.PackageID)
List<object[]>();
It looks like I have error in my mappings:
public class PackageMap : ClassMap<Package>
{
public PackageMap()
{
Table("Packages");
Id(x => x.PackageID).GeneratedBy.Identity();
Map(x => x.Aggregated).CustomType<PackageAggregation>();
References(x => x.DataEndPeriod, "DataEndPeriodID");
References(x => x.Country, "CountryID");
References(x => x.ProductGroup, "ProductGroupID");
HasMany(x => x.PackageHierarchies).KeyColumns.Add("PackageId");
HasMany(x => x.Brands).KeyColumns.Add("PackageId").Inverse();
}
}
public BrandMap()
{
Table("Brands");
CompositeId().KeyProperty(x =>x.BrandId).KeyReference(x => x.Package, "PackageId");
Map(x => x.BrandName);
References(x => x.Manufacturer).Columns(x => x.ManufacturerId).Nullable();
Map(x => x.ArtificialBrand);
}
but I can't find what is wrong. Only thing what I've found is fact, that Brand has classic ID key, but Package has composite key.
PS: SQL query from nHibernate is:
SELECT this_.BrandName as y0_, packageali1_.PackageID as y1_ FROM CD.Brands this_ WHERE this_.ArtificialBrand = #p0 and packageali1_.PackageID in (...)
so as you can see, there is no JOIN.
Could someone point me what is wrong?

The solution here would be to "work" directly with the Key property Package, instead of its alias. So, instead of packageAlias.PackageID, let's navigate there directly through the brand.Package.PackageID.
Also, if we've created so many aliases, let's use them
var brandsFromBrandsInManufacturer2 = session
.QueryOver<Brand>(() => brandAlias)
.JoinAlias(() => brandAlias.Package, () => packageAlias) // use brandAlias
.Where(() => brandAlias.ArtificialBrand == 0 )
// no go to Package directly, not via packageAlias
.And(() => brandAlias.Package.PackageID
.IsIn(branchPackagesProductGroupShortName.Keys))
// the same here, the brand.Package...
.Select(brand => brand.BrandName,
brand => brand.Package.PackageID)
List<object[]>();
Well, the second line, is in fact redundant. so it could be like this:
var list = session
.QueryOver<Brand>(() => brandAlias)
.Where(() => brandAlias.ArtificialBrand == 0 )
.And(() => brandAlias.Package.PackageID
.IsIn(branchPackagesProductGroupShortName.Keys))
.Select(brand => brand.BrandName,
brand => brand.Package.PackageID)
List<object[]>();

Related

automapper optimization mapping (model to view model -> get latest version)

I am trying to optimize this part of code:
Mapper.CreateMap<Document, DocumentViewModel>()
.ForMember(g => g.Id, map => map.MapFrom(d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().Id))
.ForMember(g => g.IdRootDocument, map => map.MapFrom(d => d.Id))
.ForMember(g => g.certyficateType, map => map.MapFrom(d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().certyficateType))
I'm Using automapper, and I'm trying to optimize this part of code
In this part I'm trying mapping object from document to documentViewModel, in this complex model, source data always will be latest document version:
d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().myProportyX
Could anyone offer an example or suggestion as to how to approach optimization in this situation?
d => d.documentVersion.Where(v => v.version == d.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First().myProporty
You are iterating quite a few times here, you may consider doing something like:
d.documentVersion.OrderByDescending(v => v.version).ThenByDescending(v => v.subVersion).First().myProperty
to reduce the number of iterations and only get the top version/subversion.
optimization mapping:
Mapper
.CreateMap<Document, DocumentViewModel>()
.ConvertUsing(doc =>
{
DocumentViewModel result = new DocumentViewModel();
DocumentVersion lastVersion = doc.documentVersion.Where(v => v.version == doc.documentVersion.Select(s => s.version).Max()).OrderByDescending(s => s.subVersion).First();
Mapper.Map(lastVersion, result);
return result;
});

Nhibernate QueryOver Project enum description to string

Let`s say we have the following query using Nhibernate:
var query = session.QueryOver<TenantOrder>(() => tenantOrderAlias)
.JoinAlias(() => tenantOrderAlias.Tenant, () => tenantAlias)
.JoinAlias(() => tenantAlias.Building, () => buildingAlias)
.WhereRestrictionOn(x => tenantOrderAlias.Id.OrderId).IsLike(order.Id);
query.Select(
Projections.Property(() => tenantAlias.Id).WithAlias(() => tenantDto.Id),
Projections.Property(() => tenantAlias.TenantNr).WithAlias(() => tenantDto.TenantNr),
Projections.Property(() => buildingAlias.BuildingNr).WithAlias(() => tenantDto.BuildingNr),
Projections.Property(() => tenantAlias.Floor).WithAlias(() => tenantDto.Floor),
Projections.Property(() => tenantOrderAlias.InstallationStatus).WithAlias(() => tenantDto.InstallationStatusName //? ));
We want to process InstallationStatus (type enum) from tenantOrderalias to InstallationStatusName (property of type string from tenantDto). The structure of the enum looks like below :
public enum TenantInstallationStatusEnum
{
[StringEnum("MS3_TenantInstallationStatus_TS0")]
TS0,//open
[StringEnum("MS3_TenantInstallationStatus_TS2")]
TS2,//abgelesen
}
We want to get the description of the enum and place it to tenantDto.InstallationStatusName. Is this possible using QueryOver or ICriteria ?
We cannot find any post about this issue.
In general, we can project only stuff which exists in a DB side or is passed as a "formula". So, we can convert the status into some conditional statement like this:
// this projection
Projections
.Property(() => tenantOrderAlias.InstallationStatus)
.WithAlias(() => tenantDto.InstallationStatusName //? ));
// could be converted into string values with this statement
Projections
.Conditional(
Restrictions.Where<TenantOrder>(to =>
to.InstallationStatus == TenantInstallationStatusEnum.TS0),
Projections.Constant("MS3_TenantInstallationStatus_TS0"),
Projections.Constant("MS3_TenantInstallationStatus_TS2")
).WithAlias(() => tenantOrderAlias.InstallationStatusName)
);
Conditional could be even nested... but at the end it could be a bit more challenging.
But, maybe more easier in fact, would be to process this conversion ex-post, in C#, on the app side...

How to partially project a child object with many fields in nHibernate

I have the following nHibernate query that select a course based on its course id and then return selected fields for the course object on the initial fetch, and the query executes with no issues.
MatchMode option = ...
CourseItem courseAlias = null;
TeacherItem teacherAlias = null;
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
.Select(c => c.Teacher).WithAlias(() => courseAlias.Teacher))
.TransformUsing(Transformers.AliasToBean<CourseItem>())
.List<CourseItem>();
I wanted to go a step further with the query to only return a partial teacher object, let's say i just wanted to return the ID and Name. So, I updated the projected list to as follows:
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
.Select(c => c.Teacher.ID).WithAlias(() => courseAlias.Teacher.ID)
.Select(c => c.Teacher.Name).WithAlias(() => courseAlias.Teacher.Name))
.TransformUsing(Transformers.AliasToBean<CourseItem>())
.List<CourseItem>();
The query doesn't work because nHibernate has no idea how to resovled based on Teacher.ID and Teacher.Name. Any thoughts on whether it's possible to NOT fetch the entire child object back to a parent object?
I've tried the following query and it works this is not my fully desired outcome
var query = session.QueryOver<CourseItem>(() => courseAlias)
.JoinAlias(() => courseAlias.Teacher, () => teacherAlias)
.Where(() => courseAlias.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(() => courseAlias.CourseID)
.Select(() => courseAlias.IsActive)
.Select(() => courseAlias.CourseDesc)
.Select(() => teacher.ID)
.Select(() => teacher.Name))
.List<object[]>();
I can query the right values but unable to transform it back correctly to the Course / teacher data type.
Any thoughts?
thanks!
We can indeed use custom transformer. There is one, which I am using for a really very very deep projections (inlcuding dynamic objects - 5.1.13. component, dynamic-component)
DeepTransformer<TEntity>
Take it (if needed adjust it) and your final query could be like this
// just the last lines are different
var query = session.QueryOver<CourseItem>()
.JoinAlias(c => c.Teacher, () => teacherAlias)
.Where(c => c.CourseID.IsInsensitiveLike(strNumber, option))
.SelectList(list => list
.Select(c => c.CourseID).WithAlias(() => courseAlias.CourseID)
.Select(c => c.IsActive).WithAlias(() => courseAlias.IsActive)
.Select(c => c.CourseDesc).WithAlias(() => courseAlias.CourseDesc)
// the native WitAlias would not work, it uses expression
// to extract just the last property
//.Select(c => c.Teacher.ID).WithAlias(() => courseAlias.Teacher.ID)
//.Select(c => c.Teacher.Name).WithAlias(() => courseAlias.Teacher.Name))
// so we can use this way to pass the deep alias
.Select(Projections.Property(() => teacherAlias.ID).As("Teacher.ID"))
.Select(Projections.Property(() => teacherAlias.Name).As("Teacher.Name"))
// instead of this
// .TransformUsing(Transformers.AliasToBean<CourseItem>())
// use this
.TransformUsing(new DeepTransformer<CourseItem>())
And in case, that your aliases do match to property names, that transformer will built the object tree...

Automapper: How do I map Lazy<TFrom> to TTo?

I need to map a list of Lazy<TFrom> to a list of TTo. What I've done isn't working - I only get null values. Without the Lazy<> wrapper it works perfectly. What am I doing wrong?
// Map this list
// It's passed in as an argument and has all the data
IList<Lazy<Employee>> employees
public static IList<TTo> MapList(IList<Lazy<TFrom>> fromModel)
{
Mapper.CreateMap<Lazy<TFrom>, TTo>();// Is this needed?
return Mapper.Map<IList<Lazy<TFrom>>, IList<TTo>>(fromModel);
// This doesn't work
// return Mapper.Map<IList<TTo>>(fromModel.Select(x => x.Value));
}
// Maps the child classes
Mapper.CreateMap<Lazy<Employee>, EmployeeDTO>()
.ForMember(x => x.AccidentDTO, i => i.MapFrom(model => model.Value.GetAccident()))
.ForMember(x => x.CriticalIllnessDTO, i => i.MapFrom(model => model.Value.GetCriticalIllness()))
.ForMember(x => x.ValidationMessages, i => i.MapFrom(model => model.Value.ValidationMessages));
// Returns nulls !!!
var dataDTO = MyMapper<Lazy<Employee>, EmployeeDTO>.MapList(employees);
Here's what I've done to solve my problem. If someone has a better idea, please let me know and I'll mark it as the answer.
// Get the Values of the Lazy<Employee> items and use the result for mapping
var nonLazyEmployees = employees.Select(i => i.Value).ToList();
var dataDTO = MyMapper<Employee, EmployeeDTO>.MapList(nonLazyEmployees);
public static IList<TTo> MapList(IList<Lazy<TFrom>> fromModel)
{
Mapper.CreateMap<Lazy<TFrom>, TTo>();
return Mapper.Map<IList<Lazy<TFrom>>, IList<TTo>>(fromModel);
}
// Maps the child classes
Mapper.CreateMap<Employee, EmployeeDTO>()
.ForMember(x => x.AccidentDTO, i => i.MapFrom(model => model.GetAccident()))
.ForMember(x => x.CriticalIllnessDTO, i => i.MapFrom(model => model.GetCriticalIllness()))
.ForMember(x => x.ValidationMessages, i => i.MapFrom(model => model.ValidationMessages));

nHibernate: could not resolve property

i have spent hours trying to figure this out including going through all the previously asked questions on StackOverflow.
i am trying to query from the TrainingCourse by EvaluationHeadId, thats works fine, however, i try to get the TrainingRoute it returns "could not resolve property: TrainingRoute.TrainingRouteDefinition of: Model.Entities.TrainingCourse"
it saves perfect, my real problem is the query.
updated to:
using (var session = SessionProvider.Instance.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
dto = session.QueryOver<TrainingCourse>()
.JoinQueryOver<EvaluationHead>(p => p.EvaluationHeads)
.JoinQueryOver<TrainingRoute>(p => p.TrainingRoute)
.Where(c => c.EvaluationHeadID == headId)
.SelectList(l => l
.Select(h => h.TrainingCourseDefn).WithAlias(() => d.TrainingCourseDefn)
.Select(h => h.IsAvailable).WithAlias(() => d.IsAvailable)
.Select(h => h.TrainingRoute.TrainingRouteDefinition).WithAlias(() => d.TrainingRouteDefinition))
.TransformUsing(Transformers.AliasToBean<TrainingCourseDTO>())
.List<TrainingCourseDTO>();
transaction.Commit();
}
}
Mappings:
public TrainingCourseMap()
{
Id(x => x.TrainingCourseID).GeneratedBy.Identity();
Map(x => x.TrainingCourseDefn);
Map(x => x.IsAvailable);
Map(x => x.TrainingCourseCreatedBy);
Map(x => x.TrainingCourseDtCreation);
Map(x => x.TrainingCourseDtModified);
Map(x => x.TrainingCourseModifiedBy);
References(x => x.TrainingRoute).Column("TrainingRouteID").Cascade.None();
HasManyToMany(x => x.EvaluationHeads).Table("EvaluationTraining").ParentKeyColumn("TrainingCourseID").ChildKeyColumn("EvaluationHeadID").Inverse().Cascade.All();
}
public EvaluationHeadMap()
{
Id(x => x.EvaluationHeadID).GeneratedBy.Identity();
Map(x => x.ManagerID);
Map(x => x.SupervisorID);
Map(x => x.EvaluationStartPeriod);
Map(x => x.EvaluationEndPeriod);
Map(x => x.EmployeeScalePoint);
Map(x => x.KRASignature);
Map(x => x.KRASignatureDate);
Map(x => x.DateCreated);
Map(x => x.DateModified);
HasMany(x => x.KeyResultAreas).KeyColumn("EvaluationHeadID").Cascade.All().Inverse();
HasMany(x => x.Evaluations).KeyColumn("EvaluationHeadID").Inverse().Cascade.All();
HasManyToMany(x => x.TrainingCourses).Table("EvaluationTraining").ParentKeyColumn("EvaluationHeadID").ChildKeyColumn("TrainingCourseID").Cascade.All().AsBag();
References(x => x.Stage).Column("StageID").Cascade.None();
References(x => x.Employee).Column("EmployeeID").Cascade.None();
References(x => x.Employment).Column("EmploymentID").Cascade.None();
//References(x => x.Manager).Column("EmployeeID");
//References(x => x.Supervisor).Column("EmployeeID");
}
public TrainingRouteMap()
{
Id(x => x.TrainingRouteID).GeneratedBy.Identity();
Map(x => x.TrainingRouteDefinition);
Map(x => x.TrainingRouteDescription);
HasMany(x => x.TrainingCourses).KeyColumn("TrainingRouteID").Cascade.AllDeleteOrphan().Inverse();
}
note: i have another query between TrainingCourse and TrainingRoute and it gives no issue at all, even accessing properties through TrainingCourse.TrainingRoute.x pattern. The only difference with this one is that am querying other tables as well.
The TrainingRoute is a reference property of the TrainingCourse (as well as EvaluationHeads). So you have to use JoinQueryOver or JoinAlias for it as well. Below we will create dummy objects to be used for aliasing (all set to null). We also split the joining of the queries, because they result in a references to newly create queries
TrainingCourse trainingCourse = null;
TrainingRoute trainingRoute = null;
EvaluationHead evaluationHead = null;
var query = session.QueryOver<TrainingCourse>(() => trainingCourse);
// here we can work with criteria against the TrainingRoute
var referenceToTraingRouteQuery = query
.JoinQueryOver<TrainingRoute>(p => p.TrainingRoute, () => trainingRoute);
// here we can filter the EvaluationHead collection
var referenceToEvaluationHeadQuery = query // here we start again from the base query
.JoinQueryOver<EvaluationHead>(p => p.EvaluationHeads, () => evaluationHead)
.Where(c => c.EvaluationHeadID == headId);
dto = query
.SelectList(l => l
.Select(() => trainingCourse.TrainingCourseDefn)
.WithAlias(() => d.TrainingCourseDefn)
.Select(() => trainingCourse.IsAvailable)
.WithAlias(() => d.IsAvailable)
// now let's used join alias
.Select(() => trainingRoute.TrainingRouteDefinition)
.WithAlias(() => d.TrainingRouteDefinition))
.TransformUsing(Transformers.AliasToBean<TrainingCourseDTO>())
.List<TrainingCourseDTO>();
Or you can use JoinAlias, see more here 16.4. Associations

Categories

Resources