FluentNhibernate - query with ReferencesAny - nhibernate

When using ReferencesAny<> there is .EntityTypeColumn("MyType") which is not visible to application.
Is there option to query by that column using HQL or something and how to integrate into my LINQ to HQL queries?

As always, NHibernate has solution for (almost) everything.
Let's assume mapping like this (ala documentation):
ReferencesAny(x => x.AnyEntity) // the property name is AnyEntity... used below
// some explicit mapping
.AddMetaValue<Household>(typeof(Employee).Name)
.AddMetaValue<Client>(typeof(Contact).Name)
// the essence of <any>
.EntityTypeColumn("MyType")
.EntityIdentifierColumn("TheId")
.IdentityType<int>();
A cite from doc: 14.7. The where clause:
Likewise, the special property class accesses the discriminator value of an instance in the case of polymorphic persistence. A .Net class name embedded in the where clause will be translated to its discriminator value.
So, now we've informed NHibernate, that the colunn MyType contains the type, the equiv of the C# type. Because we can access EntityTypeColumn via the .class - this query will give us what we want:
// the 'Contact' represents the value contained in DB...
var hql = "from MyEntity where AnyEntity.class = 'Contact' ";
...
session.CreateQuery(hql)
.List<MyEntity>();
this will generate the WHERE ... MyType = 'Contact'
The same in the QueryOver:
var query = session.QueryOver<MyEntity>()
.Where(x => x.AnyEntity is Contact)
.List<MyEntity>();
NOTE: the bad news, that with built in LINQ provider, similar query is failing. I would guess it is a bug. I.e. this is not working session.Query<MyEntity>().Where(x => x.AnyEntity is Contact), generating the wrong WHERE clause
EXTENDED as asked in the comment
We can also filter by the <any> elements ID, the 'EntityIdentifierColumn'
The syntax would look like this (see the magical ".id" selector")
query
...
.Where(Restrictions.In("AnyEntity.id", new[] {1, 2, 3, 4, 5, 6, 7, 8}))
...

How about simply mapping the column in the entity you reference. Have a base class for all kind of types for example?

Related

NHibernate QueryOver projection on many-to-one

I am trying to get a QueryOver working using a Projection on a many-to-one.
The class "Post" has a property many-to-one "Creator".
Using
session.QueryOver(Of Post).
Select(Projections.
Property(of Post)(Function(x) x.Creator).
WithAlias(Function() postAlias.Creator)).
TransformUsing(Transformers.AliasToBean(Of Post)()).
List()
works BUT each creator is retrieved by a single query rather than using a join like it is done when not using a select/projection. So if there are 5 posts with 5 different creators, 6 queries will be run 1 for the list of posts and 5 for the creators.
I tried to get it working using a JoinAlias but nothing really did the job.
I already searched for a solution, but all solutions I found did use the Linq-Provider which does not really fit since the actual "field list" is passed via a parameter.
Does anyone know if there is a solution to this other than the linq provider?
There is a solution, we can use projections for many-to-one and then custom result transformer.
DISCLAIMER: I can read VB syntax but do not have enough courage to write... I expect that you can read C# and convert it into VB....
So we can have projection like this:
// aliases
Post root = null;
Creator creator = null;
// projection list
var columns = Projections.ProjectionList();
// root properties
columns.Add(Projections.Property(() => root.ID).As("ID"));
columns.Add(Projections.Property(() => root.Text).As("Text"));
// reference properties
columns.Add(Projections.Property(() => creator.ID).As("Creator.ID"));
columns.Add(Projections.Property(() => creator.FirstName).As("Creator.FirstName"));
// so our projections now do have proper ALIAS
// alias which is related to domain model
// (because "Creator.FirstName" will be use in reflection)
var query = session.QueryOver<Post>(() => root)
.JoinAlias(() => root.Creator, () => creator)
.Select(columns)
Now we would need smart Transformer, our own custome one (plugability is power of NHibernate). Here you can find one:
public class DeepTransformer
And we can continue like this
var list = query
.TransformUsing(new DeepTransformer<Post>())
.List<Post>()
Check also this:
Fluent NHibernate - ProjectionList - ICriteria is returning null values
NHibernate AliasToBean transformer associations

NHibernate (Fluent mapping) ReferencesAny<object>

Base entity interface is IEntity which requires only "object ID {get;set;}" to be implemented.
Now, in almost every case ID is of Guid type (except for membership etc).
I am using following code to do mapping of interface
...
AnyPart<IEntity> primaryMap = ReferencesAny(x => x.Primary)
.IdentityType<object>() // tried with .IdentityType<Guid>()
.EntityIdentifierColumn("PrimaryID")
.EntityTypeColumn("PrimaryType")
.MetaType<string>();
...
Of course, next I am adding meta values.
So, Now getting error
Source array was not long enough. Check srcIndex and length, and the array's lower bound
And with .IdentityType<Guid>()
could not resolve property: Primary.ID of: Founder.Connection [.SingleOrDefault[Founder.Connection](NHibernate.Linq.NhQueryable`1[Founder.Connection], Quote((x, ) => (OrElse(AndAlso(Equal(x.Primary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211), Equal(x.Secondary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211)), AndAlso(Equal(x.Secondary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211), Equal(x.Primary.ID, 35c2142a-4c17-4b77-96fd-a2570028a211))))), )]
UPDATE:
I tried also with .IdentityType(x=>x.ID) but same problem (Source array was not long enough)
UPDATE II:
Query (Actually whole method containing query) that this error occurs on is bellow:
public IQueryable<Connection> GetConnections(IEntity connectable)
{
IQueryable<Connection> query =
Query().Where(
x => x.Primary.ID == connectable.ID || x.Secondary.ID == connectable.ID);
return query;
}
Try this in the query: x.Primary == connectable (without ID).
The problem is that you reference an object (or another unmapped type, that's why you need an any mapping). There is no ID on object.
By the way, using HQL would allow you to access the id by using the keyword id, which is not available in Linq (technically it could be made available as extension method, but I don't know Linq to NH good enough to say if it had been implemented). Every any reference conceptually has an id and a class.

NHibernate mapping by code: Mapping userTypes by convention

I would like to know how to configure NHibernate "mapping by code" so that when I map a property of a certain type, it uses a certain userType to perform that mapping. It figures this out by convention.
For example, if I have an Account class with a property Currency of type Currency, then the NHibernate configuration should figure out (by convention) that it needs to use the CurrencyUserType to perform the mapping.
I'm unable to find the relevant documentation for this, so if such documentation does exist, then a few links will be appreciated too.
Note: This is not a FluentNHibernate question.
var mapper = new ConventionModelMapper();
mapper.IsProperty((info, b) => b || info.GetPropertyOrFieldType() == typeof(Currency));
mapper.BeforeMapProperty +=
(inspector, member, customizer) =>
{
if (member.LocalMember.GetPropertyOrFieldType() == typeof(Currency))
customizer.Type<CurrencyUserType>();
};

NHibernate - Incorrect thinking? Subclassed Model based on Join

I have a simple model class (Part), which pulls from it's information from a single table (t_Part).
I would like a subclass of this model called (ProducedPart), that would still utilize NHibernate's caching mechanisms, but would only be instances of (Part) that have a foreign key relationship in a table called "t_PartProduction". I do not need to have a model for this second table.
I only need a read-only version of ProducedPart
I could always implement a Facade/Repository over this, but I was hoping to setup a mapping that would pull "t_Part" joined with "PartProduction" when I asked for "ProducedPart" in NH.
Is this the wrong way to use NH?
Edit
So, the SQL would look something like
SELECT p.*
FROM t_Part p
INNER JOIN t_PartProduction pp ON pp.PartID = p.PartID
WHERE pp.ProductionYear = '2009'
I believe what you are looking for is a joined subclass. In FNH, it will look something like:
public class PartMap : ClassMap<Part>
{
public PartMap()
{
Id(x => x.Id)
JoinedSubClass<ProducedPart>("PartID", sub => {
sub.Map(x => x.Name);
sub.Map(x => x.ProductionYear);
});
}
}
In order have NHibernate cache the results, you will need to have the subclass mapped (and if you didn't map it, you wouldn't be able to get NH to load it in the first place).
Bringing in some context from the FNH groups thread, it will not explicitly be read-only though. In my opinion, making things read-only is not an appropriate thing for NHibernate to manage. This is better controlled by the database and connections (i.e. creating a connection to the database that only has SELECT permissions on the tables/views being accessed). See my answer to a previous SO question about readonly sessions in NHibernate for more of my thoughts on the matter.
The key here is using both the where and mutable elements of the class definition for NHibernate Mappings.
Using Fluent NHibernate, this looks like:
public Part()
{
WithTable("t_Part");
Id(i => i.Id).ColumnName("PartID");
Map(m => m.Name).ColumnName("Part");
SetAttribute("where", "PartID IN ( SELECT pp.PartID FROM t_PartProduction pp WHERE pp.ProductionYear = '2009' ) ");
ReadOnly();
}
No, this is perfectly possible. Look in the NHibernate documentation for the "table per subclass" model of inheritance. It will actually implement this as a LEFT JOIN, so that when you load a Part, it creates an instance of either your Part or your ProducedPart class depending on whether the other row is present. You'll find documentation on nhibernate.info.
I'm not sure you could make ProducedPart read-only doing this though.
I'm assuming from this:
WHERE pp.ProductionYear = '2009'
that you want the subclass only where the production year is 2009, i.e. if when there is a record in t_PartProduction for a different year, you want this Part treated as a plain Part object, not a ProducedPart, then you could consider creating a view definition within your database that is a filtered version of t_PartProduction, then making your subclass join to this view rather than the base table.

NHibernate logs and executes a query twice in a row

I am using: NHibernate, NHibernate.Linq and Fluent NHibernate on SQL Server Express 2008. I am selecting an entity using a predicate on a referenced property (many-one mapping). I have fetch=join, unique=true, lazy-load=false. I enabled the log4net log and when any such query executes it logs two identical SQL queries. Running the query returns one row, and when I attempt to use the IQueryable.Single extension method it throws the exception stating there is more than one row returned. I also tried running the query using the standard IQuery.UniqueResult method with the same result, it ends up logging and actually running the query twice, then throwing an exception stating that there were multiple rows, however running the actual query in management studio returns only one result. When I disable logging I receive the same error.
The entities and mappings are declared as follows (proper access modifiers and member type variance are implied)
class User
{
int ID;
string UserName;
}
class Client
{
int ID;
User User;
Person Person;
Address Address;
}
class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.ID);
Map(x => x.UserName);
}
}
class ClientMap : ClassMap<Client>
{
public ClientMap()
{
Id(x => x.ID);
References(x => x.User).Unique();
...
}
}
Then I invoke a queries such as the following:
ISession s = GetNHibernateSession();
...
var client = s.Linq<Client>().SingleOrDefault(x => x.User.ID = 17);
or
var client = s.Linq<Client>().Where(x => x.User.ID = 17);
or
var client = s.CreateQuery("from Client as c where c.User.ID = 17").UniqueResult<Client>();
In all cases executes two identical queries. When I enable lazy load, the client is again loaded using two queries, however upon accessing a member, such as Person, only one additional query is executed.
Is this possibly a result of Fluent generating an improper mapping? Or SQL Server Express edition not being used properly by NHibernate?
The problem was caused by another mapping I had declared. I had a class inheriting from Client which had an associated mapping. This is what caused NHibernate to query twice. I noticed this because when using Linq() it returned the subclass, not Client itself. This particular instance of inheritance and mapping was a design flaw on my part and was the root of the whole problem!
NHibernate doesn't have any trouble with SQL Express, I've used it fairly extensively. Similarily, it's unlikely Fluent NHibernate is generating invalid mappings in this simple scenario (but not unheard of).
A shot in the dark, but I believe NHibernate reserves the name Id as an identifier name, so when it sees Id in the query it knows to just look at the foreign key instead of the actual joined entity. Perhaps your naming of ID instead of Id is throwing it off?
You could try using the excellent NHibernate profiler for a more detailed view of what is happening. It comes with a 30 day trial license and while in Beta there is a discount on the full license cost