Mapping a junction table with additional data in C# with NHibernate - c#

Let's say I have a Parent and Child table, and a ParentChild table that maintains the relationships between them.
Parent class:
public class Parent
{
public virtual int ParentId { get; set; }
public virtual bool Deleted { get; set; }
public Parent()
{
_children = new HashedSet<Child>();
}
protected ISet<Child> _children;
public virtual ISet<Child> children
{
get { return _children; }
protected set { _children = value; }
}
}
Child class:
public class Child
{
public virtual int ChildId { get; set; }
public virtual bool Deleted { get; set; }
public Child()
{
_parents = new HashedSet<Parent>();
}
protected ISet<Parent> _parents;
public virtual ISet<Parent> parents
{
get { return _parents; }
protected set { _parents = value; }
}
}
and the ParentChild class:
public class ParentChild
{
public virtual int ParentChildId { get; set; }
public virtual Parent Parent { get; set; }
public virtual Child Child { get; set; }
public virtual int DisplayOrderIndex { get; set; }
public virtual bool Deleted { get; set; }
}
How would I map these so that I can do an update on the parent object that will also update the junction table with the additional data (not just parent/child id) and the Child table.
For now, I'm doing:
Parent Map
public ParentMap()
{
Table("Parent");
Id(x => x.ParentId);
Map(x => x.Deleted);
HasMany(x => x.ParentChild)
.Table("Parent_Child")
.KeyColumn("ParentID")
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.LazyLoad();
}
Child Map
public ChildMap()
{
Table("Child");
Id(x => x.ChildId);
Map(x => x.Deleted);
HasMany(x => x.ParentChild)
.Table("Parent_Child")
.KeyColumn("ChildID")
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.LazyLoad();
}
ParentChild Map
public ParentChildMap()
{
Table("Parent_Child");
Id(x => x.ParentChildId, "Parent_ChildID");
References(x => x.Parent, "ParentID");
References(x => x.Child, "ChildID");
Map(x => x.DisplayOrderIndex);
Map(x => x.Deleted);
}
But when I pull from the Parent repository, the Child in the ParentChild collection of the Parent object is a Castle.Proxy instead of a Child type.
How should this mapping be done to achieve the desired result?

Related

Fluent NHibernate: How to perform INNER JOIN?

