Essentially the title of this question explains the essense of what I am trying to do, but to create a contrived example...
I have a class, call it Employee. Employee has an IPaymentBehaviour...
public class Employee
{
IPaymentBehaviour _paymentBehaviour;
protected internal Employee() { /* required by NH */}
public Employee(IPaymentBehaviour paymentBehaviour)
{
_paymentBehaviour = paymentBehaviour;
}
}
This corresponds to a database table like so:
dbo.Employees
-> EmployeeId (primary key)
-> PaymentBehaviourId (foreign key to lookup table)
-> Field1
-> Field2
-> Field3
-> Field4
Depending on the value of PaymentBehaviourId I need to 'inject' a different implementation of IPaymentBehaviour into the Employee object. Depending on which PaymentBehaviour was in use, Field1, 2, 3 or 4 might be needed to create that behaviour.
Can anyone tell me how this would be mapped using Fluent-NHibernate?
table Employees
EmployeeId (primary key)
PaymentBehaviourId (foreign key to PaymentBehaviour)
table PaymentBehaviour
PaymentBehaviourId (pk)
type (discriminator)
Field1
Field2
Field3
Field4
Classes
public class Employee
{
/* ... */
public PaymentBehaviour PaymentBehaviour { get; set; }
}
public class PaymentBehaviourA : IPaymentBehaviour
{
/* ... */
public int Field1 { get; set; }
}
public class PaymentBehaviourB : IPaymentBehaviour
{
/* ... */
public int Field2 { get; set; }
}
I don't know FluentNHibernate enough to tell you how it looks like, but in XML you would specify it like this:
<class name="Employee" table="Employees">
<many-to-one name="PaymentBehaviour" class="IPaymentBehaviour">
</class>
<class name="IPaymentBehaviour" abstract="true" >
<discriminator column="type"/>
<subclass name="PaymentBehaviourA" discriminator-value="A">
<propert name="Field1"/>
</subclass>
<subclass name="PaymentBehaviourB" discriminator-value="B">
<propert name="Field2"/>
</subclass>
</class>
NHibernate uses the default constructor to instantiate entities. There are some workarounds though.
I think I would work around it like this:
Create a default constructur on the
Employee object , that has a
private access modifier, so that NHibernate can reconstruct those types
Create an Interceptor in where you overload the appropriate methods
(OnLoad I presume), in which you make
sure that you inject the correct
IPaymentBehaviour implementation
into the entity. (Perhaps you can
create an interface
'IPaymentBehaviourInjectable' that
can be implemented by the Employee
class (implement it explicitily), so
that you can inject the correct
behaviour into the entity ...
Related
I have a model like this:
public class Order
{
public virtual int OrderType { get; set; }
}
(lots of other properties omitted of course) which maps directly to an int type in the DB.
The thing is, the numeric order type is meaningless to my application. There are single-letter codes that the user sees which denote the order type. So, I could do something like this:
public class Order
{
public virtual int OrderTypeIgnored { get; set; }
public virtual char OrderType
{
get
{
return translateForward(OrderTypeIgnored);
}
set(char val)
{
OrderTypeIgnored = translateBackward(val);
}
}
}
(lots of air code/pseudocode there, I'm relatively new to C#) and just map the OrderTypeIgnored property. But is there a cleaner way to do this? Perhaps somehow overriding the getter and setter on the mapped property itself?
A few notes: The values are static enough that embedding the translation in the code is not a problem. No, there's no LOV table, and no, I don't have control over the database structure.
Sorry if there are answers for this, but searching for things like "mapping" and "translation" don't really get me the results I'm looking for, obviously.
You could create a public char property that uses a private int field and only map the field.
Model:
public class Order
{
private int _orderType;
public virtual char OrderType
{
get
{
return TranslateForward(_orderType);
}
set
{
_orderType = TranslateBackward(value);
}
}
}
Mapping:
<property name="_orderType" access="field" />
If you don't want to map the field directly (because you use a compile-safe mapping) you can map the public property using the access strategy "field", a naming strategy like "camelcase-underscore" and explicitly specify the "Int32" type.
you can always use enums for this kind of situation.
You can define it like this:
namespace MyApp.Domain
{
using System.ComponentModel;
public enum OrderType : short
{
[Description("Order Suspended")]
Suspended = 1,
[Description("Order Delivered")]
Delivered = 2,
[Description("Order New")]
Inserted = 3
}
}
and map it this way:
<property name="Type" type="MyApp.Domain.OrderType, MyApp.Domain" >
<column name="Type" not-null="true"/>
</property>
so you can write your QueryOver in a simple way like this:
var orders = this.Session.QueryOver<MyApp.Domain.Orders>()
.Where(x => x.Type == MyApp.Domain.OrderType.Inserted)
.List();
I want to get ClassA.ClassBCollection property filtered and paged. I need to change filtering dynamically.
The default querying will result in something like:
select * from ClassA
left outer join ClassB
on id == FK_ClassB
Can I customize querying of nhibernate set somehow?
Mappings:
<class name="ClassA">
<property name="Name" />
<set name="ClassBCollection">
<key column="FK_ClassB" on-delete="cascade" />
<one-to-many class="ClassB" />
</set>
</class>
<class name="ClassB">
<property name="Something"/>
</class>
If I do understand your question...
Can I customize querying of nhibernate set somehow?
...correctly, the answer is NO.
I mean, if you think about getting the instance of ClassA and doing some paging and filtering over its <set> collection. That would be always done in memory. (What we can do with mapping I appended at the end).
we can change the approach
In this case, when you need a filter and paging over the collection items, I would strongly recommend to go the other way. Create Criteria (QueryOver, HQL) not over the ClassA but over the ClassB.
First of all we have to extend ClassB mapping:
<class name="ClassB">
<property name="Something" />
<many-to-one name="ClassA" column="FK_ClassB" fetch="join" />
</class>
And then create a Criteria like this
var criteria = NHSession.Current.CreateCriteria<ClassB>();
criteria
.Add(new InExpression("ClassA", new object[] {1})) // id of one or more ClassA
.AddOrder(new Order("Something", true)) // Order By
.SetFirstResult(2) // Skip
.SetMaxResults(10); // Take
var list = criteria.List<ClassB>();
Because we used mapping of ClassA fetch="join" the resulting SQL statement will be very similar to the first snippet in this question.
So this way, we can achieve the desired SQL Select, but we cannot use ClassA.ClassBCollection directly. We did it this way...
NOTE:
Filters / paging which we can influence on the <set> mapping are static filter in the where clause and style of fetching values.
Where clause will always be evaluated when loading ClassBCollection as a property of the ClassA. It could be like where="IsActive=true"
In case that ClassA can have a lot of items in ClassBCollection, we can manage how they will be loaed. Very effective way is attribute batch-size documented here
Can I customize querying of nhibernate set somehow?
I'm not entirely sure what this means. If you meant, can I query and use WHERE clauses over the collection, the answer is yes. Here's how:
[TestFixture]
public class StackOverflowQuestion13496270Tests
{
public ISession session;
[SetUp]
public void SetUp()
{
session = // Get the current NHibernate session
}
[Test]
public void Query_ClassA()
{
var results = session.Query<ClassA>()
.Where( x => x.ClassBCollection.Any( y => y.Name == "Bob" ) )
.Fetch( x => x.ClassBCollection )
.Skip( 0 )
.Take( 50 )
.ToList();
}
[Test]
public void Query_ClassB()
{
var results = session.Query<ClassB>()
.Where( x => x.Name == "Bob" )
.Fetch( x => x.ClassAParent )
.Skip( 0 )
.Take( 50 )
.ToList();
}
public class ClassA
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<ClassB> ClassBCollection { get; set; }
}
public class ClassB
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
// Add this and the appropriate mapping modifications to be able to navigate back to the parent
public virtual ClassA ClassAParent { get; set; }
}
}
I am trying to understand the NHibernate SQL output for this entity:
public class Person
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
}
When I write:
var maxNumber = s.Query<Person>().Max(p => p.Number);
The generated SQL looks as follows:
select cast(max(person0_.Number) as BIGINT) as col_0_0_ from Person person0_
Why is the cast needed if the Number column is bigint anyway?
I am using NHibernate 3.2, SQL Server 2008 R2, mapping defined as follows:
<class name="NhMappingTest.Person, NhMappingTest">
<id name="Id"><generator class="increment" /></id>
<property name="Number" />
</class>
It's not needed. It's an implementation detail:
protected HqlTreeNode VisitNhMax(NhMaxExpression expression)
{
return _hqlTreeBuilder.Cast(
_hqlTreeBuilder.Max(
VisitExpression(expression.Expression).AsExpression()),
expression.Type);
}
So, the code is blindingly casting the result.
Is it possible to have a collection of entities that are mapped via an IUserType? For example:
class Foo
{
public int Id { get; set; }
public ISet<Special> Items { get; set; }
}
class Special
{
public Special(string columnValue)
{
_val = columnValue;
}
public override string ToString()
{
return _val.TranformInInterestingWays();
}
private string _val;
}
class SpecialUserTypeForMapping : IUserType
{
// assume that all the right stuff is here
// for using the non-default constructor on the way in
// and calling ToString() on the way out
}
If I'm reading the documentation correctly, neither the <set>, <one-to-many>, <many-to-many>, nor <class> element supports the "type" attribute as is used to apply IUserType mapping to <property>s. So how would I go about mapping this?
The most expedient solution seems to be to use the <element> element, like so:
<class name="Foo" table="foos">
<id name="Id" />
<set name="Items" table="foo_special">
<key column="fooId" />
<element column="special_value" type="SpecialUserTypeForMapping" />
</set>
</class>
Retrieving different Foo instances from the DB is no problem, but it's not clear whether it's possible to write queries against the special_value column, which is a requirement in my scenario. This question seems to indicate that it's not.
I have two classes:
public class Parent
{
public virtual long? ID { get; set; } // native
public virtual IList<Child> Children { get; set; }
public virtual string Name { get; set; }
}
public class Child
{
public virtual long ID { get; set; } // assigned
public virtual string Name { get; set; }
}
Instantiating and saving parent and child:
child = new Child() { ID = 1, Name = "SomeName" };
parent = new Parent() { Children = new List() { child } };
session.Save(parent);
Which gives me:
NHibernate.StaleStateException: Unexpected row count: 0; expected: 1.
I think the problem is with the assigned id on the child. Since it has an id, NHibernate thinks it has previously saved before which is not the case.
The generated (trimmed & renamed) SQL is:
NHibernate: select child0_.ID as child1_1_, child0_.NAME as NAME1_, child0_.PARENT_ID as COMMAND7_1_, from CHILD child0_
NHibernate: select parent0_.PARENT_ID as parent1_10_
NHibernate: select parent0_.PARENT_ID as parent1_10_, parent0_.NAME as parent2_10_ from PARENT parent0_
NHibernate: UPDATE CHILD SET PARENT_ID = #p0 WHERE CHILD_ID = #p1;#p0 = 2, #p1 = 1
Mapping files:
<class name="MyNamespace.Child" table="CHILD">
<id name="ID" column="CHILD_ID" type="System.Int64">
<generator class="assigned"></generator>
</id>
<property name="Name" column="NAME"></property>
</class>
<class name="MyNamespace.Parent" table="PARENT">
<id name="ID" column="PARENT_ID" type="System.Int64">
<generator class="native"></generator>
</id>
<property name="Name" column="NAME"></property>
<bag name="Children">
<key column="PARENT_ID"></key>
<one-to-many class="MyNamespace.Child"></one-to-many>
</bag>
</class>
While searching google, I found about version tag which may be a solution but I do not have a persistent field to use as version. In this case, how can I save (insert) a child with assigned id and its parent?
When cascading from a parent to a child, NHibernate uses the SaveOrUpdate method. You are correct that NHibernate need some way to determine whether it should perform an insert or an update. It will look at three different fields for an unsaved value to determine if the entity is new.
Id
Version
Timestamp
With an assigned Id, you will need either a Version or Timestamp field in order to indicate that the entity is new.
An alternative would be to call Save() on the children explicitly.
I'm not 100% sure if this is the same problem you are having, but my database is 100% assigned id (ugh) and I had to create an Interceptor that kept track of whether a child is or isn't persisted for cascades to work.
The code is cut/paste (which is why it has dumb names... I didn't understand 100% at first!), and I initially got 90% of it from online documentation (which I can't find via google right now ... sorry):
Base class you put on object that has an assigned ID you want to cascade:
public class Persistent
{
private bool _saved = false;
public virtual void OnSave()
{
_saved = true;
}
public virtual void OnLoad()
{
_saved = true;
}
public virtual bool IsSaved
{
get { return _saved; }
}
}
The interceptor you add to session:
public class TrackingNumberInterceptor : EmptyInterceptor
{
public override bool? IsTransient(object entity)
{
if (entity is Persistent)
{
return !((Persistent)entity).IsSaved;
}
else
{
return null;
}
}
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
if (entity is Persistent) ((Persistent)entity).OnLoad();
return false;
}
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
if (entity is Persistent) ((Persistent)entity).OnSave();
return false;
}
}
Basically the idea is that since NHibernate doesn't know if an assigned id entity is persisted or not, you keep track for it.
By default the object starts with persisted (_saved) at false. When the entity is either loaded or saved by NHibernate, the trigger sets the objects persisted (_saved) flag to true.
So for a fresh item that isn't persisted, it starts at false and stays false because NHibernate has never saved or loaded it. When NHibernate checks whether the child is transient, the trigger responds that it is transient, and a save happens which marks the child as persisted. Also now any future use will require a load which again marks it as persisted.
Calling session.SaveOrUpdate( childObject ) should solve the problem.