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.
Related
I'm new to Fluent NHibernate and C#.NET and struggling with this problem.
I have two tables:
audit:
audit_seq (Primary Key),
user_code (Foreign Key),
audit_date
username:
user_code (Primary Key),
user_name
The audit table contains audit records with a foreign key field (user_code) for the user. The problem is that some of the user records have been deleted, but the audit records for those users remain (so I have orphaned Foreign Keys in the audit table). What I want is a result set with audit_seq, audit_date and user_name (but if orphaned the user_name should be blank).
I am using the following mapping, but believe that I need to change the UserName reference to create an outer join. I am not sure how to do that.
public class AuditMap : ClassMap<Audit>
{
public AuditMap()
{
Id(x => x.AuditSeq).Column("audit_seq").GeneratedBy.Assigned();
Map(x => x.AuditDate).Column("audit_date");
References(x => x.UserName).Column("user_code");
}
}
The table username should be mapped to an Entity (class UserName? I will use the User to distinguish its property UserName). Then it could be used in the Audit as a reference. In C#
public class Audit
{
...
public virtual User User { get; set; }
public virtual string UserName
{
get { return User != null ? User.UserName : string.Empty ;}
}
}
The User mapping
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.UserCode).Column("user_code").GeneratedBy.Assigned();
Map(x => x.UserName).Column("user_name");
...
}
}
Then we can adjust the Audit mapping this way:
public class AuditMap : ClassMap<Audit>
{
public AuditMap()
{
Id(x => x.AuditSeq).Column("audit_seq").GeneratedBy.Assigned();
Map(x => x.AuditDate).Column("audit_date");
References(x => x.User).Column("user_code")
.NotFound.Ignore();
}
}
so now we have Audit having reference to User. It will ignore the fact that some referenced Users are missing (.NotFound.Ignore()). And if there is any, our UserName property will return its name.
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'm creating a model for a website top-menu structure -
I have a MenuObject model:
public class MenuObject
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual List<MenuObject> Children { get; set; }
}
and a mapping:
public mapMenu()
{
Id(x => x.Id)
.Not.Nullable();
Map(x => x.Title)
.Not.Nullable();
HasMany<MenuObject>(x => x.Children)
.AsList();
}
Basically i want to be able to create a "Top Level" Menu item then add some child items to it - in database terms, there should be a ParentId field that contains the ID of the parent Menu Item (if any - this could be null)
I'm struggling to get my head around how this should be defined in my object model. Also once i hav this configured, how would i go about saving children? Would it be something like
public void InsertChild(MenuObject parent, MenuObject child)
{
parent.Children.add(child)
session.SAve(parent)
.....
}
or would I have to save the child independantly and link it to the parent explicitly?
Edit *****
Thanks - so now i have the following in my model:
public virtual MenuObject Parent { get; set; }
public virtual List<MenuObject> Children { get; set; }
and this in the mapping:
HasMany(x => x.Children)
.AsList()
.Inverse()
.Cascade.All()
.KeyColumn("ParentId");
References(x => x.Parent, "ParentId");
I can now add children to parent itms in the following way:
oChild.Parent = oParent;
session.SaveOrUpdate(oParent);
session.Save(oChild);
transaction.Commit();
I think i'm onto a winner! Is that the best way to do it? THanks gdoron
or would i have to save the child independantly and link it to the parent explicitly?
Anyway you have to "link' it to it's parent, If don't want to get exceptions...
You will have to save the child independently only if you don't specify Cascade.Save\All() in the mapping:
HasMany<MenuObject>(x => x.Children)
.AsList().Inverse.Cascade.All(); // Inverse will increase performance.
You have to add a Parent property to connect the "child" to it's "Parent".
It's mapping is:
References(x => x.Parent);
P.S.
You don't have to write Not.Nullable on Id, It's the defaults.
I have a db that I'm trying to model using Fluent NHibernate.
the tables in questions are:
User:
PK Id, Name, FK accessType, FK authorization
AccessType:
PK Id, Name
Authorization:
PK Id, Name
Permission:
PK Id, FK menuId, FK accessId, FK authId
User Entity:
public Users()
{
Permissions = new List<Permissions>();
}
public virtual AccessTypes AccessType { get; set; }
public virtual Authorization Authorization { get; set; }
public virtual string Name { get; set; }
public virtual IList<Permissions> Permissions { get; set; }
Permission Entity:
public class Permissions : EntityWithTypedId<long>
{
public virtual Menus Menu { get; set; }
public virtual AccessTypes AccessType { get; set; }
public virtual Authorization Authorization { get; set; }
}
User Map:
public UsersMap()
{
Table("USERS");
Map(x => x.Name, "NAME");
References<AccessTypes>(x => x.AccessType, "ACCESS_TYPE_ID");
References<Authorization>(x => x.Authorization, "AUTHORIZATION_ID");
Id(x => x.Id, "ID")
.Column("ID")
.GeneratedBy.Assigned();
HasMany<Permissions>(x => x.Permissions)
.KeyColumns.Add("ACCESS_TYPE_ID", "AUTHORIZATION_ID")
.Inverse()
.Cascade.None();
}
Permission Map:
public PermissionsMap()
{
ReadOnly();
Table("PERMISSIONS");
References<Menus>(x => x.Menu, "MENU_ID");
References<AccessTypes>(x => x.AccessType, "ACCESS_TYPE_ID");
References<Authorization>(x => x.Authorization, "AUTHORIZATION_ID");
Id(x => x.Id, "ID")
.Column("ID")
.GeneratedBy.Assigned();
}
I got this error: Foreign key (FK79B2A3E83BA4D9E3:PERMISSIONS [ACCESS_TYPE_ID, AUTHORIZATION_ID])) must have same number of columns as the referenced primary key (USERS [ID])
I need to get a list of permission by checking the user accessType and user authorization.
My question is: How can I map the Permission list in User mapping? Should I Use the ternary association?
Does anyone have any insight on how to do this?
This scenario is not supported. NHibernate has a feature called property-ref which can be used (but should be avoided) on old databases that were designed poorly. However, property-ref only supports referencing one non-primary-key column. Since you are trying to reference two such columns, this will not work.
However, since the permissions are obviously not tied to the user per se, you should not even map them.
You could still have the property for the list in the Users class and fill that with an extra method that simply reads the Permissions using a Where-condition on both columns. Still, I would advise against this. I would write a method like this (code not tested):
public IList<Permissions> GetPermissionsForUser(Users user)
{
return session.QueryOver<Permissions>()
.Where(p => p.Authorization.Equals(user.Authorization))
.And(p => p.AccessType.Equals(user.AccessType)).List();
}
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.