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"));
});
Related
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
Consider this class that represents a node in a hierarchical structure:
public class Node
{
public Node()
{
Children = new List<Node>();
}
public virtual int Id { get; set; }
public virtual IList<Node> Children { get; set; }
public virtual Node Parent { get; set; }
public virtual int Position
{
get { return Parent == null ? -1 : Parent.Children.IndexOf(this); }
set { }
}
}
The mapping looks like this (as NHibernate does not support lists in bidirectional associations, I use a bag here and have the children determine their position automatically):
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="AmbiguousColumn" assembly="AmbiguousColumn" xmlns="urn:nhibernate-mapping-2.2">
<class name="Node">
<id name="Id" type="Int32">
<generator class="identity" />
</id>
<bag name="Children" inverse="true" cascade="all" order-by="Position">
<key column="Parent" />
<one-to-many class="Node" />
</bag>
<many-to-one name="Parent" />
<property name="Position" />
</class>
</hibernate-mapping>
To get all nodes with their children loaded I'd use a query like this:
var nodes = session.QueryOver<Node>()
.Fetch(x => x.Children).Eager
.List();
However, executing this results in an exception:
NHibernate.Exceptions.GenericADOException: could not execute query
[...(sql)...] ---> System.Data.SqlClient.SqlException: Ambiguous column name 'Position'.
The SQL:
SELECT
this_.Id as Id0_1_,
this_.Parent as Parent0_1_,
this_.Position as Position0_1_,
children2_.Parent as Parent3_,
children2_.Id as Id3_,
children2_.Id as Id0_0_,
children2_.Parent as Parent0_0_,
children2_.Position as Position0_0_
FROM
Node this_
left outer join
Node children2_
on this_.Id=children2_.Parent
ORDER BY
Position
I understand why this happens: NH joins the same table twice, but uses the order clause without qualifying the column name.
The question is: how can I make this scenario work? Resorting to instead of is probably difficult as I'd like to have a bidirectional relation.
There are a couple of similar question on SO, but nowhere did I find an actual solution.
Update: the error is database/driver specific. Using the Sql Server CE (e.g. SqlServerCeDriver and MsSqlCe40Dialect) I get the proper query. Using Sql Server (e.g. Sql2008ClientDriver and MsSql2012Dialect) produces the unqualified queries.
According to my own tests, this behavior still exists in the master branch on github.
A gist with a test case: https://gist.github.com/anonymous/5377535
I think I found the cause of the problem and viable workarounds:
The cause of the issue is the fact that the column is called "Position", which is a reserved word in ODBC according to http://msdn.microsoft.com/en-us/library/ms189822.aspx
This combined with the fact that the default value for NH's hbm2ddl.keywords property is set to "keywords" somehow caused NH not to qualify the order-by clause, probably because it though "Position" was a keyword, not a column.
http://nhforge.org/blogs/nhibernate/archive/2009/06/24/auto-quote-table-column-names.aspx
Ways to fix it:
1) Use a different name for the property - one that isn't a keyword. In this case, PositionInParent would have worked without any issues.
2) Quote the order by clause properly using back-ticks.
<bag name="Children" inverse="true" cascade="all" order-by="`Position`">
Or whatever it takes in your mapping API of choice, e.g. in mapping by code:
cls.Bag(x => x.Children,
map =>
{
map.Inverse(true);
map.Cascade(Cascade.All);
map.Key(key => key.Column("Parent"));
map.OrderBy("`Position`"); // note that you must not use a lambda expression in this case
},
map => map.OneToMany());
3) Disable keyword auto import, ie. set hbm2ddl.keywords to none (neither keywords nor auto-quote will work):
<property name="hbm2ddl.keywords">none</property>
Or programmatically:
config.DataBaseIntegration(db => db.KeywordsAutoImport = Hbm2DDLKeyWords.None);
You can still auto-quote reserved words by calling SchemaMetadataUpdater.QuoteTableAndColumns just before building the session factory.
SchemaMetadataUpdater.QuoteTableAndColumns(config);
I'll stick with approach 3 for now as it is the most painless so far.
This works in v.717 (IComponentConvention) :
public void Apply(IComponentInstance instance)
{
if (instance.Type == typeof(EmailAddress))
{
instance.Properties.First(property => property.Name == "FullAddress")).Column(instance.Name);
}
}
Using the above convention I can map the name of my database column to the name of the component property. But in v.727 the hbm suddenly looks like this:
<component name="DefaultMailAddressForAlerts" class="EmailAddress">
<property name="FullAddress" type="System.String">
<column name="DefaultMailAddressForAlertsDefaultMailAddressForAlerts"/>
</property>
</component>
Making the column name in the database the name of the component property x 2. Is there a new way of creating component conventions in v727 of fluent nhibernate?
This looks like a bug. I've raised an issue in our bugtracker, see Fluent NHibernate #161.
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);
}
}
Let's say you have two tables, "Users" and "UserRoles". Here's how the two tables are structured (table - columns):
Users - UserID (int)
UserRoles - UserID (int), Role (string)
What I want is for my "User" class in my domain to have an IList of roles. How do I construct my Fluent NHibernate mapping to achieve this?
What you're looking for is a of a set of elements, which in standard hbm mapping is:
<set name="Roles" table="UserRoles">
<key column="UserID" />
<element column="Role" />
</set>
For Fluent NHibernate you can map this like so:
HasMany<string>(x => x.Roles)
.AsElement("Role");
You may need to also specify the key name using WithKeyColumn(string).
FWIW this has change minorly of present day. The current mapping is
HasMany<string>(x => x.Roles)
.Element("Role");
I beleive it would be
public User()
{
Id(x => x.UserID);
HasMany<UserRoles>(x => x.UserRoles).AsBag();
}
You'll also have to make sure you map your UserRoles class as well
This also worked:
HasMany<Role>(u => u.Roles)
.WithTableName("UserRoles")
.Component(role => role.Map(r => r.Name))
.AsList();
You don't need to map Role or UserRoles.
Make sure Role implements IEquatable < Role > ;.