subclass of class with compositeid - nhibernate

is the following hbm.xml mapping possible in MappingByCode?
<joined-subclass>
<key>
<column name="keypart1"/>
<column name="keypart2"/>
<column name="keypart3"/>
</key>

i figured it out.
class JoinedSubclassMapping : JoinedSubclassMapping<Subclass>
{
public JoinedSubclassMapping()
{
Key(key =>
{
key.Columns(c => c.Name("keypart1"), c => c.Name("keypart2"), c => c.Name("keypart3"));
});
}
}

Related

Nhibernate QueryOver by Enum Flags

I have a query by QueryOver :
public IList<Person> SearchTest(PersonEnumType type)
{
var q = SessionInstance.QueryOver<Person>();
q = q.Where(x => (x.PersonEnumType & type) == type);
return q.List<Person>();
}
and PersonEnumType is a Enum flags :
[Flags]
public enum PersonEnumType
{
Employee1 = 1,
Employee2 = 2,
Employee3 = 4
}
This throws Could not determine member from (Convert(x.PersonEnumType) & Convert(value(NHibernate.Repository.PersonRepositoryNh+<>c__DisplayClass2).type))
Of course this works in Nhibernate.Linq.
Why?
if you've mapped your property properly in your mapping file:
<property name="PersonEnumType" type="MyApp.PersonEnumType, MyApp">
<column name="Person" default="1" />
</property>
You can achieve what you're looking for using filters.
I don't know if this is the only solution but, here it goes:
You can create a filter definition:
<filter-def name="PersonEnumTypeFilter">
<filter-param name="personType" type="MyApp.PersonEnumType, MyApp"/>
</filter-def>
and implement it in your class mapping:
<filter name="PersonEnumTypeFilter" condition="(:personType & PersonEnumType) = PersonEnumType"/>
Now you can switch on your filter:
public IList<Person> SearchTest(PersonEnumType type)
{
SessionInstance.EnableFilter("PersonEnumTypeFilter").SetParameter("personType", type);
var q = SessionInstance.Query<Person>();
return q.ToList<Person>();
}
You can read more about filters here.

How to map Table-Per-Hierarchy with NHibernate 3.2 ConventionModelMapper

