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.
Related
Composite Key mapping is not working in below scenario.
Database tables are as below.
Employee { Emp_ID, Name, Role_ID } (Role_ID is foreign key from Role table);
Leave { Leave_ID, Leave_Date, Leave_Comment};
Employee_Leave { Emp_ID, Leave_ID, Approval }; (EMP_ID and Leave_ID are composite key from Employee and Leave table respectively)
Entity classes are as below.
class Employee
{
public virtual string ID { get; set; }
public virtual string Name { get; set; }
public virtual Role EmpRole { get; set; }
}
public class Leave
{
virtual public Int16 LeaveID { get; set; }
virtual public String LeaveDate { get; set; }
virtual public String Comment { get; set; }
}
public class EmployeeLeaveApproval
{
public virtual string EMP_ID { get; set; }
public virtual int Leave_ID { get; set; }
public virtual string Approval { get; set; }
}
Mapping classes are as below.
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Table("Employee");
Id(x => x.ID, "ID");
Map(x => x.Name, "NAME");
References(x => x.EMPRole, "ROLE_ID").Not.LazyLoad();
}
}
public class LeaveMap : ClassMap<Leave>
{
public LeaveMap()
{
Table("Leave");
Id(x => x.LeaveID, "LEAVE_ID");
Map(x => x.LeaveDate, "LEAVE_DATE");
Map(x => x.Comment, "LEAVE_COMMENT");
}
}
Below class mapping is working fine.
public class EmployeeLeaveApprovalMap : ClassMap<EmployeeLeaveApproval>
{
public EmployeeLeaveApprovalMap()
{
Table("Employee_Leave");
Id(x => x.EMP_ID, "EMP_ID");
Map(x => x.Leave_ID, "LEAVE_ID");
Map(x => x.Approval, "Approval");
}
}
Below class mapping is not working.
public class EmployeeLeaveApprovalMap : ClassMap<EmployeeLeaveApproval>
{
public EmployeeLeaveApprovalMap()
{
Table("Employee_Leave");
CompositeId()
.KeyProperty(x => x.EMP_ID, "EMP_ID")
.KeyProperty(x => x.Leave_ID, "LEAVE_ID");
Map(x => x.Approval, "Approval");
}
}
Getting error "Database was not configured through Database method." while calling method BuildSessionFactory.
Many many thanks in advance for any help.
Found solution rather to say found the mistake i am doing.
Equal and GetHashCode methods are left being implemented in my code.
Below is the corrected entiity,
public class EmployeeLeaveApproval : Object
{
public virtual string EMP_ID { get; set; }
public virtual int Leave_ID { get; set; }
public virtual string Approval { get; set; }
public EmployeeLeaveApproval() {}
public override bool Equals(object obj)
{
if (obj == null)
return false;
EmployeeLeaveApproval EL = (EmployeeLeaveApproval)obj;
if (EL == null)
return false;
if (EMP_ID == EL.EMP_ID && Leave_ID == EL.Leave_ID)
return true;
return false;
}
public override int GetHashCode()
{
return (EMP_ID + "|" + Leave_ID).GetHashCode();
}
}
regards..Dharmendra
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; } }
}
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;
I have a base class that looks like this:
public abstract class MyBaseClass
{
public virtual DateTime UpdatedOn { get; set; }
}
I then have a series of other entities that inherit from this:
public class User : MyBaseClass
{
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
}
My mapping for User would be:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.UserName);
Map(x => x.Password);
Map(x => x.UpdatedOn);
}
}
I then have other entities, mapped the same way.
My problem is I get the following error:
Tried to add property 'UpdatedOn' when already added.
I guess this is because I map the UpdatedOn column in every entity?
Each of my tables has this UpdatedOn column, so how should I be mapping it?
Use a derived class in fluent nHibernate
This question should help you out. You can basically use a base fluent nhibernate mapping class to map your UpdatedOn column and derive all of your other mapping classes from that base class.
I had similar kind of situation. I did like this..it works...
I was supposed to exposes entities as Interface..
public interface IEntity
{
DateTime CreationDate { get; set; }
DateTime UpdationDate { get; set; }
}
public interface IUser : IEntity
{
DateTime UserName { get; set; }
DateTime Password { get; set; }
}
public interface IEmployee : IEntity
{
DateTime Name { get; set; }
DateTime Key { get; set; }
}
public abstract class Entity : IEntity
{
public virtual DateTime CreationDate { get; set; }
public virtual DateTime UpdationDate { get; set; }
}
public class User : Entity, IUser
{
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
}
public class Employee : Entity, IEmployee
{
public virtual string Name { get; set; }
public virtual string Key { get; set; }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.UserName);
Map(x => x.Password);
Map(x => x.CreationDate);
Map(x => x.UpdationDate);
}
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Name);
Map(x => x.Key);
Map(x => x.UpdationDate);
Map(x => x.CreationDate);
}
}
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.