What's wrong with my mapping shown below? Is this a problem with GeneratedBy.Foreign()? How should I use it cause my PK in UserTable(UID) is also the FK which refers to PersonTable PK(PID). I get the Duplicate class/entity mapping consoleMappingTest.SystemUser error. what do you suggest(be sure to look at database structure- no way to change it). thanks.
Inheritance structure:
public class Person
{
public virtual int ID { get; set; }
}
public class User:Person
{
public override int ID
{
get
{
return base.ID;
}
set
{
base.ID = value;
}
}
public virtual string Name { get; set; }
public virtual int Salary { get; set; }
}
public class SystemUser:User
{
public virtual int Password { get; set; }
}
Database structure:
for saving some info about person(some fields not shown here):
PersonTable(PID)
for saving User and all it's subclasses like system user:
UserTable(UID,Name,Salary,Type)
and here is my mapping:
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonTable");
Id(x => x.ID, "PID").GeneratedBy.Assigned();//or HiLo-not important
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("UserTable");
DiscriminateSubClassesOnColumn("Type").Default("U");
Id(x => x.ID, "UID").GeneratedBy.Foreign("Person");//how should use this?
Map(x => x.Salary);
Join("PTable", j =>
{
j.KeyColumn("UID");
j.Map(x => x.Name);
});
}
}
public class SystemUserMap : SubclassMap<SystemUser>
{
public SystemUserMap()
{
DiscriminatorValue("SU");
Map(x => x.Password);
}
}
Foreign("") is meant to point to a Reference (Property with another mapped entity) from which the Id should be retrieved. You don't have a Reference to class Person named Person so you can't use it like this.
you already asked the same question with an answer. I know i didn't do it right first shot but would be nice if you told me what doesnt work with the latest edit or you dont like the solution befor asking the same question again
Related
I am trying to model a parent/child association where a Parent class (Person) owns many instances of a child class (OwnedThing) - I want the OwnedThing instances to be saved automatically when the Person class is saved, and I want the association to be bi-directional.
public class Person
{
public class MAP_Person : ClassMap<Person>
{
public MAP_Person()
{
this.Table("People");
this.Id(x => x.ID).GeneratedBy.GuidComb().Access.BackingField();
this.Map(x => x.FirstName);
this.HasMany(x => x.OwnedThings).Cascade.AllDeleteOrphan().KeyColumn("OwnerID").Inverse();
}
}
public virtual Guid ID { get; private set; }
public virtual string FirstName { get; set; }
public virtual IList<OwnedThing> OwnedThings { get; set; }
public Person()
{
OwnedThings = new List<OwnedThing>();
}
}
public class OwnedThing
{
public class MAP_OwnedThing : ClassMap<OwnedThing>
{
public MAP_OwnedThing()
{
this.Table("OwnedThings");
this.Id(x => x.ID).GeneratedBy.GuidComb().Access.BackingField();
this.Map(x => x.Name);
this.References(x => x.Owner).Column("OwnerID").Access.BackingField();
}
}
public virtual Guid ID { get; private set; }
public virtual Person Owner { get; private set; }
public virtual string Name { get; set; }
}
If I set Person.OwnedThings to Inverse then the OwnedThing instances are not saved when I save the Person. If I do not add Inverse then the save is successful but person.OwnedThings[0].Owner is always null after I retrieve it from the DB.
UPDATE
When saving the data NHibernate will set the single association end in the database because it is set via the many-end of the association, so when I retrieve the OwnedThing from the DB it does have the link back to the Person set. My null reference was from Envers which doesn't seem to do the same thing.
Am I understanding you correctly that your problem only occur on "history" entities read by nhibernate envers?
If so, it might be caused by this bug
https://nhibernate.jira.com/browse/NHE-64
The workaround for now is to use Merge instead of (SaveOr)Update.
OwnedThings[0].Owner is most likely null because you are not setting it when you do the add. When using bidirectional relationships you have to do something like the below:
Person person = new Person();
OwnedThing pwnedThing = new OwnedThing();
pwnedThing.Owner = person;
person.OwnedThings.Add(pwnedThing);
If you do not explicity set the pwnedThing.Owner and you query that same object in the same ISession that you created it on it will be null. Typically I have add or remove methods that do this "extra" work for me. Take the below example:
public class Order : Entity
{
private IList<OrderLine> orderLines;
public virtual IEnumerable<OrderLine> OrderLines { get { return orderLines.Select(x => x); } }
public virtual void AddLine(OrderLine orderLine)
{
orderLine.Order = this;
this.orderLines.Add(orderLine);
}
public virtual void RemoveLine(OrderLine orderLine)
{
this.orderLines.Remove(orderLine);
}
}
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
DynamicUpdate();
Table("ORDER_HEADER");
Id(x => x.Id, "ORDER_ID");
HasMany(x => x.OrderLines)
.Access.CamelCaseField()
.KeyColumn("ORDER_ID")
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
a tricky problem - please bear with me. Any help greatly appreciated.
I have a table/class Contact (PK Id) and two derived Client and Debtor (PK and FK ContactId). The 4th table Case has foreign keys to Debtor and Client (mappings below).
Everything worked fine at first. But then I hit some data where the same Contact is a Client in one Case but a Debtor in another. If those are read in one nhibernate query like Session.Query<Case>().Fetch(c => c.Debtor).Fetch(c => c.Client)
there is a
NHibernate.WrongClassException
"Object with id: {someGuid...} was not of the specified subclass: Client
(loading object was of wrong class [Debtor])
Seems like the session first level cache is recognizing the record by it's Id and tries to avoid reading the data from the sql result set. Of course the cast NH thinks is necessary for the reuse fails.
Unfortunately changing the DB schema is not an option. It's a legacy system. (an the schema is ok and clean IMO)
Don't know if it is important: The class Contact is not abstract. There are Contacts used who are neither Client nor Debtor.
Is there any chance of getting this to work with these multi-role-contacts? Thanks in advance.
public partial class ContactMap : ClassMap<Contact>
{
public ContactMap()
{
Id(x=>x.Id).GeneratedBy.Guid();
Map(x=>x.FirstName);
Map(x=>x.Name1).Not.Nullable();
...
}
}
public class DebtorMap : SubclassMap<Debtor>
{
public DebtorMap()
{
KeyColumn("ContactID");
Table("[dbo].[Debtor]");
Map(x => x.MaritalStatus);
...
}
}
public partial class ClientMap : SubclassMap<Client>
{
public ClientMap()
{
KeyColumn("ContactID");
Map(x => x.ClientNo).Not.Nullable();
...
}
}
public partial class CaseMap : ClassMap<Case>
public CaseMap()
{
...
References<Client>(x=>x.Client)
References<Debtor>(x=>x.Debtor)
...
}
If you can add a view to the schema, you can create a view called Roles which unions both Client and Debtor records. You can then change your object model to represent roles:
class Contact
{
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual ICollection<Role> Roles { get; private set; }
}
class Role
{
public virtual Guid Id { get; set; }
}
class Client : Role
{
public virtual string ClientNo { get; set; }
}
class Debtor : Role
{
public virtual string MaritalStatus { get; set; }
}
class ContactMap : FluentNHibernate.Mapping.ClassMap<Contact>
{
public ContactMap()
{
Table("dbo.Contacts");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.FirstName);
HasMany(x => x.Roles)
.KeyColumn("ContactId")
.Not.LazyLoad()
.Fetch.Join();
}
}
class RoleMap : FluentNHibernate.Mapping.ClassMap<Role>
{
public RoleMap()
{
Table("dbo.Roles");
Id(x => x.Id).GeneratedBy.GuidComb();
this.Polymorphism.Implicit();
}
}
class ClientMap : FluentNHibernate.Mapping.SubclassMap<Client>
{
public ClientMap()
{
Table("dbo.Clients");
KeyColumn("Id");
Map(x => x.ClientNo);
}
}
class DebtorMap : FluentNHibernate.Mapping.SubclassMap<Debtor>
{
public DebtorMap()
{
Table("dbo.Debtors");
KeyColumn("Id");
Map(x => x.MaritalStatus);
}
}
Since the Contact table does now share a PK with Client and Debtor tables this should work. The Roles view would look something like this:
create view dbo.Roles as
select
Id,
ContactId
from dbo.Clients
union all
select
Id,
ContactId
from dbo.Debtors
HI all, my scenario
public class Permission
{
public virtual Function Function { get; set; }
public virtual Profile Profile { get; set; }
}
public class MapPermission : ClassMap<Permission>
{
public MapPermission()
{
Table("Permissions".ToUpper());
CompositeId().KeyProperty(x => x.Function, "FunctionID").KeyProperty(x => x.Profile, "ProfileID");
}
}
Where Function AND Profile are two easy mapped entities. When i Run i have this error:
Could not determine type for: Data.Model.Entities.Function, Data.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(FunctionID)"}
Is there a way to avoid this? ultimately i need to create a class with CompositeID made by two custom mapped classes. If i uses compositeID with int fields it works like a charm
Thanks in Advance
Function (Like Profile) Mapping
public class Function
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class MapFunction : ClassMap<Function>
{
public MapFunction()
{
Table("FUNCTIONS");
Id(x => x.ID);
Map(x => x.Name);
}
}
Use KeyReference instead of KeyProperty
public class MapPermission : ClassMap<Permission>
{
public MapPermission()
{
Table("Permissions".ToUpper());
CompositeId()
.KeyReference(x => x.Function, "FunctionID")
.KeyReference(x => x.Profile, "ProfileID");
}
}
I have the following tables in my database:
Announcements:
- AnnouncementID (PK)
- Title
AnouncementsRead (composite PK on AnnouncementID and UserID):
- AnnouncementID (PK)
- UserID (PK)
- DateRead
Users:
- UserID (PK)
- UserName
Usually I'd map the "AnnouncementsRead" using a many-to-many relationship but this table also has an additional "DateRead" field.
So far I have defined the following entities:
public class Announcement
{
public virtual int AnnouncementID { get; set; }
public virtual string Title { get; set; }
public virtual IList<AnnouncementRead> AnnouncementsRead { get; private set; }
public Announcement()
{
AnnouncementsRead = new List<AnnouncementRead>();
}
}
public class AnnouncementRead
{
public virtual Announcement Announcement { get; set; }
public virtual User User { get; set; }
public virtual DateTime DateRead { get; set; }
}
public class User
{
public virtual int UserID { get; set; }
public virtual string UserName { get; set; }
public virtual IList<AnnouncementRead> AnnouncementsRead { get; private set; }
public User()
{
AnnouncementsRead = new List<AnnouncementRead>();
}
}
With the following mappings:
public class AnnouncementMap : ClassMap<Announcement>
{
public AnnouncementMap()
{
Table("Announcements");
Id(x => x.AnnouncementID);
Map(x => x.Title);
HasMany(x => x.AnnouncementsRead)
.Cascade.All();
}
}
public class AnnouncementReadMap : ClassMap<AnnouncementRead>
{
public AnnouncementReadMap()
{
Table("AnnouncementsRead");
CompositeId()
.KeyReference(x => x.Announcement, "AnnouncementID")
.KeyReference(x => x.User, "UserID");
Map(x => x.DateRead);
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("Users");
Id(x => x.UserID);
Map(x => x.UserName);
HasMany(x => x.AnnouncementsRead)
.Cascade.All();
}
}
However when I run this I receive the following error:
"composite-id class must override Equals(): Entities.AnnouncementRead"
I'd appreciate it if someone could point me in the right direction. Thanks
You should do just what NHibernate is telling you. AnnouncementRead should override Equals and GetHashCode methods. They should be based on fields that are part of primary key
When implementing equals you should use instanceof to allow comparing with subclasses. If Hibernate lazy loads a one to one or many to one relation, you will have a proxy for the class instead of the plain class. A proxy is a subclass. Comparing the class names would fail.
More technically: You should follow the Liskows Substitution Principle and ignore symmetricity.
The next pitfall is using something like name.equals(that.name) instead of name.equals(that.getName()). The first will fail, if that is a proxy.
http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html
Im trying to map the following classes:
public abstract class ScheduleType
{
public virtual int Id { get; set; }
public virtual TypeDiscriminatorEnum Discriminator { get; set; }
}
public class DerivedScheduleType : ScehduleType
{
public virtual bool MyProperty { get; set; }
}
public class ScheduleTypeMap : ClassMap<ScheduleType>
{
public ScheduleTypeMap()
{
Id(p => p.Id);
Map(p => p.Discriminator).CustomType<TypeDiscriminatorEnum>().Not.Nullable();
}
}
public class DerivedScheduleTypeMap : SubclassMap<DerivedScheduleType>
{
public DerivedScheduleTypeMap()
{
//DiscriminatorValue(TypeDiscriminatorEnum.DerivedSchedule);
Map(p => p.MyProperty);
}
}
The problem is that queries on ScheduleType joins with all derived tables to find the right one.
I need something that says to NHibernate to join only with the table that represents the right subclass.
Any sugestions?
Thanks in advance!
Use DiscriminateSubClassesOnColumn<TypeDiscriminatorEnum>("discriminator") instead of Map(p => p.Discriminator).
I'm not quite sure what you're trying to achieve though, because you're talking about joining other tables; discriminators aren't used with table-per-subclass, only in table-per-class-hierarchy.