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.
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'm facing a apparently very strange problem (I must be doing something wrong, just can't find my errors!). When certain POCOs are saved into the database, nothing happens. When the same POCOs have some property changed, I get a InvalidCastException during session flush, and the rows are never updated. Here's the details:
I have the following class declared:
namespace Data
{
public class Picture
{
public virtual int picid { get; set; }
public virtual int width { get; set; }
public virtual int height { get; set; }
public virtual string path { get; set; }
public virtual string thumbnail { get; set; }
public virtual int userid { get; set; }
public virtual int? placeid { get; set; }
public virtual int? eventid { get; set; }
public virtual DateTime? approved { get; set; }
public virtual DateTime date { get; set; }
public virtual bool finished { get; set; }
public virtual User User { get; set; }
public virtual Place Place { get; set; }
public virtual Event Event { get; set; }
public virtual ISet<PictureVote> Votes { get; set; }
}
}
and the following mapping for it:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Data" namespace="Data">
<class name="Picture" table="pictures">
<id name="picid">
<generator class="sequence">
<param name="sequence">pictures_picid_seq</param>
</generator>
</id>
<property name="path" />
<property name="width" />
<property name="height" />
<property name="thumbnail" />
<property name="userid" />
<property name="placeid" not-null="false" />
<property name="eventid" not-null="false" />
<property name="approved" />
<property name="date" />
<property name="finished" />
<many-to-one name="User" column="userid" class="Data.User,Data" insert="false" />
<many-to-one name="Place" column="placeid" class="Data.Place,Data" insert="false" />
<many-to-one name="Event" column="eventid" class="Data.Event,Data" insert="false" />
<set name="Votes">
<key column="picid" />
<one-to-many class="PictureVote" />
</set>
</class>
</hibernate-mapping>
I checked the table definition and all the types seem to be according to the defined class (postgresql):
Table "public.pictures"
Column | Type | Modifiers
-----------+-----------------------------+----------------------------------------------------------
picid | integer | not null default nextval('pictures_picid_seq'::regclass)
path | character varying(250) | not null
thumbnail | character varying(250) | not null
userid | integer | not null
placeid | integer |
date | timestamp without time zone | not null
finished | boolean | not null default false
width | integer | not null
height | integer | not null
eventid | integer |
approved | timestamp without time zone |
When inside the code, the following works just fine and inserts a row in the pictures table:
...
...
var plpic = new Picture
{
date = DateTime.Now,
width = img.Width,
height = img.Height,
path = pic_server_path,
thumbnail = thumb_server_path,
userid = CustomUserManagement.User.userid,
finished = false,
approved = null,
placeid = placeid
};
session.Save(plpic);
session.Flush ();
...
...
which works ok every time (and yes, I'm going to wrap it in a transaction soon enough).
However, later on, the following NEVER works (this is the code inside a MVC action):
...
...
Picture pic = session.Get<Picture>(picid);
// While debugging, I verified that the "pic" object above is retrieved just fine.
if (pic.userid != CustomUserManagement.User.userid || ModelState.IsValid == false)
return Json (new { status = "error" });
using (ITransaction tx = session.BeginTransaction()) {
try
{
pic.finished = true;
tx.Commit();
}
catch (Exception e) {
tx.Rollback();
NHibernateHelper.DestroySession();
return Json (new { status = "error" });
}
}
...
...
But this always throws a System.InvalidCastException: Cannot cast from source type to destination type at NpgsqlTypes.NpgsqlTypesHelper+c_Iterator11.<>m_C (System.Object timestamp) [0x00000] in /Users/fxjr/Desenvolvimento/ProjetosOpenSource/Npgsql/NpgsqlSourceRelease/Npgsql2/src/NpgsqlTypes/NpgsqlTypesHelper.cs:608
What am I doing wrong? I'm using .NET 4 in Mono 2.10.5, even though the same happens on Windows, NHibernate 3.2 and Npgsql 2.0.11.91. I'm also using postgresql 9.1.1, but I have set ansi_conforming_strings to OFF to make sure my Nhibernate dialect still works. For more information, the same thing happens when updating other types of objects.
I have already posted this in the nhusers list and received a very good suggestion on trying a different db to check if it its Npgsql's fault. However, I don't have the time for that right now and since I'm beginning to think it is my fault and not someone else's, I thought I'd post this here before recreating my db schema in another database and trying this code in it. I'm getting a little desperate as time is kind of running out on me.. can anyone save me on this one?
Thanks in advance.
After a long time, I thought I should come back here to answer my own question.
This is not necessarily a bug in either NHibernate or Npgsql.
I was making a huge mistake in the mapping files, by mapping the same column twice. What this means is that having a int? eventid and a Event Event in my mapped class both map to same column will create you problems, since NHibernate can't deal with this, i.e. you have to pick one of them. What happens is that NHibernate will generate queries with more parameters than the actual number of columns in your table. In MS SQL Server, it is easier to understand the thrown exception:
Invalid index N for this SqlParameterCollection with Count=N
Another alternative is mapping both but setting insert="false" and update="false" in one of them, although this is hardly useful.
I think a lot of people like me (coming from LINQ-TO-SQL) will try the same feat and have a lot of trouble later on. Sadly, I haven't found anything that emphasizes this in NHibernate's documentation.
Hope it helps someone.
I have an entity where a composite id is used. I changed to code to make use of wrapping the composite id in a seperate key class. I expected that with Linq I could do a comparison on key object and with the Criteria API to use Restrictions.IdEq but both fail. I need to explicitly compare the key values to make it work.
I cannot find any documentation if this should work so for the moment I am stuck with direct comparisons but this means that when I alter the key that I also need to update the query code which is obviously not what I would want.
As a side note, I tried this with NHibernate 3.0.0 Alpha 2 and 3.
Domain
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Cwc.Pulse.Dal"
namespace="Cwc.Pulse.Dal">
<class name="AddonStatus">
<composite-id name="Id">
<key-many-to-one name="Context" column="Context_Id" class="Context" />
<key-property name="AddonType" column="Addon_Id"/>
</composite-id>
<property name="Status" />
</class>
</hibernate-mapping>
Class
public class AddonStatus
{
public virtual string Status { get; set; }
public virtual Key Id { get; protected set; }
public AddonStatus()
{
Id = new Key();
}
public class Key
{
public virtual Context Context { get; set; }
public virtual AddonType AddonType { get; set; }
public override int GetHashCode()
{
return ContextId.GetHashCode() ^ AddonType.GetHashCode();
}
public override bool Equals(object obj)
{
if (this == obj) return true;
var o = obj as Key;
if (null == o) return false;
return Context == o.Context && AddonType == o.AddonType;
}
}
}
Working queries
The queries below work and as you can see I compare the key values explicitly. I do not compare the key object.
Linq
from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))
Expected to work but dont
I expect the following queries to work. Either in efficiently for linq in memory instead of the database but I expect the criteria api to be smart enough to handle such composite id´s in queries.
Both linq and criteria api queries make use of a Key object comparison.
var key = new AddonStatus.Key
{
Context = context,
AddonType = addonType
};
Linq
from status
in session.Query<AddonStatus>()
where status.Id == key
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))
So if anyone has such a scenario working then what am I doing wrong?
Not directly an answer to your question, but it may be useful to you anyway. You could avoid the (explicit) composite key by mapping the AddonStatus as composite-element on the owner (most probably the Context):
<class name="Context">
<map name="AddonStates" table="AddonStatus">
<key column="Context_Id" /> <!-- Foreign key to the Context -->
<index column="Addon_Id" /> <!-- Dictionary key -->
<composite-element>
<property name="Status" /> <!-- data -->
</composite-element>
</map>
</class>
In the class Context is looks like this:
class Context
{
IDictionary<AddonType, AddonStatus> AddonStates { get; private set; }
}
This results and pretty the same database structure, but it is different to work with. I can't say if this is what you actually want, but it just looks like it.
Interestingly, I'm getting almost the exact opposite of this behavior in 2.1.2.
My mapping (simplified):
<!-- Subscriber class -->
<class name="Subscriber" >
<composite-id name="SubscriberKey" class="SubscriberKey">
<key-property name="Request" column="RequestID" type="int"/>
<key-many-to-one name="User" column="UserID" class="User" not-found="ignore" />
</composite-id>
<!-- User class - note that this goes to a different schema,
and is not mutable. Who knows if that's important... -->
<class name="User" schema="AnotherDb.dbo" mutable="false">
<id name="Id" column="UserID" type="int">
<generator class="native" />
</id>
<property name="FirstName" column="FirstName" type="string" />
<property name="LastName" column="LastName" type="string" />
goes to:
public class User
{
public virtual int? Id {get; protected set;}
public virtual string FirstName { get; protected set; }
public virtual string LastName { get; protected set; }
public User() { }
}
public class Subscriber
{
public virtual SubscriberKey SubscriberKey { get; set; }
public virtual User User { get; set; }
public Subscriber() { }
}
public class SubscriberKey
{
public override bool Equals(object obj)
{
if (obj is SubscriberKey && obj != null)
return ((SubscriberKey)obj).Request == Request
&& ((SubscriberKey)obj).User.Id == User.Id;
return false;
}
public override int GetHashCode()
{
return (Request.ToString() + User.Id.ToString()).GetHashCode();
}
public virtual int Request { get; set; }
public virtual User User { get; set; }
public SubscriberKey() { }
}
Things which work:
CreateCriteria<Subscriber>()
.Add(Restrictions.IdEq(keyInstance))
.UniqueResult<Subscriber>();
CreateCriteria<Subscriber>()
.Add(Restrictions.Eq("SubscriberKey.User.Id", aUserID))
.Add(Restrictions.Eq("SubscriberKey.Request", aRequestID))
.UniqueResult<Subscriber>();
Things which don't work:
Get<Subscriber>(keyInstance);
I'm thinking this is an inconsistency between their various ID-equaling query forms. When I get time, I'll be building a minimal unit test to submit as a bug example. I'd be interested in any / all thoughts anyone might have on this...
edit: Heeey, I figured it out!
Things which do work, now that I've read this
Get<Subscriber>(new SubscriberKey() {
User = Load<User>(aUserID), // the important part!
Request = aRequestID
});
This will create a proxy object for the User key, without hitting the database (unless necessary). If you swap Load<User> for Get<User>, you'll immediately hit the database to populate the object, rather than respecting your lazy-loading properties. Use Load.
And things like this are precisely why people suggest the (type)Repository pattern - I can do this behind the scenes: Get<>(new SK(){User=Load<>(key.User.Id)}, and still Get(key) by a single key, identical to every other object.
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 ...