NHibernate 4 child collection saved, but not re-loaded - nhibernate

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.

Related

How to load different children classes to parent collection in Entity Framework Core?

Given the following:
public class Parent
{
public ChildType childType;
}
public class ChildA : Parent { ... }
public class ChildB : Parent { ... }
public enum ChildType {
childA,
childB
}
public class Content {
public long contentId;
public string? name;
public ICollection<Parent>? contentCollection; <--
...
}
I would like to use the Content class as part of an API. Is it possible to load both children into the collection just using the enum as a discriminator to determine which to cast to?
My understanding is the child objects would need to be loaded from EF as their child class first, then cast to the parent class before being added to the collection as they would be missing properties upon casting back to the child class otherwise. Is this correct? And how can the dbContext be configured to handle this when accessing through the Content class?
Apologies for all the questions, I have not done this before and cannot find an example online. I would like to know any thoughts, pointers or general info before proceeding. Please say if anything is unclear or more info is required.
Edit:
I was trying to map the child objects as their types from the DB, upcast to the parent type to be able to add multiple types to the one collection and then downcast when required for use. As far as I was aware, EF did not have the functionality to do this.
For anyone else who comes across this which needs assistance, I solved my issue by just using ADO.NET which is what entity framework is built around. I was getting stuck by trying to get this working using EF but my belief is it is not able to be done with EF.
Formatting is off a little, and I have renamed everything to suit my original question but here is the solution involved:
Writing an SP to retrieve the data similar to if the objects were stored in a TPH pattern.
Calling that SP using SQLConnection/SQLCommand. (I added this into my context class to keep DAL together but unsure if this is best practice)
public async Task<Collection<Parent>> GetModelMapCollectionAsync(long id) {
Collection<Parent> parentCollection;
using (SqlConnection connection = new SqlConnection(this.Database.GetConnectionString()))
{
using (SqlCommand sqlCommand = new SqlCommand("GetModelMapCollectionAsync", connection))
{
sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
sqlCommand.Parameters.Add(new SqlParameter("#id", id));
await connection.OpenAsync();
await sqlCommand.ExecuteNonQueryAsync();
using (SqlDataReader sqlDataReader = await sqlCommand.ExecuteReaderAsync())
{
MapCollectionResult(sqlDataReader, out parentCollection);
}
}
}
return parentCollection; }
Using a nuget package called Dapper, create row parsers for each type (easiest solution for readability/simplicity IMO)
Use the discriminator column to determine which parser to use on each row returned from the SP. This creates the child object from the row which allows it to be downcast back later.
Add that parsed object to the collection.
private void MapCollectionResult(SqlDataReader sqlDataReader, out Collection parentCollection)
{
parentCollection= new Collection();
var parentParser = sqlDataReader.GetRowParser<Parent>(typeof(Parent));
var paramClassParser = sqlDataReader.GetRowParser<ParamClass>(typeof(ParamClass));
var childAParser = sqlDataReader.GetRowParser<ChildA>(typeof(ChildA));
var childBParser = sqlDataReader.GetRowParser<ChildB>(typeof(ChildB));
ChildType type = ChildType.None;
Parent parent;
while (sqlDataReader.Read())
{
type = (ChildType)sqlDataReader["ChildTypeId"];
switch(type)
{
case ChildType.ChildA:
parent = childAParser(sqlDataReader);
break;
case ChildType.ChildB:
parent = childBParser(sqlDataReader);
break;
default:
parent = parentParser(sqlDataReader);
break;
}
parent.paramClass = paramClassParser(sqlDataReader);
parentCollection.Add(parent);
}}

Create object of one type from object of another type with database lookups

I have an application that gets a car entity from a third party database. I call the entity ThirdPartyCar. My application needs to create a Car entity by using data from a ThirdPartyCar. However, the Car entity must also derive some of its data from my application's database. For example, a status of a ThirdPartyCar might be _BOUGHT and through a database lookup my application must transform to Sold.
I currently have a Car constructor that has a ThirdPartyCar argument. But the Car constructor cannot populate the lookup data since it is an entity and entities should not have a reference to a repositories. So, I also have a service to populate the remaining data:
public class ThirdPartyCar {
#Id
private Long id;
private String vin;
private String status;
// more props + default constructor
}
public class Car {
#Id
private Long id;
private String vin;
private CarStatus status;
// more props (some different than ThirdPartyCar) + default constructor
public Car(ThirdPartyCar thirdPartyCar) {
this.vin = thirdPartyCar.getVin();
// more props set based on thirdPartyCar
// but props leveraging database not set here
}
public class CarStatus {
#Id
private Long id;
private String status;
}
public class CarBuilderService {
private final CarStatusMappingRepository repo;
public Car buildFrom(ThirdPartyCar thirdPartyCar) {
Car car = new Car(thirdPartyCar);
CarStatus status = repo.findByThirdPartyCarStatus(thirdPartyCar.getStatus());
car.setStatus(status);
// set other props (including nested props) that depend on repos
}
}
The logical place to create a Car based on a ThirdPartyCar seems to be the constructor. But I have a disjointed approach b/c of the need of a repo. What pattern can I apply such that all data is created in the constructor but still not have the entity be aware of repositories?
You should avoid linking two POJO classes from different domains in constructor. These two classes should not know anything about each other. Maybe they represent the same concept in two different systems but they are not the same.
Good approach is creating Abstract Factory interface which will be used everywhere where Car should be created from ThirdPartyCar:
interface ThirdPartyCarFactory {
Car createNewBasedOn(ThirdPartyCar source);
}
and one implementation could be your RepositoryThirdPartyCarFactory:
class RepositoryThirdPartyCarFactory implements ThirdPartyCarFactory {
private CarStatusMappingRepository repo;
private CarMapper carMapper;
public Car createNewBasedOn(ThirdPartyCar thirdPartyCar) {
Car car = new Car();
carMapper.map(thirdPartyCar, car);
CarStatus status = repo.findByThirdPartyCarStatus(thirdPartyCar.getStatus());
car.setStatus(status);
// set other props (including nested props) that depend on repos
return car;
}
}
In above implementation you can find CarMapper which knows how to map ThirdPartyCar to Car. To implement this mapper you can use Dozer, Orika, MapStruct or your custom implementation.
Other question is how you got ThirdPartyCar object. If you load it by ID from ThirdPartyRepository you can change your abstract factory to:
interface CarFactory {
Car createNew(String id);
}
and given implementation loads by ID ThirdPartyCar and maps it to Car. Everything is hidden by factory which you can easily exchanged.
See also:
Performance of Java Mapping Frameworks

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);
})

