I have a table structure something like this
table Employees
EmployeeID
EmployeeLogin
EmployeeCustID
table Customers
CustomerID
CustomerName
What i would like is to map the structure above to one single class named:
Class Employee
EmployeeID
EmployeeLogin
EmployeeName
How do i do that with fluent nhibernate ?
I don't know if it is possible with fluent, but in xml you use the join element:
simplified:
<class name="Employee" table="Customers" >
<id name="CustomerID" .../>
<property name="CustomerName"/>
<join table="Employees">
<key column="EmployeeCustID" />
<property name="EmployeeLogin" />
</join>
</class>
See this post by Ayende
I agree with Frans above but if you're stuck with someone else's code and have to use the existing structure, you can can use WithTable.
public class EmployeesMap : ClassMap<Employees>
{
public EmployeesMap()
{
Id(x => x.EmployeeId);
Map(x => x.EmployeeLogin);
WithTable("Customers", join =>
{
join.Map(m => m.EmployeeName, "CustomerName");
join.WithKeyColumn("EmployeeCustID");
});
}
}
[DataContract(IsReference = true)]
public class Employees
{
[DataMember]
public virtual int EmployeeId { get; set; }
[DataMember]
public virtual string EmployeeLogin { get; set; }
[DataMember]
public virtual string EmployeeName { get; set; }
}
I have not tried this since Fluent NHibernate went to 1.0 so my syntax may be incorrect. I'm pretty sure this will only work if Customer.CustomerId is a foreign key to Employee.
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.EmployeeId);
Map(x => x.EmployeeLogin);
Table("Customer", m =>
{
m.Map(x => x.EmployeeName, "CustomerName");
});
}
}
Is EmployeeCustID unique? If not, this is never going to work, as you then try to cram two different entity types into 1. Also, with your structure, how do you want to save an instance? -> the CustomerID isn't known, so you can't save such an entity.
IMHO it's better to simply keep Customer as a related entity to Employee, as (I assume) the EmployeeCustID is used to link a Customer entity to an Employee entity if the employee is also a customer, which means 'customer' is just a role for employee and therefore optional and changeable and thus should be a separate entity.
Related
I have following Ticket class:
public class Ticket {
public virtual int Id { get; set; }
public virtual Type Type { get; set; }
public virtual Status Status { get; set; }
public virtual State State { get; set; }
}
that maps to a table in legacy database with the following mapping:
public TicketMap()
{
Table("TICKET");
LazyLoad();
Id(x => x.Id)
.GeneratedBy.TriggerIdentity()
.Column("ID")
.Access.Property()
.Not.Nullable();
References(x => x.Type)
.Class<Type>()
.Cascade.None()
.LazyLoad()
.Columns("TYPE_ID");
References(x => x.Status)
.Class<Status>()
.Cascade.None()
.Columns("STATUS_ID", "TYPE_ID");
References(x => x.State)
.Class<State>()
.Cascade.None()
.LazyLoad()
.Columns("STATE_ID", "TYPE_ID");
}
The problem is that when I try to save a Ticket with State and Status NHibernate doesn't know which TYPE_ID to use and fails to save the entity.
Both State and Status classes have composite keys (which I know is a bad practice but, as mentioned above, it's legacy database and I'm not able to create surrogate keys).
I there a way to make this reference work?
One possible way is to make these properties read-only. In xml configuration (xml mapping is world I know) it would look like this (the same for other two ref types: Type, State):
<many-to-one name="Status" insert="false" update="false" >
<column name="STATUS_ID" />
<column name="TYPE_ID" />
</many-to-one>
...
And then create protected artificial properties
<property name="StatusId" column="STATUS_ID" insert="true" update="true" />
<property name="StateId" column="STATE_ID" /><!-- above insert and update attr are redundant -->
<property name="TypeId" column="TYPE_ID" />
These properties should be declared as protected on the Ticket entity. With some internal logic, they can be set to proper values based on selected State, Status or Type.
What we get, is full access to query engine, because all 3 reference types can be used for filtering. And Insert/update will work as well, 'cause NHibernate exactly knows which value to use.
I have entity house which have besides other properties list of images.
Every image will be uploaded as a single action, one by one using js crop techniques.
Updated:
So one house can have many images.
public House
{
public Guid Id {get; set;}
....
List<Image> Images {get; set;}
}
public class Images
{
public House House {get; set;}
public string Path {get;set;}
public string Name {get;set;}
public string Description {get;set;}
}
My db tables are following:
House
Id
Name, ...
On this side I don't have relation to the Image table
Image table
Id
HouseId
Path
Name
Description
Is this approach ok?
How to map these objects using nhibernate orm?
Thanks
Declare your entities, and then in mapping files just associate Image HouseId with entity of House class. You should have foreign key in db. As xml answer already here, I'd add fluent nhibernate way.
public class House
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
....
}
public class Image
{
public virtual int Id { get; set; }
public virtual House HouseEntity { get; set; }
....
}
public class HouseMap : ClassMap<House>
{
public HouseMap()
{
Table("House");
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name);
.....
}
}
public class ImageMap : ClassMap<Image>
{
public ImageMap()
{
Table("Image");
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.House);
//References(x => x.House, "foreignKeyName"); There is also way to reference with specifying foreign key field
Map(x => x.Name);
....
}
}
House.hbm.xml:
<bag name="Images" cascade="all-delete-orphan" inverse="true">
<key column="HouseId" />
<one-to-many class="Image" />
</bag>
Image.hbm.xml:
<id type="int" column="Id">
<generator ... />
</id>
<many-to-one name="House" column="HouseId" cascade="none" />
Since you have a primary key in the images table and no Id property in your domain object, the id-mapping should be without the name-attribute.
I want to combine table-per-class and table-per-hierarchy strategies using fluent nhibernate or nhibernate itself(I mean hbm files), but I don't know how. I prefer fluent over hbm but if it's impossible, then hbm is also fine. I tested this by introducing Entity as ClassMap and all other as SubClassMap in fluent but then in hbm files generated by fluent, Entity was a class and all other were joined-classes which is not what I want. I will describe the problem in more detail below.
Class hierarchy:
public class Entity
{
public int ID { get; set; }
public string Name { get; set; }
}
public abstract class Person : Entity
{
public string Phone { get; set; }
}
public class SystemUser : Person
{
public string Password { get; set; }
}
I want to have one table for entity and one for person and all kinds of it(all its subclasses).I mean I want to use table-per-class strategy for Entity and table-per-hierarchy strategy for Person and SystemUser classes. Database structure is something like this:
EntityTable(ID(PK),Name)
PersonTable(EntityID(PK,FK),Phone,Password)
any help appreciated.
if EntityTable Id is not database generated (which is discouraged by NH anyways) you can use the trick
public PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("PersonTable");
Id(p => p.Id, "EntityID").GeneratedBy.HiLo("100");
DiscriminateSubClassesOnColumn("PersonType");
Map(x => x.Phone);
Join("EntityTable", join =>
{
join.KeyColumn("ID");
join.Map(p => p.Name);
});
}
}
public SystemUserMap : SubclassMap<SystemUser>
{
public SystemUserMap()
{
Map(x => x.Password);
}
}
I am using PropertyRef for one of my References properties. With LazyLoad() it still does a Select and loads the User entity, even though I never "hit" the SalesPerson property.
Order Mapping
Id(x => x.Id).GeneratedBy.Native();
References(x => x.SalesPerson)
.LazyLoad()
.PropertyRef(x => x.Username)
.Column("rsm");
Map(x => x.Title);
Order Class
public class Order : BaseEntity
{
...
public virtual User SalesPerson { get; set; }
public virtual string Title { get; set; }
...
}
User Mapping
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.Username).Column("login");
User Class
public class User : BaseEntity
{
public virtual string Username { get; set; }
...
}
Generated Order Mapping
<many-to-one class="Project.User" lazy="proxy" name="SalesPerson" property-ref="Username">
<column name="rsm" />
</many-to-one>
Executing Code
var order = session.Get<Order>(1);
Console.WriteLine(order.Title);
Is there anyway to prevent the Select to load the User entity when I'm not using the User entity?
Has to do with property-ref see
NHibernate creates proxy via session.Load(), but not via Linq or Criteria API
And not that you asked, but also consider that many-to-one with proxy does not allow you to do type-checking if you subclass User, see
http://ayende.com/Blog/archive/2010/01/28/nhibernate-new-feature-no-proxy-associations.aspx
I don't think this is a bug in NHibernate. It depends on your mapping.
First, remember that the reference map will join the key (primary key and foreign key) between 2 mapping tables. To prevent SELECT + 1, just ignore the key joint.
References(x => x.SalesPerson)
.LazyLoad()
.PropertyRef(x => x.Username)
WithForeignKeyName("none") //disable key join.
I'm writing a homework for my RDBMS class, I need to perform CRUD operations on quite simple domain, which is cyber sport championship.
Students are required to use ADO.NET. My question is how can I solve bidirectional relationship, for example 1:m (every championship has many matches, but every match belongs to only one exact championship)? It seems to me that there must be some technique for that.
And the most interesting part for me is - how does ORM like EF or NHibernate solve this situation?
In NHibernate, it is quite simple and straight-forward. Here's how the domain classes would look, followed by fluent mappings. This assumes you would use NHibernate to generate your schema. If you are mapping a legacy database, it is simple to set the column names and table names used.
public class Championship {
public virtual int Id { get; set; }
public virtual IList<Match> Matches { get; set; }
}
public class Match {
public virtual int Id { get; set; }
public virtual Championship Champioship { get; set; }
}
public class ChampionshipMap : ClassMap<Championship> {
public ChampionshipMap() {
Id(x => x.Id);
HasMany(x => x.Matches);
}
}
public class MatchMap : ClassMap<Match> {
public MatchMap () {
Id(x => x.Id);
References(x => x.Championship);
}
}
Have a look at Davy Brions Blog about building your own Data Access Layer. He talks about all those sort of challenges.
For something like many-to-many with Hibernate, you define the relationship. Here's an example (reference is here:
<class name="Person">
<id name="id" column="personId">
<generator class="native"/>
</id>
<set name="addresses" table="PersonAddress">
<key column="personId"/>
<many-to-many column="addressId"
class="Address"/>
</set>
</class>
<class name="Address">
<id name="id" column="addressId">
<generator class="native"/>
</id>
<set name="people" inverse="true" table="PersonAddress">
<key column="addressId"/>
<many-to-many column="personId"
class="Person"/>
</set>
</class>
From the database side itself, for many-to-many relationship you will usually have a link table.
So we'd have:
PERSON
ADDRESS
PERSON_ADDRESS
The PERSON_ADDRESS table would contain person_id and address_id to link the two entities together. So one person could have many addresses, and a given address could potentially belong to more than one person or company.
For a 1:m relationship, it's good enough to have this:
PERSON
ADDRESS
In address, you would have the person_id column, but there could be many address records for a given person_id, giving you the 1:m capability.
For example in DataObjects.Net you can write following to get automatically associated Championship.Matches entity set and Match.Championship persistent field.
[HierarchyRoot]
public class Championship : Entity
{
[Field, Key]
public int Id { get; set; }
[Field, Association(PairTo="Championship")]
public EntitySet<Match> Matches { get; private set; }
}
[HierarchyRoot]
public class Match : Entity
{
[Field, Key]
public int Id { get; set; }
[Field]
public Championship Championship { get; set; }
}