Hi i have a problem with mapping in Nhibernate. When I run a linq query referring to one of the component classes of my entity, I get a QueryException as below:
could not resolve property: ClosedCases of: Project.Entities.Headline
I have one table with records which i want to map into multiple objects of my headline entity.
Question:
Is there something wrongly set up in my mapping?
My Headline entity is separated into multiple logical classes as you can see below
public class Headline:Entity
{
public virtual DateTime Date { get; set; }
public virtual TeamTarget Teamtarget { get; set; }
}
public class TeamTarget : Entity
{
public virtual DateTime FromDate { get; set; }
public virtual DateTime ToDate { get; set; }
public virtual TargetItem AchievedTarget { get; set; }
public virtual Team Team { get; set; }
}
public class TargetItem : Entity
{
public virtual decimal ClosedCases { get; set; }
public virtual decimal Invoicing { get; set; }
}
Mapping file:
public class HeadlineMap: ClassMap<Headline>
{
public HeadlineMap()
{
Table("Headlines.Headlines");
Id(x => x.Id).Column("HeadlinesId");
Map(x => x.Date);
Component(x => x.Teamtarget, t =>
{
t.References(x => x.Team).Column("TeamId").Cascade.None();
t.Component(x => x.AchievedTarget, ti =>
{
ti.Map(x => x.Invoicing).Column("TeamInvoicing");
ti.Map(x => x.ClosedCases).Column("TeamClosedCases");
});
});
The Linq query I am running looks like this:
decimal closedCases = _headlineRepository.All
.Where(x =>
x.Date >= DateTimeExtensionMethods.FirstDayOfMonthFromDateTime(selectMonthFromDate)
&& x.Date <= DateTimeExtensionMethods.LastDayOfMonthFromDateTime(selectMonthFromDate)
&& x.Teamtarget.Team.Id == teamId
).Average(x => x.Teamtarget.AchievedTarget.ClosedCases);
I know that i can access headline item by using team entity by team id and this works, just have problem with the mapping of achieved target.
any ideas?
I think your mapping is fine. But as far as I know, it isn't possible to query a nested component with Linq to NHibernate. It can't understand what joins to do, it becomes too complex.
I think it is possible using NHibernate 3's QueryOver API using JoinQueryOver or JoinAlias. You should read this: QueryOver in NH 3.0
Maybe something like this would work:
Headline headlineAlias = null;
TeamTarget targetAlias = null;
Team teamAlias = null;
TargetItem targetItemAlias = null;
var query = session.QueryOver<Headline>(() => headlineAlias)
.JoinAlias(() => headlineAlias.Teamtarget, () => targetAlias)
.JoinAlias(() => targetAlias.Team, () => teamAlias)
.JoinAlias(() => targetAlias.AchievedTarget, () => targetItemAlias)
.Where(x => x.Date >= DateTimeExtensionMethods.FirstDayOfMonthFromDateTime(selectMonthFromDate) && x.Date <= DateTimeExtensionMethods.LastDayOfMonthFromDateTime(selectMonthFromDate))
.And(() => teamAlias.Id == teamId)
.Select(Projections.Avg(() => targetItemAlias.ClosedCases))
.SingleOrDefault<decimal>();
Related
I have these classes:
public class Document
{
public Document()
{
Descriptions = new List<Descriptions>();
}
public virtual int Id { get; set; }
public virtual IList<DocumentDescription> Descriptions { get; set; }
}
public class DocumentDescription
{
public virtual int DocumentId { get; set; }
public virtual int LanguageId { get; set; }
}
and mappings:
public DocumentMap()
{
Id(x => x.Id);
HasMany(x => x.Descriptions).KeyColumn("DocumentId");
}
public DocumentDescriptionMap()
{
CompositeId()
.KeyProperty(x => x.DocumentId)
.KeyProperty(x => x.LanguageId);
}
my query is:
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds)).List();
I need a query over solution to restrict DocumentDescriptions by few languages, which I will get run-time. I don't want to get all DocumentDescriptions for one Document (only few). Is it possible to set filter/limitation for a child collection?
So, I find out how to add additional join clause to my query:
DocumentDescription dd = null;
ICriterion criterion = Restrictions.On<DocumentDescription>(x => x.LanguageId).IsIn(languageIds.ToArray());
var query = Session.QueryOver<Document>().Where(x => x.Id.IsIn(documentIds));
query.Left.JoinQueryOver(x => x.Descriptions, () => dd, criterion);
SQL:
SELECT * FROM tDocument
LEFT OUTER JOIN tDocumentDescription ON tDocumentDescription.DocumentId = tDocument.Id AND tDocumentDescription.LanguageId IN (#languageIds)
WHERE tDocument.Id IN (#documentIds)
I have the following parent entity Department which contains a collection of child entities Sections
public class Department
{
private Iesi.Collections.Generic.ISet<Section> _sections;
public Department()
{
_sections = new HashedSet<Section>();
}
public virtual Guid Id { get; protected set; }
public virtual string Name { get; set; }
public virtual ICollection<Section> Sections
{
get { return _sections; }
}
public virtual int Version { get; set; }
}
public partial class Section
{
public Section()
{
_employees = new HashedSet<Employee>();
}
public virtual Guid Id { get; protected set; }
public virtual string Name { get; set; }
public virtual Department Department { get; protected set; }
public virtual int Version { get; set; }
}
I would like to transform (flatten) it to the following DTO
public class SectionViewModel
{
public string DepartmentName { get; set; }
public string SectionName { get; set; }
}
Using the following code.
SectionModel sectionModel = null;
Section sections = null;
var result = _session.QueryOver<Department>().Where(d => d.Company.Id == companyId)
.Left.JoinQueryOver(x => x.Sections, () => sections)
.Select(
Projections.ProjectionList()
.Add(Projections.Property<Department>(d => sections.Department.Name).WithAlias(() => sectionModel.DepartmentName))
.Add(Projections.Property<Department>(s => sections.Name).WithAlias(() => sectionModel.SectionName))
)
.TransformUsing(Transformers.AliasToBean<SectionModel>())
.List<SectionModel>();
I am however getting the following exception: could not resolve property: Department.Name of: Domain.Section
I have even tried the following LINQ expression
var result = (from d in _session.Query<Department>()
join s in _session.Query<Section>()
on d.Id equals s.Department.Id into ds
from sm in ds.DefaultIfEmpty()
select new SectionModel
{
DepartmentName = d.Name,
SectionName = sm.Name ?? null
}).ToList();
Mappings
public class DepartmentMap : ClassMapping<Department>
{
public DepartmentMap()
{
Id(x => x.Id, m => m.Generator(Generators.GuidComb));
Property(x => x.Name,
m =>
{
m.Length(100);
m.NotNullable(true);
});
Set(x => x.Sections,
m =>
{
m.Access(Accessor.Field);
m.Inverse(true);
m.BatchSize(20);
m.Key(k => { k.Column("DeptId"); k.NotNullable(true); });
m.Table("Section");
m.Cascade( Cascade.All | Cascade.DeleteOrphans);
},
ce => ce.OneToMany());
}
}
public class SectionMap : ClassMapping<Section>
{
public SectionMap()
{
Id(x => x.Id, m => m.Generator(Generators.GuidComb));
Property(x => x.Name,
m =>
{
m.Length(100);
m.NotNullable(true);
});
ManyToOne(x => x.Department,
m =>
{
m.Column("DeptId");
m.NotNullable(true);
});
}
}
But this throws a method or operation is not implemented.
Seeking guidance on what I am doing wrong or missing.
NHibernate doesn't know how to access a child property's child through the parent entity. A useful thing to remember about QueryOver is that it gets translated directly into SQL. You couldn't write the following SQL:
select [Section].[Department].[Name]
right? Therefore you can't do the same thing in QueryOver. I would create an alias for the Department entity you start on and use that in your projection list:
Department department;
Section sections;
var result = _session.QueryOver<Department>(() => department)
.Where(d => d.Company.Id == companyId)
.Left.JoinQueryOver(x => x.Sections, () => sections)
.Select(
Projections.ProjectionList()
.Add(Projections.Property(() => department.Name).WithAlias(() => sectionModel.DepartmentName))
.Add(Projections.Property(() => sections.Name).WithAlias(() => sectionModel.SectionName))
)
.TransformUsing(Transformers.AliasToBean<SectionModel>())
.List<SectionModel>();
I noticed in your comment you'd like an order by clause. Let me know if you need help with that and I can probably come up with it.
Hope that helps!
This may be now fixed in 3.3.3. Look for
New Feature
[NH-2986] - Add ability to include collections into projections
Not sure but if this is your problem specifically but if you are not using 3.3.3 then upgrade and check it out.
Aslo check out the JIRA
Have you tried a linq query like
from d in Departments
from s in d.Sections
select new SectionModel
{
DepartmentName = d.Name,
SectionName = s == null ? String.Empty : s.Name
}
Though I have benefited from the collective wisdom of this site many times, this is my first question here.
I have, say, three classes like so:
public class ClassA
{
public ClassA() {}
public virtual IList<ClassB> ClassBs { get; set; }
}
public class ClassB
{
public ClassB() {}
public virtual DateTime StartDate { get; set; }
public virtual DateTime? EndDate { get; set; }
public virtual ClassC SomeObject { get; set; }
}
public class ClassC
{
public ClassC() {}
public virtual string Name { get; set; }
}
I am using NHibernate (3.3.1.4) and FluentNHibernate (1.3.0.733), and the mapping files are:
public Class ClassAMap : ClassMap<ClassA>
{
HasMany(x => x.ClassBs);
}
public Class ClassBMap : ClassMap<ClassB>
{
Map(x => x.StartDate).Not.Nullable();
Map(x => x.EndDate).Nullable();
References(x => x.SomeData).Not.Nullable();
}
public Class ClassCMap : ClassMap<ClassC>
{
Map(x => x.Name).Not.Nullable();
}
There are also IDs and versioning but I somehow think they are irrelevant.
What I want to do is:
select all "SomeObject"s from ClassBs of all "ClassA"s which have their "EndDate"s null and which have the most current StartDate in their group.
I tried some juggling with QueryOver but the most I could get was an IList<IList<ClassB>> which is really far from what I want to accomplish.
Edit:
(I think) Following code accomplishes the task with Linq. But this code requires all records from the DB. This may not be a problem for ClassBs with up to 3 records or so per ClassA but for ClassBs with hundreds of records this means getting all those records from DB to use just one record from ClassBs of each ClassA in the DB.
IList<ClassC> classCLs = new List<ClassC>();
ClassB latest = null;
foreach (
IList<ClassB> classBLs in
Session.QueryOver<ClassA>()
.Select(c => c.ClassBs).List<IList<ClassB>>()
) {
latest = classBLs.Where(cB => cB.EndDate == null).Aggregate((curr, next) => next.StartDate > curr.StartDate ? next : curr);
if (latest != null && !classCLs.Contains(latest.SomeObject)) {
classCLs.Add(latest.SomeObject);
}
}
Assuming your ClassA has an Id property, maybe this, involving a subquery, can be of some help :
ClassA aAlias = null;
ClassB bAliasMax = null, bAlias = null;
var subQuery = QueryOver.Of<ClassA>().JoinAlias(a => a.ClassBs, () => bAliasMax)
.Where(Restrictions.On(() => bAliasMax.EndDate).IsNotNull)
.Where(a=>a.Id==aAlias.Id)
.Select(Projections.Max(() => bAliasMax.StartDate));
var result =
_laSession.QueryOver(() => aAlias)
.JoinAlias(a => a.ClassBs, () => bAlias)
.WithSubquery.WhereProperty(() => bAlias.StartDate).Eq(subQuery)
.Select(Projections.Property(() => bAlias.SomeObject)) // suggested adding from question's author
.List<ClassC>(); // see above
I guess there are more effective answers, which would involve grouping.
Using NHibernate 3.2 ByCode configuration, I am attempting to map the following hierarchical entity:
public class BusinessType
{
public virtual Guid BusinessTypeId { get; set; }
public virtual Guid? ParentBusinessTypeId { get; set; }
public virtual String BusinessTypeName { get; set; }
public virtual ICollection<BusinessType> Children { get; set; }
}
with this ClassMapping:
public class BusinessTypeMapper : ClassMapping<BusinessType>
{
public BusinessTypeMapper()
{
Id(x => x.BusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.ParentBusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.BusinessTypeName);
Set(x => x.Children,
cm =>
{
// This works, but there is an ugly string in here
cm.Key(y => y.Column("ParentBusinessTypeId"));
cm.Inverse(true);
cm.OrderBy(bt => bt.BusinessTypeName);
cm.Lazy(CollectionLazy.NoLazy);
},
m => m.OneToMany());
}
}
This works fine, but I'd rather be able to specify the key of the relation using a lambda so that refactoring works. This seems to be available, as follows:
public class BusinessTypeMapper : ClassMapping<BusinessType>
{
public BusinessTypeMapper()
{
Id(x => x.BusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.ParentBusinessTypeId, x => x.Type(new GuidType()));
Property(x => x.BusinessTypeName);
Set(x => x.Children,
cm =>
{
// This compiles and runs, but generates some other column
cm.Key(y => y.PropertyRef(bt => bt.ParentBusinessTypeId));
cm.Inverse(true);
cm.OrderBy(bt => bt.BusinessTypeName);
cm.Lazy(CollectionLazy.NoLazy);
},
m => m.OneToMany());
}
}
The problem is that this causes NHibernate to generate a column called businesstype_key, ignoring the already-configured ParentBusinessTypeId. Is there any way to make NHibernate use a lambda to specify the relation, rather than a string?
I never need to navigate from children to parents, only from parents
to children, so I hadn't thought it necessary
then remove public virtual Guid? ParentBusinessTypeId { get; set; } completly. NH will then only create "businesstype_key" (convention) and no "ParentBusinessTypeId". if you want to change that then you have to specify your prefered columnname with cm.Key(y => y.Column("yourpreferredColumnName"));
I'm trying to do what seems like a simple query for the number of Products produced by each Manufacturer, but NHibernate isn't generating T-SQL that MS SQL Server finds valid.
session.Query<Product>()
.GroupBy(p => p.Manufacturer)
.Select(grp => new {Mftr = grp.Key.Name, ProductCount = grp.Count()})
.ToList();
This seems dead simple, but the SQL statement that NHibernate generates doesn't include all the necessary column names, so it fails when running against a SQL Server 2008 or SQL Server CE database. If I point the same code to an in-memory SQLite database, it works fine.
More information is below, and I also created a small console application that demonstrates my problem. How do I fix this problem?
Generated SQL
select manufactur1_.Id,
cast(count(*) as INT),
manufactur1_.Id,
manufactur1_.Name
from "Product" product0_
left outer join "Manufacturer" manufactur1_
on product0_.Manufacturer_id=manufactur1_.Id
group by manufactur1_.Id -- Where's manufactur1_.Name?
Entities
public class Product {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Manufacturer Manufacturer { get; set; }
}
public class Manufacturer {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
FNH Mappings
public class ProductMap : ClassMap<Product> {
public ProductMap() {
Id(x => x.Id).GeneratedBy.HiLo("1");
Map(x => x.Name);
References(x => x.Manufacturer).Cascade.SaveUpdate().Not.Nullable();
}
}
public class ManufacturerMap : ClassMap<Manufacturer> {
public ManufacturerMap() {
Id(x => x.Id) .GeneratedBy.HiLo("1");
Map(x => x.Name);
}
}
Here's a QueryOver version...
//alias variables
Manufacturer m = null;
ManufacturerProducts dto = null;
var result = Session.QueryOver<Product>
.Left.JoinAlias(x => x.Manufacturer, () => m)
.SelectList(list => list
.SelectGroup(() => m.Id).WithAlias(() => dto.Id)
.SelectGroup(() => m.Name).WithAlias(() => dto.Name)
.SelectCount(x => x.Id).WithAlias(() => dto.ProductCount))
.TransformUsing(Transformers.AliasToBean<ManufacturerProducts>())
.List<ManufacturerProducts>();