Why NHibernate create a temporary table when I call the Update method on Query? - nhibernate

I just want to update a column table based on a condition. Here my code :
this.session.Query<EventEntity>()
.Where(e => [...])
.Update(c => new { Enabled = true });
This call generates the following SQL :
insert into HT_event SELECT evententit0_.Id as Id FROM event evententit0_ WHERE [...]
UPDATE event SET Enabled=? WHERE (Id) IN (select Id from HT_event)
Why NHibernate creates a temp table to store ids instead of directly generates a correct IN clause ? How can I disable this behavior ?
To give more information, I use a table per class hierarchy inheritance mapping with mapping by code. My EventEntity is an abstract class :
public abstract class EventEntity { ... }
public class EventMap : ClassMapping<EventEntity> { ... }
I have a few others entities that inherit from EventEntity
public abstract class MatchEntity { ... }
public class MatchMap : JoinedSubclassMapping<MatchEntity> { ... }
Thx for your help !

Hibernate propose a way to customize the bulk id strategy, but it's not available for NHibernate at this moment : https://github.com/nhibernate/nhibernate-core/issues/2004.

Related

NHibernate 4 child collection saved, but not re-loaded

I've got an NHibernate 4 project with several collection relationships. I'm unit-testing the object model, exercising all the collections. Most work fine, but in one case, the child collection is cascade-saved properly, but on loading the parent entity and examining the collection property, the child collection is empty.
Here are the abbreviated classes. GatewayUser is the parent object, and it has a collection of Student. The collection has a private backing property, and AddStudent/RemoveStudent methods.
Further complications: I'm using the NHibernate.AspNet.Identity library for OAuth2 user management, and GatewayUser inherits from IdentityUser. That in turn inherits from the the library's internal base entity class, which is different from my project's own base class.
public class GatewayUser : IdentityUser
{
public GatewayUser()
{
}
public virtual string FirstName { get; set; }
// ...More value properties and OAuth stuff omitted
// students associated with this user
private IList<Student> _students = new List<Student>();
public virtual IList<Student> Students
{
get { return new ReadOnlyCollection<Student>(_students); }
}
public virtual GatewayUser AddStudent(Student s)
{
if (_students.Contains(s))
return this;
s.GatewayUser = this;
_students.Add(s);
return this;
}
public virtual GatewayUser RemoveStudent(Student s)
{
if (_students.Contains(s))
{
_students.Remove(s);
}
return this;
}
Student is more ordinary; it inherits from my own BaseEntity class, has many value properties, and its own child collection of ProgramApplication items. Interestingly, this collection saves and loads fine; it's got the same structure (private backer, etc.) as the failing collection in GatewayUser.
The mapping is complicated, because the library internally maps its classes with NHiberante.Mapping.ByCode.Conformist classes (which I have no prior experience with).
I'm mapping my own classes with NHibernate automapping, because I have so many classes and properties to map. To get it all working, I copied the library's mapping helper class, and modified it a bit to add my base entity classes to it's list called baseEntityToIgnore. I also had to create a conformist mapping for GatewayUser, since it has a different base entity type, and my automapping wouldn't pick it up.
The unit test looks like this:
[Test]
public void GatewayUserCascadesStudents()
{
var u = new GatewayUser() { FirstName = "Mama", LastName = "Bear", UserName = "somebody#example.com" };
var s1 = new Student() { FirstName = "First", LastName = "Student" };
var s2 = new Student() { FirstName = "Second", LastName = "Student" };
u.AddStudent(s1).AddStudent(s2);
using (var s = NewSession())
using (var tx = s.BeginTransaction())
{
s.Save(u);
tx.Commit();
}
GatewayUser fetched = null;
int count = 0;
using (var s = NewSession())
{
fetched = s.Get<GatewayUser>(u.Id);
count = fetched.Students.Count;
}
Assert.AreEqual(2, count);
}
The generated SQL inserts into both AspNetUsers and GatewayUser (reflecting the inheritance relationship), and inserts two records into Student. All good. On fetching, the SELECT joins the two user tables, and I get a GatewayUser object, but accessing the Students collection does not trigger a SELECT on the Student table. But if I change the mapping to Lazy(CollectionLazy.NoLazy), the SQL to select eagerly load Students appears in the log, but the collection is not populated. If I switch the database from SQLite to Sql Server, I see the student records in the table. The generated SQL (when NoLazy is applied) will fetch them. So on the database end, things look fine.
I have to think my Frankenstein mapping situation is to blame. I'm mixing the library's conformist mapping with Fluent mapping, and there are two different base entity classes. However, the generated schema looks correct, and the save cascades correctly, so I don't know if that's the issue.
Found my own answer. My mapping of the parent class's list was like this:
public class GatewayUserMap : JoinedSubclassMapping
{
public GatewayUserMap()
{
Key(g => g.Column("Id"));
Property(c => c.FirstName, m => m.Length(50));
// ... more properties
List(gu => gu.Students, map =>
{
map.Key(c => c.Column("GatewayUser_Id"));
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Index(li => li.Column("ListIndex"));
map.Access(Accessor.Field | Accessor.NoSetter);
}
);
}
}
I have a private backing field for the collection. Removing Accessor.NoSetter from the collection mapping fixed it. In fact, it still worked without Accessor.Field -- I guess the mapper does a good job of looking around for one, and using it if found. Changing the name of the private backer from "_students" to "funnyName" prevented the mapper from finding it.

FluentNHiberante union-subclass mapping does not generate table for base class

I am trying to map following domain model using union-subclass strategy and FluentNHibernate. Here is how my classes look (with unneeded parts removed)
public class Benefit
{
}
public class Leave : Benefit
{
}
public class SeasonTicketLoan : Benefit
{
}
And here is my mapping code
public class BenefitMappings : ClassMap<Benefit>
{
public BenefitMappings()
{
UseUnionSubclassForInheritanceMapping();
}
}
public class LeaveMappings : SubclassMap<Leave>
{
}
public class SeasonTicketLoanMappings : SubclassMap<SeasonTicketLoan>
{
}
When I generate a database script using SchemaExport for the above mapping, I get a table for Leave and another one for SeasonTicketLoan but none for Benefit. Am I missing anything here?
...Am I missing anything here?
Yes, you are using mapping Table Per Concrete Class (TPC), Which is intended to create
separate table per each class and
NO table for parent.
To get really deep and clear understanding, you should read this comprehensive article:
Inheritance mapping strategies in Fluent Nhibernate
Where you can read:
Table Per Concrete Class (TPC)
In TPC inheritance, every class in an inheritance hierarchy will have its own table. The inheritance hierarchy masks the fact that there are several independent underlying tables representing each subtype.
code snippet extract:
// mapping of the TPCBaseEntity base class
public class TPCBaseEntityMap : ClassMap<TPCBaseEntity>
{
public TPCBaseEntityMap()
{
// indicates that this class is the base
// one for the TPC inheritance strategy and that
// the values of its properties should
// be united with the values of derived classes
UseUnionSubclassForInheritanceMapping();
In case, we would like to have also table per base class(es), we need:
Table Per Type(TPT)
TPT is an inheritance described in the database with separate tables. Every table provides additional details that describe a new type based on another table which is that table’s parent.
again some mapping snippet extract:
// mapping of the TPTAnimal base class
public class TPTAnimalMap : ClassMap<TPTAnimal>
{
public TPTAnimalMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Animal");
...
// mapping of the TPTHorse class
public class TPTHorseMap : SubclassMap<TPTHorse>
{
public TPTHorseMap()
{
// the name of the schema that stores the table corresponding to the type
Schema("dbo");
// the name of the table corresponding to the type
Table("TPT_Horse");

Fluent nHibernate SubclassMap and AddFromAssemblyOf

I created a generic user repository base class that provides reusable user management functionality.
public class UserRepository<TUser> where TUser : new, IUser
{
}
I have a concrete implementation of IUser called UserImpl, and corresponding mapping class UserImplMap : ClassMap<UserImpl> (they all are in the same namespace and assembly). I add the mapping using AddFromAssemblyOf . I also use this to create / generate the schema.
So far so good and things work as expected.
Now, in a different project, I needed a few additional properties in my IUser implementation class, so I implemented a new class UserImplEx : UserImpl. This class has the additional properties that I needed. Also, I created a new mapping class UserImplExMap : SubclassMap<UserImplEx>
Now when I create schema using this approach, I get two tables one for UserImpl and one for UserImplEx.
Is is possible to configure / code Fluent mapping in some way so that all the properties (self, plus inherited) of UserImplEx get mapped in a single table UserImplEx instead of getting split into two tables?
Alternatively, if I provide full mapping in UserImplExMap : ClassMap<UserImplEx>, then I do get the schema as desired, but I also get an additional table for UserImpl (because corresponding mapping is present in the UserRepository assembly). If I follow this approach, is there a way to tell AddFromAssemblyOf to exclude specific mapping classes?
Option 1
since you have inhertance here and want the correct type back NH has to store the type somewhere, either through the table the data is in or a discriminator.
If a discriminator column in the table does not matter then add DiscriminatorColumn("userType", "user"); in UserImplMap and DiscriminatorValue("userEx") in UserImplExMap
Option 2
class MyTypeSource : ITypeSource
{
private ITypeSource _inner = new AssemblyTypeSource(typeof(UserImplMap).Assembly);
public IEnumerable<Type> GetTypes()
{
return _inner.Where(t => t != typeof(UserImplMap)).Concat(new [] { typeof(UserImplExMap) });
}
public void LogSource(IDiagnosticLogger logger)
{
_inner.LogSource(logger);
}
public string GetIdentifier()
{
return _inner.GetIdentifier();
}
}
and when configuring
.Mappings(m =>
{
var model = new PersistenceModel();
PersistenceModel.AddMappingsFromSource(new MyTypeSource());
m.UsePersistenceModel(model);
})

Fluent NHibernate HasMany relation with different subtypes of same superclass

I´m using Fluent Nhibernate with automapping and having problem setting up a bi-directional HasMany relationship because of my current inheritance.
I simplified version of my code looks like this
public abstract class BaseClass
{
public BaseClass Parent { get; set; }
}
public class ClassA : BaseClass
{
public IList<ClassB> BChilds { get; protected set; }
public IList<ClassC> CChilds { get; protected set; }
}
public class ClassB : BaseClass
{
public IList<ClassD> DChilds { get; protected set; }
}
public class ClassC : BaseClass
{
}
public class ClassD : BaseClass
{
}
Every class can have one parent and some parents can have childs of two types. I´m using table-per-type inheritance which result in the tables
"BaseClass"
"ClassA"
"ClassB"
"ClassC"
"ClassD"
To get a working bi-directional mapping I have made the following overrides
(one example from ClassA)
mapping.HasMany<BaseType>(x => x.BChilds).KeyColumn("Parent_Id");
mapping.HasMany<BaseType>(x => x.CChilds).KeyColumn("Parent_Id");
This works fine on classes with only one type of children, but ClassA with two child types will get all subtypes of BaseType in each list which ofcourse will end up in an exception. I have looked at two different workarounds tho none of them feels really sufficient and I really believe there is a better way to solve it.
Workaround 1: Point to the concrete subtype in the HasMany mapping. (Updated with more info)
mapping.HasMany<ClassB>(x => x.BChilds).KeyColumns("Parent_Id");
(BaseType replaced with ClassB)
With this mapping NHibernate will in some cases look in the ClassB table for a column named Parent_Id, obviously there is no such column as it belongs to the BaseClass table. The problem only occurs if you add a statement based on BChilds during a ClassA select. e.g loading an entity of ClassA then calling ClassA.BChilds seems to work, but doing a query (using NhibernateLinq) something like
Query<ClassA>().Where(c => c.BChilds.Count == 0)
the wrong table will be used. Therefore I have to manually create a new column in this table with the same name and copy all the values. It works but it´s risky and not flexible at all.
Workaround 2: Add a column to the BaseClass that tells the concrete type and add a where statement to the HasMany mapping.
(after my update to workaround1 I´m no longer sure if this could be a workable solution)
By adding a column they same way as it´s done when using table-per-hierarchy inheritance with a discriminatorValue. i.e BaseType table will get a new column with a value of ClassA, ClassB... Tho given how well NHibernate handles the inheritance overall and by reading the NHibernate manual I believe that the discriminator shouldn´t be needed in a table-per-type scenario, seems like Nhibernate already doing the hardpart and should be able to take care of this in a clean way to without adding a new column, just can´t figure out how.
What's your base class mapping and what does your subclass map look like?
You should be able to do
mapping.HasMany(x => x.BChilds);
And with the correct mapping, you shouldn't have a problem.
If it's fluent nhibernate, look into
UseUnionSubclassForInheritanceMapping();

Fluent NHibernate Map Enum as Lookup Table

I have the following (simplified)
public enum Level
{
Bronze,
Silver,
Gold
}
public class Member
{
public virtual Level MembershipLevel { get; set; }
}
public class MemberMap : ClassMap<Member>
{
Map(x => x.MembershipLevel);
}
This creates a table with a column called MembershipLevel with the value as the Enum string value.
What I want is for the entire Enum to be created as a lookup table, with the Member table referencing this with the integer value as the FK.
Also, I want to do this without altering my model.
To map an enum property as an int column, use method CustomType.
public class MemberMap : ClassMap<Member>
{
Map( x => x.MembershipLevel ).CustomType<int>();
}
In order to keep the enum and lookup table in sync, I would add the lookup table and data to your sql scripts. An integration test can verify that the enum and lookup table values are the same.
If you wanted SchemaExport to create this table, add a class and mapping for it.
public class MembershipLevel
{
public virtual int Id { get; set; }
public virtual string Code { get; set; }
}
public class MembershipLevelMap : ClassMap<MembershipLevel>
{
Id( x => x.Id );
Map( x => x.Code );
}
If you are creating the table with SchemaExport, you will need to populate it as well:
foreach (Level l in Enum.GetValues( typeof( Level ))) {
session.Save( new MembershipLevel{ Id = (int) l, Code = l.ToString() });
}
I wouldn't do that because your Enum declaration is not dynamic, or simpler, it doesn't change without recompiling, while your lookup table may change at any moment. If the Enum's and lookup table's values don't match, what's next?
Another reason is if you change the Enum (in code), you'd have to synchronise it with the database table. Since Enums don't have an incremental key (PK), they can't be synchronised so simple. Let's say you remove one Enum member from your code and recompile it, what is supposed to happen? And if you change a value?
I hope I made my objections to this approach clear. So I strongly recommend storing the name or the value of your enum members. To store it by name, just map like this:
public class MemberMap : ClassMap<Member>
{
Map(x => x.MembershipLevel, "level")
.CustomType<GenericEnumMapper<Level>>()
.Not.Nullable();
}
To store the values, do as #Lachlan posted in his answer.
Or if you really need a lookup table and wants to use an Enum with strict checking, create a normal model with PK (or use value for this), KEY and VALUE. Create your enum with your static members, and make the application query the database for the names and values when you start it. If things don't match, do whatever you need. Additionally, this doesn't guarantee your table won't change while your program is running, so you better be sure it doesn't.