Fluent NHibernate - HasOne mapped to a ReferencesAny - nhibernate

I have the following POCO classes:
public class Container
{
public virtual Int64 ContainerId { get; protected set; }
public virtual string Name { get; set; }
public virtual Location Location { get; set; }
}
public abstract class Location
{
public virtual Int64 LocationId { get; protected set; }
public virtual string Name { get; set; }
}
public class UniqueLocation : Location
{
public virtual Container Container { get; set; }
}
public class SharedLocation : Location
{
public SharedLocation()
{
this.Containers = new List<Container>();
}
public virtual IList<Container> Containers { get; set; }
}
and the following Fluent mapping:
public class ContainerMap: ClassMap<Container>
{
public ContainerMap()
{
Table("Containers");
Id(x => x.ContainerId);
Map(x => x.Name);
ReferencesAny(x => x.Location).IdentityType<Int64>().EntityTypeColumn("LocationType").EntityIdentifierColumn("LocationId")
.AddMetaValue<UniqueLocation>("U")
.AddMetaValue<SharedLocation>("S");
}
}
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Table("Locations");
Id(x => x.LocationId);
Map(x => x.Name);
}
}
public class UniqueLocationMap : SubclassMap<UniqueLocation>
{
public UniqueLocationMap()
{
HasOne(x => x.Container).PropertyRef(x => x.Location).ForeignKey("LocationId").Cascade.All().Constrained();
}
}
public class SharedLocationMap : SubclassMap<SharedLocation>
{
public SharedLocationMap()
{
HasMany(x => x.Containers).KeyColumn("LocationId");
}
}
The problem is HasOne() mapping generates the following exception: "broken column mapping for: Container.Location of: UniqueLocation, type Object expects 2 columns, but 1 were mapped".
How do I tell HasOne() to use/map both LocationType and LocationId?

AFAIK Where conditions are not possible on Entity references except using Formulas. The design seems a strange because it would be nasty to change a unique Location to a shared location.
what you want can be done using:
Reference(x => x.Container).Formula("(SELECT c.Id FROM Container c WHERE c.LocationId = Id AND c.LocationType = 'U')");
But i would prefere
class Location
{
...
public virtual bool IsUnique { get { return Container.Count == 1; } }
}

Related

Unable to cast object of type 'NHibernate.Mapping.List' to type 'NHibernate.Mapping.IKeyValue'

Trying to map a relationship in Fluent NHibernate and I'm getting this exception.
Here are the classes
public abstract class TaskContainer : DomainObject
{
public virtual IList<Task> Tasks { get; set; }
}
public class Task : TaskContainer
{
public virtual TaskContainer TaskContainer { get; set; }
public virtual string Description { get; set; }
public static Task Get(int id)
{
return Get<Task>(id);
}
}
And the mapping file
public class TaskMap : ClassMap<Task>
{
public TaskMap()
{
Id(x => x.Id);
Map(x => x.Description);
ReferencesAny(x => x.TaskContainer)
.EntityIdentifierColumn("TaskContainer_Id")
.EntityTypeColumn("TaskContainerDiscriminator")
.IdentityType<int>()
.AddMetaValue<Task>("Task")
HasMany(x => x.Tasks)
.KeyColumn("TaskContainer_Id")
.PropertyRef("Tasks")
.AsList();
}
}
I've seen other references to this error, but they had to do with implementing List<T> instead of IList<T>, which I am not doing.
Try: .PropertyRef("TaskContainer")

Fluent NHibernate: map list of abstract class with table per concrete class (union-subclass)

