I am trying to convert this code:
Component(x => x.User, x =>
{
x.References(m => m.UserAccount).Columns(#"UserAccountId", #"UserAccountType");
x.References(m => m.Postman, #"PostmanId");
});
back into hbm.xml, my question: is everything alrigh with way I did it or am I missing something?
Converted code:
<component name="User">
<many-to-one name="UserAccount">
<column name="UserAccountId"/>
<column name="UserAccountType"/>
</many-to-one>
<many-to-one name="Postman" column="PostmanId"></many-to-one>
</component>
You can output all of your fluent nhibernate mappings as xml if you want. Sounds a lot easier than this.
Generate XML mappings from fluent Nhibernate
Related
I need to retrieve all the users with a valid Wish property (so not null). This is the xml of my class:
<class name="Project.Engine.Domain.User,Project.Engine" table="Users" lazy="true">
<id name="UserID" column="UserID">
<generator class="native" />
</id>
<property name="Firstname" column="Firstname" type="string" not-null="true"
length="255" />
<property name="Lastname" column="Lastname" type="string" not-null="true"
length="255" />
<property name="Email" column="Email" type="string" not-null="true"
length="255" />
<one-to-one name="Wish" cascade="all" property-ref="UserID"
class="Project.Engine.Domain.Wish, Project.Engine" />
</class>
The method to get all my users is the following:
public PagedList<User> GetAll(int pageIndex, int pageSize,
string orderBy, string orderByAscOrDesc)
{
using (ISession session = NHibernateHelper.OpenSession())
{
var users = session.CreateCriteria(typeof(User));
users.Add(Restrictions.IsNotNull("Wish"));
return users.PagedList<User>(session, pageIndex, pageSize);
}
}
As you can notice, I have added the Restriction on the child object. This doesn't work properly as the method return all users including the ones with Wish property as null. Any help?
this is the xml for child:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Project.Engine.Domain.Wish,Project.Engine" table="Wish" lazy="false">
<id name="WishID" column="WishID">
<generator class="native" />
</id>
<property name="UserID" column="UserID" type="int" not-null="true" length="32" />
<property name="ContentText" column="ContentText" type="string" not-null="false" length="500" />
<property name="Views" column="Views" type="int" not-null="true" length="32" />
<property name="DateEntry" column="DateEntry" type="datetime" not-null="true" />
</class>
</hibernate-mapping>
Well, there is a bug with one-to-one and null testing of the side which may not exist. I had already encountered it but forgot about it. The property-ref just render it a bit more tricky to diagnose, but it does exist on actual one-to-one too.
Here is its corresponding issue in NHibernate tracking tool.
Workaround: test for null state of an non-nullable property of Wish, like Wish.Views.
Forgive the wild guess on test syntax, I do not use nhibernate-criteria anymore since years, but try by example:
public PagedList<User> GetAll(int pageIndex, int pageSize,
string orderBy, string orderByAscOrDesc)
{
using (ISession session = NHibernateHelper.OpenSession())
{
var users = session.CreateCriteria(typeof(User));
users.Add(Restrictions.IsNotNull("Wish.Views"));
return users.PagedList<User>(session, pageIndex, pageSize);
}
}
Using linq-to-nhibernate, I confirm this workaround works with my own projects, which gives by example:
// The "TotalAmount != null" seems to never be able to come false from a
// .Net run-time view, but converted to SQL, yes it can, if TransactionRecord
// does not exist.
// Beware, we may try "o.TransactionsRecord != null", but you would get struck
// by https://nhibernate.jira.com/browse/NH-3117 bug.
return q.Where(o => o.TransactionsRecord.TotalAmount != null);
I maintain my other answer since you may consider using a many-to-one instead, especially since you do not have made a bidirectionnal mapping (no corresponding constrained one-to-one in Wish) in addition to not having an actual one-to-one. many-to-one does not suffer of the bug.
one-to-one mapping using property-ref is not an "actual" one-to-one, and usually this is a sign a many-to-one mapping should be used instead.
Maybe this is not related to your trouble, but you may give it a try.
An "actual" one-to-one has the dependent table primary key equals to the parent table primary key. (Dependent table, Wish in your case, would have a foreign primary key, UserId in your case. See this example.)
I have sometime "played" with 'one-to-one property-ref', and I always ended giving it up due to many issues. I replaced that with more classical mappings, either changing my db for having an actual one-to-one, or using a many-to-one and living with a collection on child side though it would always contain a single element.
Hello I am presently converting the xml mappings to code mappings and I am stuck at one place as I am not getting the proper way to convert ManyToOne Mappings.
The XML Mappings are
</many-to-one>
<many-to-one class="MyProject.Activity.Communication, MyProject.Activity" name="Comm">
<column name="CommID" />
</many-to-one>
Now I have this MyProject.Activity.Communication in other solution and don't have any reference in my mapping project. I want to specify class in my code mappings.
ManyToOne(x => x.Comm, map =>
{
map.Column("CommID");
});
How do I specify class in this mapping as the Entity name is referenced so I need to add the class in my code mappings.
The mappings would be using Reflection to fetch the assembly name.
ManyToOne(x => x.Survey, map =>
{
map.Column("SurveyID");
map.Class(Type.GetType("MyProject.Activity.Communication, MyProject.Activity"));
});
I am working with a brownfield database and am trying to configure a subclass map which joins to its subclasses with a column other than that of the specified id. The login table has a primary key column login_sk which I'd like to use as its id. It joins to two tables via a login_cust_id column (to make things more fun the corresponding columns in the adjoining tables are named differently). If I setup login_cust_id as the id of the UserMap it joins to its subclasses as expected. For what I hope are obvious reasons I do not want to use login_cust_id as the id for my User objects.
public class UserMap : ClassMap<IUser>
{
public UserMap()
{
Table("login");
Id(x => x.Id).Column("login_sk"); // want to setup map like this
// if used instead this works for subclass joining / mapping
// Id(x => x.Id).Column("login_cust_id");
// would prefer to only reference login_cust_id for subclass mapping
}
}
public class CustomerUserMap : SubclassMap<CustomerUser>
{
public CustomerUserMap()
{
Table("customer");
Map(c => c.DisplayName, "cust_mail_name");
Map(c => c.RecordChangeName, "cust_lookup_name");
KeyColumn("cust_id");
}
}
public class EntityUserMap : SubclassMap<EntityUser>
{
public EntityUserMap()
{
Table("entity");
Map(c => c.DisplayName, "entity_name");
KeyColumn("entity_id");
}
}
What I'd like to do is only use the login_cust_id column when joining to subclasses. Is there a fluent mapping setting that allows me to specify this? If not a fluent mapping is there a regular NHibernate XML mapping that would work? I'd prefer to not even map the column and only use it for joining if possible. If it helps there is a potential discriminator column login_holder_type which indicates which table to join to.
It did occur to me to setup an IClassConvention but after poking at the passed IClassInstance I could not determine any settings which would help me.
public class UserIdConvention : IClassConvention, IClassConventionAcceptance
{
public void Apply(IClassInstance instance)
{
// do something awesome with instance.Subclasses to
// specify the use of login_cust_id for subclass joining...
}
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => typeof(User).Equals(x.EntityType));
}
}
The lack of a populated Subclasses collection for the passed instance caused me to look for a more specific inspector which IParentInspector appears to be. Unfortunately Fluent NHibernate does not appear to have corresponding implementations for IParentInstance, IParentConvention or IParentConventionAcceptance like it does for IJoinedSubclassInspector. While I could probably implement my own before I do I wanted to ensure I wasn't barking up the wrong tree.
Is this sort of subclass id adjustment even possible? Am I missing something obvious in either my map or the Fluent NHibernate Conventions namespace? How can I map to a joined subclass with a different column/property than the id of parent?
I was able to think of three possible solution to your problem please see my findings below.
Solution 1: Discriminator based mapping with Join
My initial idea was to use a discriminator based mapping for modelling the inheritance, with each sub-class containing a join with a property ref, i.e
<class name="IUser" abstract="true" table="login">
<id name="Id" column="login_sk">
<generator class="identity"/>
</id>
<discriminator column="login_holder_type" not-null="true" type="System.String"/>
<subclass name="CustomerUser" discriminator-value="Customer">
<join table="customer" >
<key column="cust_id" property-ref="login_cust_id" />
<property name="DisplayName" column="cust_mail_name"/>
<property name="RecordChangeName" column="cust_lookup_name" />
</join>
</subclass>
<subclass name="EntityUser" discriminator-value="Entity">
<join table="entity" >
<key column="entity_id" property-ref="login_cust_id" />
<property name="CompanyName"/>
</join>
</subclass>
</class>
Unfortunately at this time this feature is supported in Hibernate but not in NHibernate. Please see here and here for the outstanding tickets. Some work has gone towards adding this feature which can be seen on this fork on github.
Solution 2: Discriminator based mapping with Many-to-One
Another option is to still use the discriminator based mapping, but use a many-to-one mapping within each of the sub-classes, which would allow you to join on the foreign key using a property-ref. This has the disadvantage of requiring separate classes for all of the properties in your customer and entity tables but is a workable solution.
<class name="IUser" abstract="true" table="login">
<id name="Id" column="login_sk">
<generator class="identity"/>
</id>
<discriminator column="login_holder_type" not-null="true" type="System.String"/>
<subclass name="CustomerUser" discriminator-value="Customer">
<many-to-one name="CustomerProps" property-ref="login_cust_id" />
</subclass>
<subclass name="EntityUser" discriminator-value="entity">
<many-to-one name="EntityProps" property-ref="login_cust_id" />
</subclass>
</class>
<class name="CustomerProps" Table="customer" >
<id name="Id" column="cust_id">
<generator class="assigned"/>
</id>
<property name="DisplayName" column="cust_mail_name"/>
<property name="RecordChangeName" column="cust_lookup_name" />
</class>
<class name="EntityProps" Table="entity" >
<id name="Id" column="entity_id">
<generator class="assigned"/>
</id>
<property name="CompanyName"/>
</class>
Solution 3: Discriminator based mapping with Joins to Updatable Views
The final option is to create an Updatable View in the DB for the customer and entity tables which contains the login_sk field. You can then use Join within each sub-class as you wouldn't require the property-ref.
<class name="IUser" abstract="true" table="login">
<id name="Id" column="login_sk">
<generator class="identity"/>
</id>
<discriminator column="login_holder_type" not-null="true" type="System.String"/>
<subclass name="CustomerUser" discriminator-value="Customer">
<join table="customerView" >
<key column="login_sk" />
<property name="DisplayName" column="cust_mail_name"/>
<property name="RecordChangeName" column="cust_lookup_name" />
</join>
</subclass>
<subclass name="EntityUser" discriminator-value="Entity">
<join table="entityView" >
<key column="login_sk" />
<property name="CompanyName"/>
</join>
</subclass>
</class>
I'm wondering how to best implement a property (here, LatestRequest) which is read-only, backed by a query.
Here, I have an Export, which can be requested to happen multiple times. I'd like to have a property on the Export to get the latest ExportRequest. At the moment, I've got a many-to-one mapping with a formula, like this:
<class name="Export" table="Exports">
<id name="Id">
<generator class="guid" />
</id>
<property name="Name" />
<bag name="Requests" cascade="all,delete-orphan" inverse="true" access="field.camelcase" order-by="CreationDate desc">
<key column="ExportId"/>
<one-to-many class="ExportRequest"/>
</bag>
<many-to-one name="LatestRequest"
class="ExportRequest"
formula="(select top 1 ExportRequests.Id from ExportRequests order by ExportRequests.CreationDate desc)"/>
</class>
<class name="ExportRequest" table="ExportRequests">
<id name="Id">
<generator class="native" />
</id>
<many-to-one name="Export"
class="Export"
column="ExportId"
not-null="true" />
<property name="CreationDate" />
<property name="Tag" />
</class>
However, since the ExportRequests.Id for LatestRequest is fetched when the Export is fetched, this access pattern returns stale data:
var export = session.Get<Export>(exportId);
export.AddRequest(new ExportRequest(DateTime.Now, "Foo"));
Assert.AreEqual("Foo", export.LatestRequest.Tag); // fails due to stale data
session.Update(export);
So, what's the best way to implement the LatestRequest property?
I could change the getter for LatestRequest so that it executes a Find query, which should be up to date, but I'm not sure how to make the Export aware of the session.
There are several ways to solve this. I would probably not try to make it "automatic" this way. To enhance performance and simplicity, I would just make it a normal reference and manage it in the business logic or the entity like this:
class Export
{
private IList<ExportRequest> requests;
ExportRequest LatestRequest { get; private set; }
public void AddRequest (ExportRequest request)
{
requests.Add(request);
LatestRequest = request;
}
}
So you don't need any special mappings nor any additional queries to get the latest request. And, most importantly, the entity is consistent in memory and does not depend on the persistency.
However you solve it, it is very important that the entity and the business logic is managing the data (in memory). It should be "persistence ignorant". The database and the data access layer are not responsible to manage relations and logic of the entities.
I've been wrecking my mind on how to get my tagging of entities to
work. I'll get right into some database structuring:
tblTag
TagId - int32 - PK
Name
tblTagEntity
TagId - PK
EntityId - PK
EntityType - string - PK
tblImage
ImageId - int32 - PK
tblBlog
BlogId - int32 - PK
class Image
Id
EntityType { get { return "MyNamespace.Entities.Image"; }
IList<Tag> Tags;
class Blog
Id
EntityType { get { return "MyNamespace.Entities.Blog"; }
IList<Tag> Tags;
The obvious problem I have here is that EntityType is an identifer but
doesn't exist in the database. If anyone could help with the this
mapping I'd be very grateful.
You don't need the entity type. Take a look at any-type mapping (it stores the type name in the database in the relation table, but you don't need it in the entity model).
See this blog post by ayende.
Edit: tried to write an example.
You could have an own table for each tagged object, this is easy and straight forward, you don't even need any types:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="Image_Tags">
<key column="Image_FK"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
Tried to use some advanced features to map it into a single table, but I think it doesn't work this way:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
<bag name="Objects" table="tblTagEntity" access="noop">
<key column="TagId"/>
<many-to-any id-type="System.Int64" meta-type="System.String">
<meta-value
value="IMAGE"
class="Image"/>
<meta-value
value="BLOG"
class="Blog"/>
<column name="EntityType"/>
<column name="EntityId"/>
</many-to-any>
</bag>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="tblTagEntity" where="EntityType='IMAGE'">
<key column="EntityId"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
The tricks here are:
access="noop" to specify the foreign key without having a property in the entity model, see this post.
where="EntityType='IMAGE'" to filter the loaded data.
The problem is that most probably the EntityType is not set to any useful value. This could be fixed somewhere, but I don't think that it is worth the effort.
Someone else has probably a better idea.
Edit 2: another (working) solution
make the association table an entity:
in short:
Tag => TagEntity: not mapped or one-to-many inverse (noop)
TagEntity => Tag: many-to-one
TagEntity => Object: any
Object => TagEntity: one-to-many inverse
This should work straight forward.
classes:
class Tag
{
string Name { get; set; }
}
class TagEntity
{
Tag Tag { get; set; }
object Entity { get; set; }
}
class Image
{
IList<TagEntity> tags { get; private set; }
}
The only drawback seems to be that you have to make sure that the bidirectional associations are consistent without loading to much data. Note that inverse collections are not stored.
Edit 2: Performance notes
When you add / remove tags, you could do a trick. TagEntity has a reference to the tagged entity. The Entity also has a list of TagEntities, but this is marked as inverse. (This means, they are loaded, but not stored.)
You can add and remove tags without loading the Entity an without loading all the tags.
Adding:
Get Tag to add (or load proxy if you have the id of the tag)
Load Entity (just proxy, using session.Load, no db access here)
create new TagEntity, assign tag and entity-proxy
save TagEntity
Removing:
Get TagEntity to remove
delete TagEntity.
Within the session, you don't have this tag assigned to/removed from the TagEntity. This works fine assumed that you only add or remove tags within this transaction.
I you define a list of TagEntities on the Tag, you can do the same, without loading all the TagEntities just to add or remove one.
You could make EntityType an Enum in your code. And/or, you could try making EntityType an actual entity in your database (tblEntityType).
Got Stefans final solution to work! Here's my final mappings:
Image
<bag name="TagEntites" table="tblTagEntity" cascade="all" fetch="join" inverse="true" where="EntityType='EntityImage'">
<key column="EntityId"></key>
<one-to-many class="TagEntity" />
</bag>
TagEntity
<id name="Id">
<column name="TagEntityId"></column>
<generator class="identity" />
</id>
<any name="Entity" id-type="System.Int32" meta-type="System.String">
<meta-value value="EntityImage" class="Image" />
<column name="EntityType"></column>
<column name="EntityId"></column>
</any>
<many-to-one name="Tag" class="Tag" cascade="all" fetch="join">
<column name="TagId"></column>
</many-to-one>