public class Node
{
public virtual int Id {get; set;}
public virtual string Name {get; set;}
public virtual IList<Node> Ancestors {get; set;}
public virtual IList<Node> Descendants {get; set;}
}
how to setup the mapping convention for this case?
thank you
Are you sure you want a convention? I am going to guess you just want a fluent mapping. Here's an example from the last time I helped someone with this:
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x.CustomerId);
Map(x => x.Birthday);
Map(x => x.FirstName);
HasManyToMany(x => x.Parents)
.ParentKeyColumn("ChildID")
.ChildKeyColumn("ParentID")
.Inverse();
HasManyToMany(x => x.Children)
.ParentKeyColumn("ParentID")
.ChildKeyColumn("ChildID");
}
}
See here for the original thread. That thread has a link to an example project I made for demonstrating self referencing many-to-many relationships, which is here.
Related
I have a table called "Customers" and in this table there are store and office address. In the code there is a Customer class with two properties that are of type Address (one for StoreAddress and OfficeAddress).
public class Customer
{
public virtual int Id { get; set;}
public virtual string Name {get;set;}
public virtual string Email {get; set;}
public virtual Address StoreAddress {get; set;}
public virtual Address OfficeAddress {get; set;}
}
public class Address
{
public string Address1 {get; set;}
public string Address2 {get; set;}
public string State {get; set;}
public string City {get; set;}
public string Zip {get; set;}
}
I can map items that are not of an entity type Address but not sure how to map to another entity property within the customer entity?..
Table("Customers");
Schema("dbo);
Id(x => x.ID).Column("CustomerId");
Map(x => x.Name);
Map(x => x.Email);
How would I be able to map to my StoreAddress and OfficeAddress from the table Customers table?
You can use component mapping:
Component(x => StoreAddress).ColumnPrefix("StoreAddress");
Component(x => OfficeAddress).ColumnPrefix("OfficeAddress");
Then create a component map for Address type:
public class AddressMap : ComponentMap<Address>
{
public AddressMap()
{
//map properterties
}
}
In case, I understand your missing point, we can use:
References / many-to-one
References is for creating many-to-one relationships between two entities, and is applied on the "many side." You're referencing a single other entity, so you use the References method. #HasMany / one-to-many is the "other side" of the References relationship, and gets applied on the "one side."
Table("Customers");
Schema("dbo);
Id(x => x.ID).Column("CustomerId");
Map(x => x.Name);
Map(x => x.Email);
// this is the fluent way for many-to-one
References(x => x.StoreAddress);
References(x => x.OfficeAddress);
The complete overview of the References() syntax could be found here: Mapping-by-Code - ManyToOne (by Adam Bar) - which is about mapping-by-code, but provides comparison with fluent syntax (the second half of the post)
I have two tables in Oracle
Entity
----------
**EntityId** NUMBER(9), **EntityName** VARCHAR2
EntityLinks
--------------
**EntityLinkId** NUMBER(9),**ParentEntityId** NUMBER(9), **ChildEntityId** NUMBER(9)
Table EntityLinks will store ManyToMany relationship between various entities. ParentEntityId and ChildEntityId are having foreign key relationship with Entity.
I have below a below class for Entity as well
public class Entity
{
public virtual int EntityId {get; set}
public virtual IList<Entity> ParentEntities {get; set}
public virtual IList<Entity> ChildEntities{get; set}
}
public class EntityLinks
{
public virtual int EntityLinkId {get; set}
public virtual Entity ParentEntityId {get; set}
public virtual Entity ChildEntityId {get; set}
}
Here is the mapping for both the classes:
public class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Table("Entity")
Id(x=>x.EntityId).GeneratedBy.Increment();
*---- How to map for ParentEntities and ChildEntites?----*
}
}
public class EntityLinksMap : ClassMap<Entity>
{
public EntityMap()
{
Table("Entity")
Id(x=>x.EntityId).GeneratedBy.Increment();
References(x=>x.ParentEntityId).Column("ParentEntityId");
References(x=>x.ChildEntityId).Column("ChildEntityId");
}
}
My question is how should I do mapping in entity class for ParentEntities and ChildEntites so that I get the list of both parent and child for a particular entity?
I would say that it is really good, that you are using man-in-the-middle table as a standard mapped entity.
I am talking about the fact, that we can use mapping without that pairing table being represented as mapped entity. The syntax would be HasManyToMany
But because you've chosen to have pairing table as an entity you have to change the Business model:
public class Entity
{
public virtual int EntityId {get; set}
//public virtual IList<Entity> ParentEntities {get; set}
//public virtual IList<Entity> ChildEntities{get; set}
public virtual IList<EntityLinks> ParentEntityLinks {get; set}
public virtual IList<EntityLinks> ChildEntityLinks {get; set}
}
Mapping then would be:
public EntityMap()
{
Table("Entity")
Id(x=>x.EntityId).GeneratedBy.Increment();
HasMany(x => x.ParentEntityLinks)...
HasMany(x => x.ChildEntityLinks)...
}
More about HasMany mapping:
Mapping-by-Code - Set and Bag by Adam Bar
Scroll down to Fluent NHibernate's equivalent:
HasMany(x => x.Users)
.AsSet<CustomComparer>() // or .AsSet(), .AsBag()
.Fetch.Join()
.BatchSize(100)
.LazyLoad() // or .ExtraLazyLoad()
.Table("tableName")
.Schema("schemaName")
.Cascade.AllDeleteOrphan() // or .None(), .SaveUpdate(), .All(), DeleteOrphan()
.Inverse()
...
// and many more settings
...
If in fact, you really want to keep the many-to-many, and properties like
public virtual IList<Entity> ParentEntities {get; set}
public virtual IList<Entity> ChildEntities{get; set}
The mapping will be
HasManyToMany(x => x.ParentEntities)...
HasManyToMany(x => x.ChildEntities)...
...which I would not suggest to use: How to create NHibernate HasManyToMany relation or Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?, Here are some more details how to use HasManyToMany if ...
I figured out the mappings to be use. I was just confused in the problem at hand since this is self-referencing.
public class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Table("Entity")
Id(x=>x.EntityId).GeneratedBy.Increment();
//solution mapping
HasManyToMany(x => x.ChildEntities)
.Table("EntityLinks")
.ParentKeyColumn("ParentEntityId")
.ChildKeyColumn("ChildEntityId");
HasManyToMany(x => x.ParentEntities)
.Table("EntityLinks")
.ParentKeyColumn("ChildEntityId")
.ChildKeyColumn("ParentEntityId");
}
}
public class EntityLinksMap : ClassMap<EntityLinks>
{
public EntityMap()
{
Table("EntityLinks")
Id(x=>x.EntityId).GeneratedBy.Increment();
References(x=>x.ParentEntityId).Column("ParentEntityId");
References(x=>x.ChildEntityId).Column("ChildEntityId");
}
}
Ok. I have read a lot of similar situations but all of them seems to be more related to one-to-many mappings.
long story short... Here is the code.
public class Roles
{
public virtual int RoleID { get; protected set; }
public virtual Guid RoleGUID { get; protected set; }
public virtual string RoleName { get; protected set; }
public virtual string RoleDescription { get; protected set; }
public virtual IList<User> Users { get; protected set; }
}
public class RolesMap:ClassMap<Roles>
{
public RolesMap()
{
Table("Web_Roles");
Id(x => x.RoleID);
Map(x => x.RoleDescription);
Map(x => x.RoleName);
Map(x => x.RoleGUID);
HasManyToMany(x => x.Users)
.Cascade.All()
.Inverse()
.Table("Web_UserRoles");
}
}
public class User
{
public virtual int UserID { get; protected set; }
public virtual string UserName { get; protected set; }
public virtual string Password { get; protected set; }
public virtual string Email { get; protected set; }
public virtual Guid UserGUID { get; protected set; }
public virtual IList<Roles> Roles { get; protected set; }
}
public class UserMap: ClassMap<User>
{
public UserMap()
{
Table("Web_User");
Id(x => x.UserID);
Map(x => x.UserName);
Map(x => x.Password);
Map(x => x.UserGUID);
Map(x => x.Email);
HasManyToMany(x => x.Roles)
.Cascade.All()
.Table("Web_UserRoles");
}
}
public class UserRoles
{
public virtual int RoleID { get; protected set; }
public virtual int UserID { get; protected set; }
}
So, that's the domain entities and their respectives mappings. Not biggie. Of course, no mapping for UserRoles since it's only a "mapping table".
When i get my User I get the following error.
could not initialize a collection: [FamilyDerm.AUTH.Domain.User.Roles#1][SQL: SELECT roles0_.UserID as UserID1_, roles0_.Roles_id as Roles4_1_, roles1_.RoleID as RoleID1_0_, roles1_.RoleDescription as RoleDesc2_1_0_, roles1_.RoleName as RoleName1_0_, roles1_.RoleGUID as RoleGUID1_0_ FROM Web_UserRoles roles0_ left outer join Web_Roles roles1_ on roles0_.Roles_id=roles1_.RoleID WHERE roles0_.UserID=?]
It doesn't take much to realize that the mapping is converting RoleID from my "mapping entity" UserRoles to Roles_id, which I don't understand why. I am following NHibernate exact "way" but i don't seem to get it straight.
If I replace in SQL Server Roles_id by RoleID, it works like a charm. Obviously I am having either a naming convention issue or a mapping issue itself.
As I write this, i see questions popping out to my right with similar concerns but none of them offer a solution to my problem.
Thank you.
Because you haven't explicitly declared the field names in the Web_UserRoles table nHibernate infers them by adding an _Id to the name of the child collection so
HasManyToMany(x => x.Users)
.Cascade.All()
.Inverse()
.Table("Web_UserRoles");
will infer a field named Users_Id in the Web_UserRoles table and likewise Roles_Id from your HasManyToMany Roles mapping.
Modifying your HasManyToMany definitions to explicitly define the Parent and Child Id columns from your ProductInProduct table should sort your issue:
HasManyToMany(x => x.Users)
.Cascade.All()
.Inverse()
.Table("Web_UserRoles")
.ParentKeyColumn("UserId")
.ChildKeyColumn("RoleId");
and
HasManyToMany(x => x.Roles)
.Cascade.All()
.Table("Web_UserRoles")
.ParentKeyColumn("RoleId")
.ChildKeyColumn("UserId");
Say I have two Entities.
public class Category{
public virtual int Id{get;set;}
public virtual IList<Post> Posts{get;set;}
}
public class Post{
public virtual int Id{get;set;}
public virtual string Title{get;set;}
}
In the Db there's a many-to-many table
CategoryPostRel
CategoryId
PostId
The category Map then looks like this:
public CategoryMap()
{
HasManyToMany(x => x.Posts)
.Table("CategoryPostRel")
.ParentKeyColumn("CategoryId")
.ChildKeyColumn("PostId");
}
Ok, but say I only want the Ids from the Posts. So I change my Category entity to look like this.
public class Category{
public virtual int Id{get;set;}
public virtual IList<int> PostIds{get;set;}
}
So now, how do I get the ids with my mapping as the HasManyToMany maps Entities, not columns right?
Note that I can't change the db at all and the many-to-many table has no unique identifier.
public CategoryMap()
{
HasManyToMany(x => x.PostIds)
.Table("CategoryPostRel")
.ParentKeyColumn("CategoryId")
.ChildKeyColumn("PostId").HowDoIgetTheIds...?
}
You could create an entity that models this relationship CategoryPost and do something like this:
public CategoryMap()
{
HasMany(x => x.CategoryPostIds)
.KeyColumn("CategoryId")
}
public CategoryPostMap
{
CompositeId()
.KeyProperty(x => x.PostId)
.KeyProperty(x => x.CategoryId)
}
This is obviously not an ideal solution but it may work.
I have two classes. One is Order:
public class Order
{
public virtual int Id { get; set; }
public virtual IList<Product> Products { get; set; }
}
The other one is Product:
public class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
They are fluently mapped like this:
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Table("Orders");
Id(x => x.Id, "Id");
HasMany(x => x.Products)
.KeyColumn("OrderId")
.Cascade.All();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Table("Products");
Id(x => x.Id, "Id");
Map(x => x.Name);
}
}
The database does NOT have a not-null constraint on the OrderId column of the Products table.
Problem is: both the order and the products are being persisted, however the products are being persisted with a null value on the OrderId column.
Am I missing something?
instead of HasMany just use Reference
Reference(x => x.Products).Cascade.All();
Inverse on HasMany is an NHibernate term, and it means that the other end of the relationship is responsible for saving.
Well, as strange as it seems, we solved this issue here by re-working our Session management, using it to create an ITransaction in a using statement. Odd, but solved. Thanks everyone!
Try using:
HasMany(x => x.Products)
.KeyColumn("OrderId")
.Inverse()
.Cascade.All();
Which(Inverse()) states that OrderId is on the Products table