I'm having a problem with the following scenario.
My class structure is as follows:
public class Owner
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Vehicle> Vehicles { get; set; }
}
public abstract class Vehicle
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
}
public abstract class PoweredVehicle : Vehicle
{
public virtual string EngineType { get; set; }
}
public class Car : PoweredVehicle
{
public virtual int Doors { get; set; }
}
public class Truck : PoweredVehicle
{
public virtual long MaxLoad { get; set; }
}
public class Bicycle : Vehicle
{
public virtual int FrameSize { get; set; }
}
Fluent mappings:
public class OwnerMap : ClassMap<Owner>
{
public OwnerMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name);
HasMany(x => x.Vehicles);
}
}
public class VehicleMap : ClassMap<Vehicle>
{
public VehicleMap()
{
Id(x => x.Id).GeneratedBy.HiLo("10");
Map(x => x.Name);
UseUnionSubclassForInheritanceMapping();
}
}
public class PoweredVehicleMap : SubclassMap<PoweredVehicle>
{
public PoweredVehicleMap()
{
Map(x => x.EngineType);
Abstract();
}
}
public class CarMap : SubclassMap<Car>
{
public CarMap()
{
Map(x => x.Doors);
}
}
public class TruckMap : SubclassMap<Truck>
{
public TruckMap()
{
Map(x => x.MaxLoad);
}
}
public class BicycleMap : SubclassMap<Bicycle>
{
public BicycleMap()
{
Map(x => x.FrameSize);
}
}
I insert a Car and a Bicycle. When I try to insert an Owner with a list of Vehicle objects (with a Car and a Bicycle), I get the following error:
Exception: NHibernate.Exceptions.GenericADOException: could not insert
collection:
[NHibernateTest.Owner.Vehicles#8ace95bc-ad80-46d7-94c7-a11f012b67c6][SQL:
UPDATE "Vehicle" SET Owner_id = #p0 WHERE Id = #p1] --->
System.Data.SQLite.SQLiteException: SQLite error
Since I setup table per concrete class, why is NHibernate trying to update a non-existing table, which is representing the base class? Is this type of mapping not supported for this scenario?
Also, when I change from HasMany to HasManyToMany, this works fine.
In this case the only choice is Inverse() mapping. This means that the concrete Vehicle (Car, Bicycle) must care about the persistence of the relationship.
To enable this, extend the Vehicle class with new property:
public abstract class Vehicle
{
..
// new owner independent navigation property
public virtual Guid OwnerId { get; set; }
}
and extend mapping of the Vehicle
public VehicleMap()
{
..
Map(x => x.OwnerId).Column("Owner_id);
}
and finally invert persistence responsibility. Not the owner, but the collection item will care about correct Owner_id column changes (when concrete Vehicle insert/update is invoked).
(more about inverse: https://stackoverflow.com/a/1454445/1679310)
public OwnerMap()
{
..
HasMany(x => x.Vehicles)
.Inverse()
.Cascade.All();
}
When Vehicle is added into Owner's collection, its OwnerId must be also assigned:
owner.Vehicles.Add(car);
car.OwnerId = owner.Id;

Table Per Subclass Inheritance mapping by NHibernate Mapping-by-Code

How to write mappings in new NHibernate Mapping-By-Code in Table Per Subclass strategy for this classes:
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class JuridicalPerson : Person
{
public virtual int Id { get; set; }
public virtual string LegalName { get; set; }
}
public class PrivatePerson : Person
{
public virtual int Id { get; set; }
public virtual bool Sex { get; set; }
}
Here is a possible mapping in a slighly abbreviated form
public class PersonMapping : ClassMapping<Person>
{
public PersonMapping()
{
Table("person");
Id(x => x.Id, m => m.Generator(Generators.Native));
Property(x => x.Name);
}
}
public class JuridicalPersonMapping : JoinedSubclassMapping<JuridicalPerson>
{
public JuridicalPersonMapping()
{
Table("juridical_person");
Key(m => m.Column("person_id"));
Property(x => x.LegalName);
}
}
public class PrivatePersonMapping : JoinedSubclassMapping<PrivatePerson>
{
public PrivatePersonMapping()
{
Table("private_person");
Key(m => m.Column("person_id"));
Property(x => x.Sex);
}
}
You don't need to duplicate declaration of the Id property in the derived classes. It's inherited from the parent Person class.

Fluent NHibernate - How to map a List<IInterface> to multiple types?

I am trying to map a list containing instances of different types that all implements a common interface with Fluent NHibernate.
Below is a simplified example of how I want my model to look like. I want all types of questions to be stored in the same table and all types of answers to be stored in one table per type.
When using the Mapping in the example for survey Nhibernate treats all questions as IQuestion, and all Answers as IAnswer
What am I doing wrong?
public class SurveyMap : ClassMap<Survey>
{
public SurveyMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Questions).Cascade.All();
HasMany(x => x.Answers).Cascade.All();
}
}
public class BoolAnswerMap : SubclassMap<BoolAnswer>
{
public BoolAnswerMap()
{
Map(x => x.Value).Nullable();
References(x => x.Question);
}
}
public class DecimalAnswerMap : SubclassMap<DecimalAnswer>
{
public DecimalAnswerMap()
{
Map(x => x.Value).Nullable();
References(x => x.Question);
}
}
public class AnswerMap : ClassMap<IAnswer>
{
public AnswerMap()
{
Id(x => x.Id);
}
}
public class BoolQuestionMap : SubclassMap<BoolQuestion>
{
public BoolQuestionMap()
{
//HasMany(x => x.SubQuestions).Cascade.All(); -- Let's leave the subquestions for now
}
}
public class DecimalQuestionMap : SubclassMap<DecimalQuestion>
{
public DecimalQuestionMap()
{
}
}
public class QuestionMap : ClassMap<IQuestion>
{
public QuestionMap()
{
Id(x => x.Id);
Map(x => x.QuestionText).Not.Nullable();
DiscriminateSubClassesOnColumn("Type");
}
}
public class Survey{
private IList<IQuestion> questions = new List<IQuestion>();
private IList<IAnswer> answers = new List<IAnswer>();
public virtual string Name { get; set; }
public virtual IEnumerable<IQuestion> Questions { get { return questions; } }
public virtual IEnumerable<IAnswer> Answers { get { return answers; } }
public virtual void AddQuestion(IQuestion question){
questions.Add(question);
}
public virtual void AddAnswer(IAnswer answer{
answers.Add(answer);
}
}
public interface IQuestion{
int Id { get; set; };
string QuestionText { get; set; }
}
public interface IAnswer{
int Id { get; set; }
IQuestion Question { get; set; }
}
public class BoolQuestion: IQuestion{
private IList<IQuestion> subQuestions = new List<IQuestion>();
int Id { get; set; };
string QuestionText { get; set; }
public virtual IEnumerable<IQuestion> SubQuestions { get { return subQuestions; } }
public virtual void AddSubQuestion(IQuestion question){
subQuestions.Add(question);
}
}
//You could argue that this could be just Question (but this is a simplified example)
public class DecimalQuestion: IQuestion{
int Id { get; set; };
string QuestionText { get; set; }
}
public class BoolAnswer : IAnswer {
public int Id { get; set; }
public IQuestion Question { get; set; }
bool Value { get; set; }
}
public class DecimalAnswer : IAnswer {
public int Id { get; set; }
public IQuestion Question { get; set; }
decimal Value { get; set; }
}
ReferencesAny should do what you want.

NHibernate - Delete Not Peristing in the Database

i'm trying to remove an item from a one to many list and have it persist in the database. Here are the entities i have defined:
public class SpecialOffer
{
public virtual int SpecialOfferID { get; set; }
public virtual string Title { get; set; }
public virtual IList<SpecialOfferType> Types { get; private set; }
public SpecialOffer()
{
Types = new List<SpecialOfferType>();
}
}
public class SpecialOfferType
{
public virtual SpecialOffer SpecialOffer { get; set; }
public virtual Type Type { get; set; }
public virtual int MinDaysRemaining { get; set; }
#region composite id requirements
public override bool Equals(object obj)
{
if (obj == null || !(obj is SpecialOfferType))
return false;
var t = (SpecialOfferType)obj;
return SpecialOffer.SpecialOfferID == t.SpecialOffer.SpecialOfferID && Type.TypeID == t.Type.TypeID;
}
public override int GetHashCode()
{
return (SpecialOffer.SpecialOfferID + "|" + Type.TypeID).GetHashCode();
}
#endregion
}
public class Type
{
public virtual int TypeID { get; set; }
public virtual string Title { get; set; }
public virtual decimal Price { get; set; }
}
With the following fluent mappings:
public class SpecialOfferMap : ClassMap<SpecialOffer>
{
public SpecialOfferMap()
{
Table("SpecialOffers");
Id(x => x.SpecialOfferID);
Map(x => x.Title);
HasMany(x => x.Types)
.KeyColumn("SpecialOfferID")
.Inverse()
.Cascade.All();
}
}
public class SpecialOfferTypeMap : ClassMap<SpecialOfferType>
{
public SpecialOfferTypeMap()
{
Table("SpecialOfferTypes");
CompositeId()
.KeyReference(x => x.SpecialOffer, "SpecialOfferID")
.KeyReference(x => x.Type, "TypeID");
Map(x => x.MinDaysRemaining);
}
}
public class TypeMap : ClassMap<Type>
{
public TypeMap()
{
Table("Types");
Id(x => x.TypeID);
Map(x => x.Title);
Map(x => x.Price);
}
}
The problem i have is that if i remove an item from the SpecialOffer.Types collection it successfully removes it from the list but when i try to save the session the change is not persisted in the database. I'm assuming this is something to do with the composite id on the join table since i have been able to do this successfully in the past with a standard id.
I'd appreciate it if someone could show me what i'm doing wrong. Thanks
I think you have to 1) Change the cascade setting on SpecialOffer.Types to Cascade.AllDeleteOrphan() and 2) set SpecialOfferType.SpecialOffer = null when you remove it from the collection. Since the collection is the inverse side of the relationship, the many-to-one reference to SpecialOffer on SpecialOfferType has to be set to null to make it an orphan, then Cascade.AllDeleteOrphan will cause it to be deleted.