I'm using NHibernate 3.2 mapping by code/convention and am having trouble mapping simple table-per-hierachy inheritance. My base class is LookupBase and there are more than a dozen classes that derive from this base class. I'd like the class model to map to a single table in the database with a discriminator column (The discriminator column will contain the name of the respective concrete class).
The base class, LookupBase is in a different assembly from the concrete classes.
Here are how the concrete classes are implemented:
namespace ROWMobile.Domain
{
public class NotificationMethod : LookupBase
{
}
public class ContactMethod : LookupBase
{
}
public class ContactType : LookupBase
{
}
...
As you can see, there are no additional properties in the concrete classes - they inherit all properties from LookupBase.
private HbmMapping GenerateMappings()
{
ConventionModelMapper relationalMapper = new ConventionModelMapper();
var baseLookupType = typeof(LookupBase);
relationalMapper.IsRootEntity((t, declared) => t.BaseType != null && (t.BaseType == typeof(object)));
relationalMapper.IsTablePerClassHierarchy((t, declared) =>
{
if (t == typeof(LookupBase))
{
return true;
}
return false;
});
var mapping = relationalMapper.CompileMappingFor(GetDomainEntities());
return mapping;
}
private static IEnumerable<Type> GetDomainEntities()
{
Assembly domainAssembly = typeof(Event).Assembly;
IList<Type> baseEntities = new List<Type>();
baseEntities.Add(typeof(LookupBase));
IEnumerable<Type> domainEntities = from t in domainAssembly.GetTypes()
where (IsSubclassOfRawGeneric(typeof(LookupBase), t)
&& !t.IsGenericType)
select t;
IEnumerable<Type> allEntities = domainEntities.Concat(baseEntities);
return allEntities;
}
static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
{
while (toCheck != null && toCheck != typeof(object))
{
var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
if (generic == cur)
{
return true;
}
toCheck = toCheck.BaseType;
}
return false;
}
I call the above code like this:
HbmMapping generatedMappings = GenerateMappings();
System.Diagnostics.Debug.WriteLine(Serialize(generatedMappings));
NhConfiguration.AddDeserializedMapping(generatedMappings, null);
Then, I have a test that creates the schema:
[TestMethod]
public void GenerateSchema()
{
NHibernateConfigurator nhc = new NHibernateConfigurator();
nhc.BuildSessionFactory<MsSql2008Dialect>();
SchemaExport schemaExport = new SchemaExport(nhc.NhConfiguration);
schemaExport.Execute(true, true, false);
}
When I Xml Serialize the HbmMapping produced, it looks like this:
<?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" xmlns="urn:nhibernate-mapping-2.2">
<class name="Marathon.MobileApplication.Client.LookupBase, MobileApplication.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="LookupBases" abstract="true">
<id name="Id" type="Int32" />
<discriminator />
<property name="Value" />
<property name="InternalId" />
</class>
<joined-subclass name="ROWMobile.Domain.NotificationMethod, ROWMobile.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" extends="Marathon.MobileApplication.Client.LookupBase, MobileApplication.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<key column="notificationmethod_key" />
</joined-subclass>
<joined-subclass name="ROWMobile.Domain.ContactMethod, ROWMobile.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" extends="Marathon.MobileApplication.Client.LookupBase, MobileApplication.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<key column="contactmethod_key" />
</joined-subclass>
<joined-subclass name="ROWMobile.Domain.ContactType, ROWMobile.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" extends="Marathon.MobileApplication.Client.LookupBase, MobileApplication.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<key column="contacttype_key" />
</joined-subclass>
....
Which produces a LookupBases table that has the discriminator table, but it also produces a table for each concrete class. Can someone please tell me what I'm doing wrong? Also, does anyone know of any documentation available for the mapping by code/convention feature introduced in NHibernate 3.2?
Have you considered adding the hbm for the parts that just dont seem to work correctly? I know it's a hack but it seems to work for me.

nhibernate queryover join with subquery to get aggregate column

I have been searching for several hours now how to do this, but can't seem to find anything to help me.
Here is the database model:
This is the SQL query I am trying to run:
SELECT b.*, a.Assignments FROM Branch b LEFT JOIN (
SELECT b.BranchID , COUNT(ab.BranchID) AS Assignments
FROM Branch b LEFT JOIN AssignmentBranch ab ON b.BranchID = ab.BranchID
GROUP BY b.BranchID
) a ON b.BranchID = a.BranchID
So, basically, I want to return a list of branches and a new column that represents the number of assignments for that branch.
Branch model
public class Branch : IEntity<int>
{
public virtual int ID
{
get;
set;
}
public virtual string Name { get; set; }
public virtual IList<AssignmentBranch> Assignments { get; set; }
}
AssignmentBranch model
public class AssignmentBranch : IEntity<int>
{
public virtual int ID
{
get;
set;
}
public virtual DateTime AssignedOn { get; set; }
public virtual Branch Branch { get; set; }
}
Here is my NHibernate configuration:
<class name="Branch" table="Branch">
<id name="ID" column="BranchID">
<generator class="identity"></generator>
</id>
<property name="Name"/>
<bag name="Assignments" cascade="none" inverse="true">
<key column="BranchID"/>
<one-to-many class="AssignmentBranch"/>
</bag>
<class name="AssignmentBranch" table="AssignmentBranch">
<id name="ID" column="AssignmentBranchID">
<generator class="identity"></generator>
</id>
<property name="AssignedOn" />
<property name="FromDate" />
<property name="ToDate" />
<many-to-one name="Assignment" column="AssignmentID" />
<many-to-one name="Branch" column="BranchID" />
I have tried this a number of ways, but I can't seem to find a way to join with a sub-query using QueryOver.
I tried like this:
// aliases
Branch branch = null; AssignmentBranch assignment = null;
var subquery = QueryOver.Of<Branch>(() => branch)
.Where(() => branch.Project.ID == projectID)
.JoinQueryOver<AssignmentBranch>(() => branch.Assignments, ()=> assignment,
NHibernate.SqlCommand.JoinType.LeftOuterJoin)
.SelectList(list => list
.SelectGroup(x=>x.ID)
.SelectCount(()=>assignment.ID)
);
var query = session.QueryOver<Branch>(()=>branch)
.JoinAlias(???) // how can I join with a sub-query?
.TransformUsing(Transformers.AliasToBean<BranchAssignments>())
.List<BranchAssignments>();
Can anyone help me please? It doesn't have to be with a sub-join exactly, maybe there is another better solution out there that I am missing...
Thank you,
Cosmin
After reading hundreds of similar questions in here, I have found the answer: a correlated sub-query. Like this:
// aliases
Branch branch = null; AssignmentBranch assignment = null;
var subquery = QueryOver.Of<AssignmentBranch>(() => assignment)
.Where(() => assignment.Branch.ID == branch.ID)
.ToRowCountQuery();
var query = session.QueryOver<Branch>(() => branch)
.Where(() => branch.Project.ID == projectID)
.SelectList
(
list => list
.Select(b => b.ID)
.Select(b => b.Name)
.SelectSubQuery(subquery)
)
.TransformUsing(Transformers.AliasToBean<BranchAssignments>())
.List<BranchAssignments>();
The similar question I got my answer from is this one.
its not that easy with QueryOver, because it is currently not possible to have statements in the FROM clause. One thing that comes to my mind (not the most efficient way i think)
var branches = session.QueryOver<Branch>().Future();
var assignmentMap = session.QueryOver<BranchAssignment>()
.Select(
Projections.Group<BranchAssignment>(ab => ab.Branch.Id).As("UserId"),
Projections.RowCount())
.Future<object[]>()
.ToDictionary(o => (int)o[0], o => (int)o[1]);
return branches.Select(b => new { Branch = branch, AssignmentCount = assignmentMap[branch.Id] });
with LINQ it would be
var branchesWithAssignementCount = session.Query<Branch>()
.Select(b => new { Branch = b, AssignmentCount = b.Branch.Count })
.ToList();

Cant query sybase with Nhibernate

Have started to use NHibernate on sybase ASE data, problem am facing is when I load entity I get below error
"System.IndexOutOfRangeException : Invalid index 0 for this OdbcParameterCollection with Count=0."
This is how I configure session
properties["connection.provider"] = "NHibernate.Connection.DriverConnectionProvider";
properties["connection.driver_class"] = "NHibernate.Driver.OdbcDriver";
properties["connection.connection_string"] = #"Driver={Adaptive Server Enterprise};server=;port=; db=;uid=;pwd=";
properties["dialect"] = "NHibernate.Dialect.SybaseASE15Dialect";
And object mapping
<class name="MenuGroup" table="MENU_GROUP">
<id name="Id" column="id" type="Int32">
<generator class="identity" />
</id>
<property name="Name" column="name" type="String" length="100" not-null="true" />
<property name="Position" column="position" type="Int32" />
</class>
and If I do
var menuGroup = _session.Get<Menu.MenuGroup>(1);
I get error
NHibernate.Exceptions.GenericADOException : could not load an entity: [DomainModel.Menu.MenuGroup#1][SQL: SELECT menugroup0_.id as id1_0_, menugroup0_.name as name1_0_, menugroup0_.position as position1_0_ FROM MENU_GROUP menugroup0_ WHERE menugroup0_.id=?]
----> System.IndexOutOfRangeException : Invalid index 0 for this OdbcParameterCollection with Count=0.
I solved this problem by creating my own connection driver
using NHibernate.Driver;
namespace Framework.Persistency
{
public sealed class MySybaseSQLAnywhereDriver : SybaseSQLAnywhereDriver
{
public override bool UseNamedPrefixInSql
{
//default is false
get { return true; }
}
public override bool UseNamedPrefixInParameter
{
//default is false
get { return true; }
}
public override string NamedPrefix
{
//default is string.Empty
get { return ":"; }
}
}
}
And use it in the NHibernate config:
configDictionary.Add(Environment.ConnectionDriver, typeof(MySybaseSQLAnywhereDriver).AssemblyQualifiedName);
moving away from odbc helped, changed the config to
properties["connection.provider"] = "NHibernate.Connection.DriverConnectionProvider";
properties["connection.driver_class"] = "NHibernate.Driver.SybaseAseClientDriver";
properties["connection.connection_string"] = #"server=*;port=5000; db=;user id=*;password=;";
properties["dialect"] = "NHibernate.Dialect.SybaseASE15Dialect";

NHibernate, joined subclass hierarchy, PreUpdate event data changes on an entity which is only modified in the PreUpdate event is not persisted

Overview: With NHibernate I am experimenting with a 3 layered hierarchy using joined subclasses. There is a Category, which inherits from AuditableEntity (to add PreUpdate and PreInsert audit trail), which finally inherits from an Entity.
Problem: None of the data changes to the AuditableEntity object, which are carried out exactly as Ayende’s blog post, are being persisted to the database. The AuditableEntity objects properties are successfully updated by the PreUpdate code, but it is as if NHibernate is not seeing the AuditableEntity as dirty as no update sql statement occurs.
Hbm:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Learning"
namespace="Learning.entities">
<class name="Entity" >
<id name="Id" type="guid">
<generator class="guid.comb"></generator>
</id>
<version name="Version"/>
<joined-subclass name="AuditableEntity" >
<key column="AuditableEntity_id"></key>
<property name="CreatedOn" ></property>
<property name="CreatedBy" ></property>
<property name="LastModifiedOn" ></property>
<property name="LastModifiedBy" ></property>
<joined-subclass name="Category">
<key column="AuditableEntity_id"></key>
<property name="Name" />
</joined-subclass>
</joined-subclass>
</class>
</hibernate-mapping>
NHibernate config for listeners:
<event type="pre-insert">
<listener class="Learning.eventlisteners.AuditInsertEventListener, Learning" />
</event>
<event type="pre-update">
<listener class="Learning.eventlisteners.AuditUpdateEventListener, Learning" />
</event>
PreUpdate code:
namespace Learning.eventlisteners
{
public class AuditInsertEventListener : IPreInsertEventListener
{
public bool OnPreInsert(PreInsertEvent #event)
{
var audit = #event.Entity as IAuditable;
if (audit == null)
return false;
var createdOn = DateTime.Now;
var createdBy = loggedOnProfile;
AuditCommon.Set(#event.Persister, #event.State, "CreatedOn", createdOn);
AuditCommon.Set(#event.Persister, #event.State, "CreatedBy", createdBy);
AuditCommon.Set(#event.Persister, #event.State, "LastModifiedOn", createdOn);
AuditCommon.Set(#event.Persister, #event.State, "LastModifiedBy", createdBy);
audit.CreatedOn = createdOn;
audit.CreatedBy = createdBy;
audit.LastModifiedOn = createdOn;
audit.LastModifiedBy = createdBy;
return false;
}
}
public static class AuditCommon
{
internal static void Set(IEntityPersister persister, IList<object> state, string propertyName, object value)
{
var index = Array.IndexOf(persister.PropertyNames, propertyName);
if (index == -1)
return;
state[index] = value;
}
}
public class AuditUpdateEventListener : IPreUpdateEventListener
{
public bool OnPreUpdate(PreUpdateEvent #event)
{
var audit = #event.Entity as IAuditable;
if (audit == null)
return false;
var lastModifiedOn = DateTime.Now.AddSeconds(28);
var lastModifiedBy = loggedOnProfile;
AuditCommon.Set(#event.Persister, #event.State, "LastModifiedOn", lastModifiedOn);
AuditCommon.Set(#event.Persister, #event.State, "LastModifiedBy", lastModifiedBy);
audit.LastModifiedOn = lastModifiedOn;
audit.LastModifiedBy = lastModifiedBy;
return false;
}
}
}
Code:
using (var session = SessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var category = session.Query<Category>().First();
category.Name = "Updated";
session.SaveOrUpdate(category);
transaction.Commit();
}
An observation: if I manually update just one of the AuditableEntity properties before calling SaveOrUpdate, the PreUpdate event is obviously fired and appropriate data changes are made, and then the AuditableEntity data IS persisted to the database.
using (var session = SessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var category = session.Query<Category>().First();
category.Name = "Updated";
category.CreatedOn = DateTime.Now;
session.SaveOrUpdate(category);
transaction.Commit();
}
Help: I obviously don't want to have to dummy edit an AuditableEntity properties, so any ideas as to what I am doing wrong here?
To answer this I have authored an nhibernate.info WIKI article - http://nhibernate.info/doc/howto/various/changing-values-in-nhibernate-events
The abstract; Audit trails using NHibernate's event model often use the OnPreInsert and OnPreUpdate event listeners to change/ modify the state of the entity. While this does works and is widely documented as a solution, it should be noted the OnPreInsert and OnPreUpdate events are not intended to be used to change the values of the entity and instead they should be used to check values and for that reason they return "veto".
An update blog post from Fabio - http://fabiomaulo.blogspot.com/2011/05/nhibernate-bizarre-audit.html