Can NHibernate query for specific children without lazy loading the entire collection?

When I have an entity object with a one-to-many child collection, and I need to query for a specific child object, is there a feature or some clever pattern I haven't come up with yet to avoid that NHibernate fetches the entire child collection?
Example:
class Parent
{
public virtual int Id { get; proteced set; } // generated PK
public virtual IEnumerable<Child> Children { get; proteced set; }
}
class Child
{
public virtual int Id { get; protected set; } // generated PK
public virtual string Name { get; protected set; }
public virtual Parent Parent { get; protected set; }
}
// mapped with Fluent
class Service
{
private readonly ISessionFactory sessionFactory;
public Service(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
void DoSomethingWithChildrenNamedBob(int parentId)
{
using(var session = sessionFactory.OpenSession())
{
var parent = session.Get<Parent>(parentId);
// Will cause lazy fetch of all children!
var childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
// do something with the children
}
}
}
I know it's not the best example because in this case one would probably just query the Child entities directly, but I have encountered situations where I already had a Parent object and needed to traverse specific sub-trees through it.
Short answer: no. Longer answer: you can make it do this, with some sleight of hand.
Rippo's answer above shows how you would do it the 'proper' NHibernate way (whether it's with Linq or QueryOver or HQL doesn't really matter - the point is you have to step outside the parent -> child relationship to do a query). You can take this a step further and disguise this behind a façade. But to do so, you have to remove the mapped relationship entirely and replace it with a query at all times. You'd take out the Parent -> Children mapping, but leave the Child -> Parent mapping intact; then re-write the property on Parent to look like this:
public virtual IQueryable<Child> Children
{
get
{
// somehow get a reference to the ISession (I use ambient context), then
return session.Query<Child>().Where(c => c.Parent == this);
}
}
Now, when you use Parent.Children you get back a queryable collection, so you could then write
IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
The only way you could do this and preserve the mapping is to amend NHibernate's collection objects (or inject your own). Diego Mijelshon (who is around these parts) wrote a spike of exactly that, adding IQueryable support to NHibernate collections so you could do
IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");
But from what I can see, this never went any further and there's no apparent plan to add this capability to NH. I have run Diego's code and it does work, but obviously it's not production quality and hasn't been tested, and I don't think it's ever been officially 'released' even as a private patch.
Here's the link to the discussion on the NH issue tracker: https://nhibernate.jira.com/browse/NH-2319
I believe NH should support this out of the box, as it's a natural way for most .NET devs to want to interact with pretty much anything enumerable, now that we have Linq, and not being able to do it without the side-effect of loading an unbounded collection into RAM sucks. But the traditional NH model is session -> query and that's what 99% of people use.
I asked the same question on NHusers a few weeks ago and didn't get an answer so I suspect the answer is you will always get all the parents children and then perform a in-memory filter. In many cases this might be the correct way in seeing it.
In your case I would rewrite the query to be:-
var childrenNamedBob = session.Query<Children>()
.Where(w => w.Parent.Id == parentId && w.Name == "Bob");
Then simply to get parent (if childrenNamedBob has results) you could call:-
var parent = childrenNamedBob.First().Parent;
or as you rightly pointed out:-
var parent = session.Get<Parent>(parentId);
You can now do that with NHibernate 5 directly without specific code !
See https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt
Build 5.0.0
=============================
** Highlights
...
* Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...

NHibernate - Do I have to have a class to interface with a table?

I have a class called Entry. This class as a collection of strings called TopicsOfInterest. In my database, TopicsOfInterest is represented by a separate table since it is there is a one-to-many relationship between entries and their topics of interest. I'd like to use nhibernate to populate this collection, but since the table stores very little (only an entry id and a string), I was hoping I could somehow bypass the creation of a class to represent it and all that goes with (mappings, configuration, etc..)
Is this possible, and if so, how? I'm using Fluent Nhibernate, so something specific to that would be even more helpful.
public class Entry
{
private readonly IList<string> topicsOfInterest;
public Entry()
{
topicsOfInterest = new List<string>();
}
public virtual int Id { get; set; }
public virtual IEnumerable<string> TopicsOfInterest
{
get { return topicsOfInterest; }
}
}
public class EntryMapping : ClassMap<Entry>
{
public EntryMapping()
{
Id(entry => entry.Id);
HasMany(entry => entry.TopicsOfInterest)
.Table("TableName")
.AsList()
.Element("ColumnName")
.Cascade.All()
.Access.CamelCaseField();
}
}
I had a similar requirement to map a collection of floats.
I'm using Automapping to generate my entire relational model - you imply that you already have some tables, so this may not apply, unless you choose to switch to an Automapping approach.
Turns out that NHibernate will NOT Automap collections of basic types - you need an override.
See my answer to my own question How do you automap List or float[] with Fluent NHibernate?.
I've provided a lot of sample code - you should be able to substitute "string" for "float", and get it working. Note the gotchas in the explanatory text.