I was wondering if someone could help me. I want to perform SQL INNER JOIN operation by using NHibernate. First of all let me introduce you to the structure of my database.
I have the following parameters into my C# method: int documentId, int userId, int folderId. My main goal is to get RoleDocumentValueEntity. By using USER_ID and FOLDER_ID I can get the unique FolderUserRoleEntity. I am looking for the ROLE_ID. I want to join ROLE_DOCUMENT_VALUE and FOLDER_USER_ROLES tables by ROLE_ID where DOCUMENT_ID is equal to specific value which I have as a parameter of C# method.
My entities:
public class RoleDocumentValueEntity
{
public virtual int Id { get; set; }
public virtual RoleEntity Role { get; set; }
public virtual DocumentEntity Document { get; set; }
public virtual string Text { get; set; }
}
public class RoleEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<UserRoleEntity> UserRoles { get; set; }
public virtual IList<FolderUserRoleEntity> FolderUserRoles { get; set; }
}
public class FolderEntity
{
public virtual int Id { get; set; }
public virtual UserEntity User { get; set; }
public virtual IList<DocumentEntity> Documents {get; set;}
}
public class UserEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class UserRoleEntity
{
public virtual int Id { get; set; }
public virtual int UserId { get; set; }
public virtual RoleEntity Role { get; set; }
}
public class FolderUserRoleEntity
{
public virtual int Id { get; set; }
public virtual int UserId { get; set; }
public virtual RoleEntity Role { get; set; }
public virtual FolderEntity Folder { get; set; }
}
Mappings:
public class RoleDocumentValueMap : BaseDatabaseMap<RoleDocumentValueEntity>
{
public RoleDocumentValueMap()
{
this.Table("ROLE_DOCUMENT_VALUE");
Id(x => x.Id, "ROLE_DOCUMENT_VALUE_ID");
// Relationships
References(x => x.Role)
.Column("ROLE_ID")
.Cascade.None();
References(x => x.Document)
.Column("DOCUMENT_ID")
.Cascade.None();
}
}
public class RoleMap : BaseDatabaseMap<RoleEntity>
{
public RoleMap()
{
this.Table("ROLES");
Id(x => x.Id, "ROLE_ID");
HasMany(x => x.UserRoles)
.KeyColumn("ROLE_ID")
.Cascade.All()
.Inverse();
HasMany(x => x.FolderUserRoles)
.KeyColumn("ROLE_ID")
.Cascade.All()
.Inverse();
}
}
public class UserRoleMap : BaseDatabaseMap<UserRoleEntity>
{
public UserRoleMap()
{
this.Table("USER_ROLES");
this.Id(x => x.Id, "USER_ROLE_ID");
Map(x => x.UserId).Column("USER_ID");
References(x => x.Role).Column("ROLE_ID").Cascade.None().Fetch.Join();
}
}
public class FolderUserRoleMap : BaseDatabaseMap<FolderUserRoleEntity>
{
public FolderUserRoleMap()
{
this.Table("FOLDER_USER_ROLES");
Id(x => x.Id, "FOLDER_USER_ROLE_ID");
Map(x => x.UserId).Column("USER_ID");
References(x => x.Folder).Column("FOLDER_ID").Cascade.None().Fetch.Join();
References(x => x.Role).Column("ROLE_ID").Cascade.None().Fetch.Join().Not.LazyLoad();
}
}
public class FolderMap : BaseDatabaseMap<FolderEntity>
{
public FolderMap()
{
this.Table("FOLDERS");
Id(x => x.Id, "FOLDER_ID");
HasMany(x => x.Documents)
.Cascade
.AllDeleteOrphan()
.Inverse()
.KeyColumn("FOLDER_ID");
}
}
public class DocumentMap : BaseDatabaseMap<DocumentEntity>
{
public DocumentMap()
{
this.Table("DOCUMENTS");
Id(x => x.Id, "DOCUMENT_ID");
// Relationships
References(x => x.Folder)
.Column("FOLDER_ID")
.Cascade.None();
}
}
INNER JOIN
I manually created SQL query which I want to get by using NHibernate.
SELECT ROLE_DOCUMENT_VALUE.*
FROM ROLE_DOCUMENT_VALUE
INNER JOIN FOLDER_USER_ROLES ON FOLDER_USER_ROLES.ROLE_ID = ROLE_DOCUMENT_VALUE.ROLE_ID
AND FOLDER_USER_ROLES.FOLDER_ID = ?
AND FOLDER_USER_ROLES.USER_ID = ?
AND ROLE_DOCUMENT_VALUE.DOCUMENT_ID = ?;
It looks like my NHibernate criteria should look like this one:
var result = this.Session.CreateCriteria<RoleDocumentValueEntity>()
.CreateCriteria("FolderUserRoles", "fr")
.Add(Restrictions.Eq("fr.UserId", userId))
// etc
.List();
But this criteria cannot be performed because RoleDocumentValueEntity doesn't have such property as FolderUserRoles.
Please, could you provide me what is the best practice in this case? How can I create the SQL query which I want by using NHibernate?
you could use linq support for Nhibernate to achieve this easily,
from doc in Session.Query<RoleDocumentValueEntity>()
join role in Session.Query<FolderUserRoleEntity>()
on doc.Role equals role.Role

nhibernate Could not determine type for entity exception

