I have a class which contains a collection of Points (PointF's rather).
I want to be able to persist instances of that class using NHibernate.
My class looks somewhat like this (simplified):
public class MyClass
{
public IDictionary<string, PointF> Points = new Dictionary<string, PointF>();
public void AddPoint( location, PointF position )
{
Points.Add(location, position);
}
}
The mapping of this collection looks like this (simplified):
<map name="Points" table="Locations">
<key column="MyClassId" />
<index column="LocationName" />
<composite-element class="System.Drawing.PointF, System.Drawing">
<property name="X" column="X" />
<property name="Y" column="Y" />
</composite-element>
</map>
The problem now is, that NHibernate throws an error while processing the mapping file, since PointF is not a known (mapped) entity.
How can I solve this in the most simple way ?
How can I make sure that NHibernate is able to persist my collection of locations (with their coordinates (point) ?
The problem is not that you didn't map the type PointF - because you map it as composite-element, which is correct.
When mapping such types you need to make sure
that properties are writable (which is luckily the case here)
that it has a default constructor, which is not the case here.
So how should NH create new instances when there is not default constructor? It can't.
Your options are:
implement an interceptor or NH event. I think it is possible to inject code there which creates instances of certain types, but I don't know how.
implement a NH user type (derived from ICompositeUserType), which is not too hard to do
map another type (eg. a wrapper to PointF)
Related
I have two objects, Case and Note. A Case can have gobs of Notes, like, in the thousands. We are trying to load them asynchronously, in batches, and stream them to the UI so there is no delay waiting for them all to load.
The class/mappings are
public class Case
{
public virtual IList<Note> Notes { get; protected set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SCMS.TAMS.BusinessEntities" namespace="SCMS.TAMS.BusinessEntities">
<class name="Case" table="Cases">
<bag name="Notes" inverse="true" cascade="all" lazy="true">
<key column="CaseID" />
<one-to-many class="Note" />
</bag>
</class>
</hibernate-mapping>
public class Note
{
public virtual Case Case {get; set;}
public virtual long CaseId {get; set;}
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SCMS.TAMS.BusinessEntities" namespace="SCMS.TAMS.BusinessEntities" default-lazy="true">
<class name="Note" table="CaseNotes">
<many-to-one name="Case" column="CaseID"/>
<property name="CaseId" column="CaseID" />
</class>
</hibernate-mapping>
Now, when I call
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Skip(0).Take(10).ToList();
to load the first 10 Notes for Case 123, the thing loads the Case object, which takes about 30 seconds because there's lots of other things on it, and other logic when it gets loaded, etc., none of which I need/want at this time. All I want/need are the 10 Notes.
I've tried all sorts of variations on this mapping and none of them have worked. What am I doing wrong here?
How are you using this query? is it some thing for the UI? liking showing in a grid or something? or are you performing business logic in a component?
Either way you want to project into another object. Your query right now returns a list of notes which is then going to load that parent object per the mappings.
So if you are using this query to send the information to the UI of an asp.net mvc application, project directly into your view model
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Select(n => new SomeViewModel { Prop1 = n.Prop1, Prop2 = n.Prop2 ...}).Skip(0).Take(10).ToList();
or create an anonymous object
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Select n => new { n.Prop1, n.Prop2, ...}).Skip(0).Take(10).ToList();
This will keep the parent object from loading. It also has the added benefit that you are only querying the information you need because the query be limited to the data you are projecting.
Important to know is that if all above is true...
this is the real mapping (which is not it is just an obvious extract)
<class name="Note" table="CaseNotes">
<many-to-one name="Case" column="CaseID"/>
...
this is the class (again extract without ID)
public class Note
{
public virtual Case Case {get; set;}
public virtual long CaseId {get; set;}
}
and that would be a UNIT TEST statement to load notes:
var list = NHibernateSession
.Query<Note>()
.Where(n => n.CaseId == 123)
.Skip(0).Take(10)
.ToList();
then NHibernate will NEVER load the Case object. Never. Because:
NHibernate is lazy, just live with it
The reason, the trigger to load related reference (Case property) must be something explicit.
Mostly:
there is usage of the Case object somewhere. E.g. in override of the GetHashCode() the Case.ID is used
Or:
there is a serialization or DTO conversion which does touch the Case property
In those case, NHibernate must load that...
So, create few unit tests with basic queries and assure that your the is really as shown above. Then it will work as expected
I have an interface like this:
public interface ICoreType {
int TypeID {get;}
}
And an NHibernate class that implements it like this:
public class DueDateType : ICoreType {
public virtual int DueDateTypeID {get;set;}
...
public virtual int TypeID { get{ return this.DueDateTypeID; } }
}
Here's my mapping:
<class name="DueDateType" table="tdfDueDateType" lazy="true" >
<cache usage="read-write" include="all"/>
<id name="DueDateTypeID" column="DueDateTypeID" >
<generator class="identity"/>
</id>
...
</class>
When I get an instance of the object from NHibernate, and then return it outside the scope of the active session it was created in, I can get the value DueDateTypeID just fine, but accessing TypeID throws a LazyInitializationException. There is no mapping for TypeID, as I simply want it to return whatever DueDateTypeID's value is.
Marking the class lazy="false" in my mapping erases the problem. From what I've been reading, that is because there is no proxy generated for a non-lazy mapped class.
I would like to use the lazy loading features of NHibernate in this case; do I need to implement IInterceptor or some such, in order to get NHibernate to treat this property differently?
The problem is that all members of the entity trigger lazy initialization when accessed (except of the id). NH can't know what you actually are doing within the member. When triggering lazy initialization outside of the session, you get an error.
Suggestions:
Try to avoid any access to entities outside the session. It is only safe when turning off lazy loading - which is usually not a way to go.
Don't wrap the id. If you access TypeID, the entity is initialized although it is not necessary, which is bad for performance.
Following the technique described here, I was able to populate a domain object that uses custom collections for its children. The relevant property mapping looks like this:
<component name="Contacts" class="My.CustomList`1[[Domain.Object, DomainAssembly]], MyAssembly">
<set name="InnerList">
<key column="PARENT_ID" />
<one-to-many class="Contact" />
</set>
</component>
My custom collection class exposes the InnerList property as an ICollection like so:
protected System.Collections.ICollection InnerList
{
get
{
return this;
}
set
{
Clear();
foreach (DomainObject o in value)
{
Add(o);
}
}
}
This worked like a charm to load data from the database and not have to abandon my rather useful custom collection class.
Then I moved on to try implement saving, and following the advice given in this thread, decided to wrap every call to NHibernate in a transaction.
Now when I commit following my load, NHibernate throws an InvalidCastException: "Unable to cast object of type 'My.CustomList`1[Domain.Object, DomainAssembly]' to type 'Iesi.Collections.ISet'."
Is there a way to make this work the way I expect it to?
EDIT:
Following the lead provided by Raphael, I tried switching to ICollection<T> which gives me a different InvalidCastException when I commit the transaction: Unable to cast object of type 'My.CustomList`1[Domain.Object]' to type 'NHibernate.Collection.IPersistentCollection'.
Change the property to be of type
IList<T>
I have the following class
public class Person
{
private IList<Person> _children;
public IEnumerable<Person> Children { get; }
public void AddChild(Person child)
{
// Some business logic and adding to the internal list
}
}
What changes would I have to make for NHibenrate to be able to persist the Child collection (apart from making everything virtual, I know that one).
Do I have to add a setter to the children property which does something like a _children.Clear(); _children.AddRange(value). Currently the model expresses my intent quite nicely but I'm not sure how much alteration is need for NH to be able to help me out with persistence.
NHibernate is able to map private fields. Access and naming strategies are discussed in the property section of the reference documentation.
Making your public members virtual is required for proxies to work. These will usually be runtime-generated subclasses of your entity classes.
In this example mapping the field _children will be Children in HQL and Criteria queries.
<class name="Person" table="person">
<bag name="Children" access="field.camelcase-underscore">
<key column="parentid" />
<one-to-many class="Person" />
</bag>
</class>
For mapping component in nhibernate , is there a way in the hmb file we can indicate a overlaoded constructor to be used instead of default one.
In below mapping nHibernate will use the default constructor of MyClass when reading data from database - I am wondering if we can instruct nhibernate to use a overloaded constructor instead ?
<component name="MyProperty" class="MyClass" >
<property name="Member1" column="member_1" />
<property name="Member2" column="member_2" />
<property name="Member3" column="member_3" />
</component >
Edit #1
Alternatively , does nHibernate allow to map a static value to a property instead of a column ?
Something like below:
<component name="MyProperty" class="MyClass" >
<property name="Member1" column="member_1" />
<property name="Member2" column="member_2" />
<property name="Member3" **value="555"** />
</component >
NHibernate will always use the default constructor to instantiate an object of the type, (unless you want to create some kind of DTO and retrieve it via HQL) and then it will use the properties (or backing fields if specified) to populate the object.
If you have a type for which you do not want to expose a default (no-args) constructor, but you want to make sure that you can only instantiate the type via a specific constructor, then I always do it like this:
public class MyClass
{
private MyClass()
{
// Default constructor has been made private, so it is not usable
// by user code, but NHibernate requires a default constructor
// (it may be private)
}
public MyClass( int member1, int member2, string member3 )
{
}
}
The only standard way you can use constructor with parameters in NHibernate using select new HQL construct:
select new Family(mother, mate, offspr)
from Eg.DomesticCat as mother
join mother.Mate as mate
left join mother.Kittens as offspr
Otherwise it uses parameterless constructors for all purposes. I'm not sure whether this can be altered by hacking NHibernate internals (IClassPersister, IInterceptor, etc.)