Why can't NHibernate access a property inherited from an abstract base class. When I try to use the property in a QueryOver in the Where clause I'm getting
could not resolve property: ID of: TheWorkshop.Web.Models.Customer
var customer = Session.QueryOver<Customer>()
.Where(c=>c.ID ==id)
.SingleOrDefault<Customer>();
Intelisense helped me build the query and the solution compiles, so there is an ID property on the Customer class. The ID property on Customer is inherited from an abstract Contact class that in turn inherits from a DomainEntity<T> which exposes a protected field.
public abstract class DomainEntity<T>
{
protected Guid _persistenceId;
//...
}
public abstract class Contact : DomainEntity<Contact>
{
public virtual Guid ID
{
get { return _persistenceId; }
}
public virtual Address Address
{
get { return _address; }
set { _address = value; }
}
//...
}
and in the mapping file
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="TheWorkshop.Web"
namespace="TheWorkshop.Web.Models"
default-access="field.camelcase-underscore"
default-lazy="true">
<class name="Contact" table="Contacts" abstract="true">
<id name="_persistenceId" column="ID" type="Guid" access="field"
unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
<!-- ... -->
<union-subclass name="Customer" table="Customers">
Following the answer to a similar question I updated to NHibernate 3.3.3-CR1 from NHibernate 3.3.2.4000 but I still have the same issue.
The problem was that NHibernate couldn't infer from my mapping how to resolve the ID property. So although the classes compiled fine and the _persistenceId property on the abstract base class could be accessed through a getter on the implementing classes, because of the mismatch in names between _persistenceId and ID NHibernate wasn't able to follow that.
The (easier) solution was to change my names to match up. There is a harder solution which involves implementing the IProperyAccessor, IGetter and ISetter interfaces and in order to provide a path to pass the string ID in order to use the ClassName access strategy.
The simpler of the two solutions was just to rename _persistenceId to _id (and update all the references to it) so
<id name="_persistenceId" column="ID" type="Guid" access="field"
unsaved-value="00000000-0000-0000-0000-000000000000">
becomes
<id name="Id" column="Id" type="Guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
Note I was also able to drop the access="field" in the updated id mappings
Related
This method gets called.
public IList<MyStuff> GetMyStuff(Int64 MyStuffId)
{
ICriteria criteria = NHibernateSessionManager.Instance.GetSession().CreateCriteria(typeof(MyStuff));
criteria.Add(Expression.Eq("x", MyStuff));
return criteria.List<MyStuff>();
}
But if I profile SQL Server, I can see that NHibernate doesn't try to access the server.
No errors are thrown. It is just the criteria.List() simply returns 0 rows.
MyStuff is a class
public class MyStuff {
public virtual int Id { get; set; }
public virtual int x { get; set; }
... more attributes ....
public override int GetHashCode() {
return (GetType().FullName + "|" + Id.ToString()).GetHashCode();
}
}
And MyStuff is a HBM mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="false" assembly="MyStuff" namespace="My.Stuff" default-lazy="false">
<class name ="MyStuff" table="dbo.viewMyStuff" dynamic-update="false" lazy="false">
<cache usage="read-only"/>
<id name="Id" column="Id" type="int">
<generator class="native" />
</id>
<property name="x" />
.... other properties
</class>
</hibernate-mapping>
The following works just:
select * from viewMyStuff
NHibernate does just fine with other classes/views in the same project.
In fact if I intentionally typo the "table" in the HBM file to "XviewXMyStuffX" NHibernate doesn't have any problem with the typo. Why is NHibernate simply ignoring the expected attempt to access my database view?
I turns out that the view treats the attribute "x" as a string. But in nHibernate I define it as a Int64. These type differences much be causing the criteria to fail. But without any reported error?
Double check that your query really tries to use the exact class you intenden, and that the mapping also applies to exactly the same class. Beware of classes with same name in different namespace or assembly, for instance. One cause of this type of issue is if you attempt to query for a class that is in fact not mapped in NHibernate - then NHibernate will return en empty result, and not an error.
Oh, and have you tried without the cache-element to rule that out?
We have a BaseEntity of which all our other domain classes inherit. On this BaseEntity are some basic properties. This could be something like DateLastChange for example.
We're using NHibernate with hbm mapping files. I'm trying to avoid having to map DateLastChange in every mapping file.
I found this post by Ayende, which makes me believe I could use union-subclass to achieve this (see his last approach). However, he includes a table name for his abstract class, that isn't in his table-schema.
<class name="Party"
abstract="true"
table="Parties">
...
Does the table have to exist, or will NHibernate just ignore this attribute? And can I then omit it?
This is not needed. As per the documentation (thanks to kalki):
<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="sequence"/>
</id>
<property name="amount" column="AMOUNT"/>
...
<union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT">
<property name="creditCardType" column="CCTYPE"/>
...
</union-subclass>
<union-subclass name="CashPayment" table="CASH_PAYMENT">
...
</union-subclass>
<union-subclass name="ChequePayment" table="CHEQUE_PAYMENT">
...
</union-subclass>
</class>
And:
If your superclass is abstract, map it with abstract="true". If it is
not abstract, an additional table (it defaults to PAYMENT in the
example above), is needed to hold instances of the superclass.
I have the following abstract class in fluentnhibernate:
public abstract class EntityMapping<TEntity> : ClassMap<TEntity> where TEntity : EntityBase
{
protected EntityMapping()
{
Id(x => x.Id, "Id")
.UnsavedValue("00000000-0000-0000-0000-000000000000")
.GeneratedBy.GuidComb()
.Index("IX_Lookup");
OptimisticLock.Version();
Version(x => x.Version);
Map(x=>x.DateLastChange); // your column
}
}
all other mappings use the abstract class:
public SomeEntityMap:EntityMapping<SomeEntity>{
public SomeEntityMap(){
Map(x=>x.SomeProperty);
}
}
I have a baseclass that IS NOT abstract and two classes that is based on this class but which have different implemenations in how they calculate the result. The baseclass also inherits from an abstract class that is shared in many different places in the system so I cannot really change that one.
I know that I could extract a baseclass that all three inherits from and just use a normal mapping with subclasses but I just want to know if it is possible to create a hbm file that maps this scenario.
class BaseClass : CalculationBaseClass
{
public virtual int Calculate()
{
...
}
}
class SpecializedClass : BaseClass
{
public override int Calculate()
{
...
}
}
class HistoricClass : BaseClass
{
public override int Calculate()
{
...
}
}
From NHibernate documentation:
NHibernate supports the three basic inheritance mapping strategies.
table per class hierarchy
table per subclass
table per concrete class
You would choose one of the strategies based on what you current table structure is, or if you don't have legacy schema you can just choose the one that is most appropriate for you object model (based on mapped properties for example). In your case, if you use 'table per class hierarchy' you would end up with mapping like this:
<class name="CalculationBaseClass" table="MyTable">
<id name="Id" type="Int64" column="ID">
<generator class="native"/>
</id>
<discriminator column="TYPE" type="String"/>
<subclass name="BaseClass" discriminator-value="BASE">
...
</subclass>
<subclass name="SpecializedClass" discriminator-value="SPECIALIZED">
...
</subclass>
<subclass name="HistoricClass " discriminator-value="HISTORIC">
...
</subclass>
</class>
I have the following entities:
namespace NhLists {
public class Lesson {
public virtual int Id { get; set; }
public virtual string Title { get; set; }
}
public class Module {
public virtual int Id { get; set; }
public virtual IList<Lesson> Lessons { get; set; }
public Module() {
Lessons = new List<Lesson>();
}
}
}
And the following mappings:
<class name="Module" table="Modules">
<id name="Id">
<generator class="identity"/>
</id>
<list name="Lessons" table="ModuleToLesson"
cascade="save-update">
<key column="moduleId"/>
<index column="position"/>
<many-to-many
column="lessonId"
class="NhLists.Lesson, NhLists"/>
</list>
</class>
<class name="Lesson" table="Lessons">
<id name="Id">
<generator class="identity"/>
</id>
<property name="Title">
<column name="Title" length="16" not-null="true" />
</property>
</class>
When I delete a lesson by session.Delete(lesson), is there anyway I can have NHibernate automatically update the association in Module.Lessons to remove the entry from the set? Or am I forced to go through all Modules and look for the lesson and remove that by hand?
Edit: Fixed ICollection and <set> in mappings to IList<> and <list> like I want and tested it.
You have false idea. If you want to delete the Lesson object from Module you do that manually. NHibernate just tracks such your action and when session.Commit() is called then the reference between Module and Lesson is deleted in the database.
Calling session.Delete(lesson) deletes the lesson object from database (if foreign keys are set properly then reference between Module and Lesson is deleted of course but it is not responsibility for NHibernate).
In conclusion, it is not possible to delete the lesson object from the Module.Lessons list automatically by calling session.Delete(lesson). NHibernate does not track such entity references.
Turns out that if we do not need IList semantics and can make do with ICollection the update problem can be solved by adding a reference back from Lesson to Module, such as:
public class Lesson {
...
protected virtual ICollection<Module> InModules { get; set; }
...
}
And to the mapping files add:
<class name="Lesson" table="Lessons">
...
<set name="InModules" table="ModuleToLesson">
<key column="lessonId"/>
<many-to-many column="moduleId" class="NhLists.Module, NhLists"/>
</set>
</class>
Then a Lesson deleted is also removed from the collection in Module automatically. This also works for lists but the list index is not properly updated and causes "holes" in the list.
I'm looking to create a many to many relationship using NHibernate. I'm not sure how to map these in the XML files. I have not created the classes yet, but they will just be basic POCOs.
Tables
Person
personId
name
Competency
competencyId
title
Person_x_Competency
personId
competencyId
Would I essentially create a List in each POCO for the other class? Then map those somehow using the NHibernate configuration files?
You can put the many-to-many relation to either class, or even to both. This is up to your domain model. If you map it to both, one of them is inverse.
class Person
{
// id ...
IList<Competency> Competencies { get; private set; }
// you domain model is responsible to manage bidirectional dependencies.
// of course this is not a complete implementation
public void AddCompetency(Competency competency)
{
Competencies.Add(competency);
competency.AddPerson(this);
}
}
class Competency
{
// id ...
IList<Person> Persons { get; private set; }
}
Mapping:
<class name="Person">
<id ....>
<bag name="Competencies" table="Person_x_Competency">
<key column="personId"/>
<many-to-many class="Competency" column="competencyId"/>
</bag>
</class>
<class name="Competency">
<id ....>
<bag name="Persons" table="Person_x_Competency" inverse="true">
<key column="competencyId"/>
<many-to-many class="Person" column="personId"/>
</bag>
</class>
Only make it bidirectional if you really need it.
By the way: it is much better to write the classes first and create the database design afterwards. The database can be exported from the mapping files. This is very useful.