i am new to fluent nhibernate. i'm developing a project contains following 3 tables in its database:
"Person",
"RealPerson" and
"LegalPerson"
these three tables have relation as shown in picture:
all entities in my code such as these three entities are inherited from a base entity class here is the code of these entities
public class Person : Entity
{
public virtual RealPerson RealPerson { set; get; }
public virtual LegalPerson LegalPerson { set; get; }
}
public class RealPerson : Entity
{
public virtual string FirstName { set; get; }
public virtual string LastName { set; get; }
public virtual string FatherName { set; get; }
public virtual string NationalCode { set; get; }
public virtual DateTime BirthDate { set; get; }
public virtual string PhoneNumber { set; get; }
public virtual string MobileNumber { set; get; }
public virtual string EmailAddress { set; get; }
public virtual string HomeAddress { set; get; }
public virtual string WorkAddress { set; get; }
public virtual RealPerson Proxy { set; get; }
}
public class LegalPerson : Entity
{
public virtual string LegalPerson_Name { set; get; }
public virtual string RegistrationNumber { set; get; }
public virtual string Address { set; get; }
public virtual string PhoneNumber1 { set; get; }
public virtual string PhoneNumber2 { set; get; }
public virtual string PhoneNumber3 { set; get; }
public virtual RealPerson Proxy { set; get; }
}
and the code of base entity class is here:
public class Entity
{
protected bool Equals(Entity other)
{
if (other == null)
return false;
if (Id == Guid.Empty || other.Id == Guid.Empty)
return base.Equals(other);
return Id.Equals(other.Id);
}
public virtual Guid Id { set; get; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is Entity)
return Equals((Entity)obj);
return base.Equals(obj);
}
protected ISession Session
{
get { return SessionAccountant.GetSession(); }
}
public virtual void Save()
{
Session.SaveOrUpdate(this);
}
public virtual void Delete()
{
Session.Delete(this);
}
}
Finally the classmaps are as following:
public class PersonMapping : ClassMap<Person>
{
public PersonMapping()
{
Table("Person");
Id(x => x.Id).GeneratedBy.GuidComb().Column("Person_Id");
References(x => x.RealPerson).Nullable().LazyLoad().Column("RealPerson_Id");
References(x => x.LegalPerson).Nullable().LazyLoad().Column("LegalPerson_Id");
}
}
public class RealPersonMapping : ClassMap<RealPerson>
{
public RealPersonMapping()
{
Table("RealPerson");
Id(x => x.Id).GeneratedBy.GuidComb().Column("RealPerson_Id");
Map(x => x.FirstName).Not.Nullable().Column("FirstName");
Map(x => x.LastName).Not.Nullable().Column("LastName");
Map(x => x.FatherName).Not.Nullable().Column("FatherName");
Map(x => x.NationalCode).Not.Nullable().Column("NationalCode");
Map(x => x.BirthDate).Nullable().Column("BirthDate");
Map(x => x.ShenasnamehNumber).Nullable().Column("ShenasnamehNumber");
Map(x => x.PhoneNumber).Nullable().Column("PhoneNumber");
Map(x => x.MobileNumber).Nullable().Column("MobileNumber");
Map(x => x.EmailAddress).Nullable().Column("EmailAddress");
Map(x => x.HomeAddress).Nullable().Column("HomeAddress");
Map(x => x.WorkAddress).Nullable().Column("WorkAddress");
References(x => x.Proxy).Nullable().LazyLoad().Column("Proxy_Id");
}
}
public class LegalPersonMapping : ClassMap<LegalPerson>
{
public LegalPersonMapping()
{
Table("LegalPerson");
Id(x => x.Id).GeneratedBy.GuidComb().Column("LegalPerson_Id");
Map(x => x.LegalPerson_Name).Not.Nullable().Column("LegalPerson_Name");
Map(x => x.RegistrationNumber).Not.Nullable().Column("RegistrationNumber");
Map(x => x.Address).Not.Nullable().Column("Address");
Map(x => x.PhoneNumber1).Nullable().Column("PhoneNumber1");
Map(x => x.PhoneNumber2).Nullable().Column("PhoneNumber2");
Map(x => x.PhoneNumber3).Nullable().Column("PhoneNumber3");
References(x => x.Proxy).Nullable().LazyLoad().Column("Proxy_Id");
}
}
i set the configuration and create a session. but when i run the project i get this exception at run time:
An unhandled exception of type 'NHibernate.MappingException' occurred in NHibernate.dll
Additional information: Could not determine type for: EntitiesClasses.Person
what is wrong with this code?!
You need to inherit from SubclassMap.
See here http://notherdev.blogspot.com/2012/01/mapping-by-code-inheritance.html?m=1
I think this is going to change your schema in that your base class isn't going to have the id of the subclass. The id of the base class is the id of the subclass.

Fluent NHibernate Many To Many on Same Table

How can I create relation like this?
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.UserId);
Map(x => x.UserName);
Map(x => x.Password);
Map(x => x.FirstName);
Map(x => x.LastName);
HasManyToMany(x => x.FBFriends).Cascade.All().Table("UserFBFriend");
HasManyToMany(x => x.FBFriends).Cascade.All().Inverse().Table("UserFBFriend");
}
}
public class User : BaseClass<User>
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
public virtual string Password { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<User> FBFriends { get; set; }
public User()
{
FBFriends = new List<User>();
}
public virtual void AddUserFBFriend(User user)
{
user.FBFriends.Add(this);
FBFriends.Add(user);
}
}
UPDATE
HasManyToMany(x => x.LeftFBFriends).Cascade.All().Table("UserFBFriend");
HasManyToMany(x => x.RightFBFriends).Cascade.All().Inverse().Table("UserFBFriend");
public virtual IList<User> LeftFBFriends { get; set; }
public virtual IList<User> RightFBFriends { get; set; }
public User()
{
LeftFBFriends = new List<User>();
RightFBFriends = new List<User>();
}
As discussed, we need two collections, representing both directions A-B, B-A
public virtual IList<User> LeftFBFriends { get; set; }
public virtual IList<User> RightFBFriends { get; set; }
And we'd need explicit column mapping:
HasManyToMany(x => x.LeftFBFriends)
.Table("UserFBFriend")
.ParentKeyColumn("LeftColumn")
.ChildKeyColumn("RightColumn")
.Cascade.All().Table("UserFBFriend");
HasManyToMany(x => x.RightFBFriends)
.Table("UserFBFriend")
.ParentKeyColumn("RightColumn")
.ChildKeyColumn("LeftColumn")
.Cascade.All()
.Inverse()
So, this way we do correctly instruct NHibernate, which column will play parent (this ID) and which will be the child (collection ID). We have to do that for both collections

How to map a MainValue property using Fluent or LinqToNHibernate?

I have a one to many relationship between my product and image entities. What I'm trying to achieve is to map a main image in the product entity, using Fluent mapping or LinqToNHibernate.
My classes:
public class Product {
public virtual int Id { get; set; }
public virtual IList<Image> images { get; set; }
}
public class Image {
public virtual int Id { get; set; }
public virtual Product Product { get; set; }
public virtual bool IsMain { get; set; }
}
My mappings:
public class ProductMap : ClassMap<Product> {
public ProductMap() {
Id(x => x.Id);
HasMany(x => x.images).KeyColumn("id_product");
}
}
public class ImageClassMap : ClassMap<Image> {
public ImageClassMap() {
Table("product_image");
Id(x => x.Id);
References(x => x.Product, "id_product");
Map(x => x.IsMain).Column("is_main");
}
}
I was able to achieve this with QueryOver as follows:
Image imageAlias = null;
var product = session.QueryOver<Product>()
.Where(x => x.Id == 2)
.Left.JoinQueryOver(x => x.images, () => imageAlias, x => x.IsMain)
.SingleOrDefault();
Is it possible to have a MainImage property in the Product class, where IsMain property is true, using Fluent mapping or LinqToNHibernate?
Yes, you can try,
public class Product {
...
public Image MainImage { get; set; }
}
and then the mapping,
public ProductMap() {
...
HasOne(product => product.MainImage).KeyColumn("main_image_id");
}

TreeMenu with self Referencing and nested Menu

I have a need to create a TreeMenu using NHibernate.
Here is what i cameup with:
public class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Id(x => x.Id);
Map(x => x.AppId);
Map(x => x.Title);
Map(x => x.MaxDepth).Default("3");
Map(x => x.IsActive);
References(x => x.Parent).LazyLoad().Column("ParentId");
HasMany(x => x.ChildrenNodes)
.LazyLoad()
.KeyColumn("ParentId")
.Cascade.All();
Table("Category");
}
}
public class Category : Node
{
public virtual string AppId { get; set; }
public override string Title { get; set; }
public override int MaxDepth { get; set; }
public virtual Category Parent { get; set; }
public virtual IList<Category> ChildrenNodes { get; set; }
public virtual bool IsActive { get; set; }
}
public abstract class Node
{
public virtual long Id { get; set; }
public abstract string Title { get; set; }
public abstract int MaxDepth { get; set; }
}
Here is my testing code:
[Test]
public void CreateTableInDb()
{
using (var db = new FileDatabase(DbFileLocation, true))
{
var categoryMenu = new Category
{
AppId = "1",
MaxDepth = 3,
IsActive = true,
Title = "Services"
};
db.Create(categoryMenu);
categoryMenu.ChildrenNodes = new List<Category>
{
new Category
{
Title = "SubService-Level1",
Parent = categoryMenu,
}
};
db.Update(categoryMenu);
}
}
When i look into the table it's creating i have only one row,
with empty ParentId column.
How can i fix it and what i'm doing wrong?
first you'll missing .Inverse() on the hasmany. As it stands it will save and update the parentId twice
all collections should be readonly. NHibernate will replace them with tracking and lazyloading collections which you are throwing away. Change the code to
public class Category : Node
{
public Category()
{
// best practice so that nobody has to null check the collection
ChildrenNodes = new List<Category>();
}
public virtual IList<Category> ChildrenNodes { get; private set; }
public void Add(Category subcategory)
{
subcategory.Parent = this;
ChildrenNodes.Add(subcategory);
}
}
// in testcode
categoryMenu.Add(new Category { Title = "SubService-Level1" });

Categories

Resources