How do I use Fluent NHibernate ReferencesAny mapping? - nhibernate

I've read a lot about Fluent NHibernate's ReferencesAny but I haven't seen a complete example. I think I understand most of it, but there is one part I don't get. In the class mapping ReferencesAny(x => x.MemberName) is used to define the relationship to the one or more referenced classes. What is MemberName? How is it defined and how is it used to create the data in the database.
I have three tables, the records in one table can reference records in one of the other two tables. The first two are auto mapped, so the Id field is not specifically defined.
public class Household
{
public virtual string Name { get; set; }
public virtual IList<AddressXref> AddressXrefs { get; set; }
}
public class Client
{
public virtual string Name { get; set; }
public virtual IList<AddressXref> AddressXrefs { get; set; }
}
I'm not sure if the AddressXref table can be auto mapped. If so I need to find out how to do that too. For now I'll do it the conventional way with Fluent.
public class AddressXref
{
public virtual int id { get; set; }
public virtual string TableName { get; set; }
public virtual Int32 Table_id { get; set; }
public virtual string Street { get; set; }
public virtual string City { get; set; }
}
class AddressXrefMap : ClassMap<AddressXref>
{
public AddressXrefMap()
{
Table("AddressXref");
Id(x => x.id);
Map(x => x.TableName);
Map(x => x.Table_id);
Map(x => x.Street);
Map(x => x.City);
ReferencesAny(x => x.TableRef)
.AddMetaValue<Household>(typeof(Household).Name)
.AddMetaValue<Client>(typeof(Client).Name)
.EntityTypeColumn("TableName")
.EntityIdentifierColumn("Table_id")
.IdentityType<int>();
}
}
The part I need help with is how is the TableRef, referred to in ReferencesAny(), member of AddressXref defined in the class?
Also, how it is used in the code when creating data records? I image it will be similar to this:
Household Household = new Household();
Household.Name = "Household #1";
AddressXref AddrXref = new AddressXref();
AddrXref.Street1 = "123 Popular Street";
AddrXref.City = "MyTown";
AddrXref.TableRef = Household;
Session.SaveOrUpdate(AddrXref);
I love using Fluent with NHibernate, but I'm still amazed at the learning curve. :)
Thanks,
Russ

since both Household and Client don't share a base class other than object you have to declare it as this:
public class AddressXref
{
public virtual int Id { get; set; }
public virtual object TableRef { get; set; }
public virtual string Street { get; set; }
public virtual string City { get; set; }
}
and test it like this
if (addrXref.TableRef is HouseHold)
// it's a household

Related

NHibernate One-To-One Relationship

I have the following Domain Model(s):
public class WriteOffApprovalUser
{
public virtual string UserName { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
public virtual string EmployeeID { get; set; }
public virtual string EmployeeStatusCode { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string PreferredName { get; set; }
public virtual string JobTitle { get; set; }
public virtual string Division { get; set; }
public virtual string Department { get; set; }
public virtual string Location { get; set; }
public virtual string City { get; set; }
public virtual string DeskLocation { get; set; }
public virtual string MailID { get; set; }
public virtual string Phone { get; set; }
public virtual string Fax { get; set; }
public virtual string SecCode { get; set; }
public virtual string UserId { get; set; }
public virtual string SupervisorID { get; set; }
}
These are my Fluent Mappings
public class WriteOffApprovalUserMap : ClassMap<WriteOffApprovalUser>
{
public WriteOffApprovalUserMap()
{
//Schema("LEGAL");
Table("WRITEOFF_APPROVAL_USER");
Id(x => x.UserName).Column("USER_NAME");
HasOne(x => x.Employee).PropertyRef("UserId");
}
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
// Table Name
//Schema("ADP_FEED_OWNER");
Table("ADP_EMPLOYEE");
// Primary Key
Id(x => x.EmployeeID).Column("EMPLID");
// Mappings
Map(x => x.UserId).Column("USER_ID");
Map(x => x.FirstName).Column("FIRST_NAME");
Map(x => x.LastName).Column("LAST_NAME");
Map(x => x.PreferredName).Column("PREFERRED_NAME");
}
}
Here is my Query:
var results = new Repository<WriteOffApprovalUser>(session)
.Query()
.ToList();
This is the SQL it is generating, and I was expecting a JOIN instead.
select writeoffap0_.USER_NAME as USER1_1_ from WRITEOFF_APPROVAL_USER writeoffap0_
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
SELECT employee0_.EMPLID as EMPLID0_0_, employee0_.USER_ID as USER2_0_0_, employee0_.FIRST_NAME as FIRST3_0_0_, employee0_.LAST_NAME as LAST4_0_0_, employee0_.PREFERRED_NAME as PREFERRED5_0_0_ FROM ADP_EMPLOYEE employee0_ WHERE employee0_.EMPLID=:p0;
Now there are four rows in the database, and the right data is coming back, but I would not expect five separate SQL statements to do this.
You need to eager load/fetch the Employee entity to avoid the behavior that you are seeing which is often referred to as the SELECT N+1 problem. In order to do this you have two options:
Option 1. Eager load in the mapping meaning when you query the WriteOffApprovalUser entity, it will always perform a JOIN to the Employee table. Note: This may sound like what you want but be cautious as you will force all developers who ever work with this entity to be stuck with this design decision until the end of time. You'll have to ask yourself, would I ever want to query the WriteOffApprovalUser table and not perform a JOIN to the Employee table. If the answer is yes, then don't force the eager loading in the mapping file.
To have the Employee automatically fetched, change your HasOne code in the mapping to look something like this:
HasOne(x => x.Employee).PropertyRef("UserId").Not.LazyLoad().Fetch.Join();
Option 2. Perform the eager loading in the query. I noticed that you are using some kind of Repository of T pattern so you may have to modify it to handle eager loading. Typical eager loading using NHibernate's built in LINQ Query<T> class in the NHibernate.Linq namespace looks something like this:
var results = new session.Query<WriteOffApprovalUser>()
.Fetch( x => x.Employee ) // This will tell NHibernate to perform a JOIN to the Employee table
.ToList();

How to use composite Ids in one-to-many mappings in fluent nhibernate?

I got a scenario where a composite Id uniquely identifies an entity. I defined the MSSQL to have a multiple primary key on those fields. In addition I would like an auto-incremented id to be used for referencing a one-to-many relationship. Here's the schema:
public class Character
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Region Region { get; set; }
public virtual string Realm { get; set; }
public virtual IList<CharProgression> Progression { get; set; }
}
public class CharProgression
{
public virtual int Id { get; set; }
public virtual Character Character { get; set; }
public virtual Stage Stage { get; set; }
public virtual int ProgressionPoints { get; set; }
public virtual int NumOfSaves { get; set; }
}
public class Stage
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
}
The mappings look like this:
class CharacterMap : ClassMap<Character>
{
public CharacterMap()
{
Table("characters");
Id(ch => ch.Id, "id").GeneratedBy.Identity().Not.Nullable();
CompositeId().KeyProperty(ch => ch.Region, "region")
.KeyProperty(ch => ch.Realm, "realm")
.KeyProperty(ch => ch.Name, "name");
HasMany<CharProgression>(ch => ch.Progression).Inverse().Cascade.All();
}
}
class CharProgressionMap : ClassMap<CharProgression>
{
public CharProgressionMap()
{
Table("char_progression");
CompositeId().KeyReference(cprog => cprog.Character, "char_id",
.KeyReference(cprog => cprog.Stage, "stage_id");
Id(cprog => cprog.Id, "id").GeneratedBy.Identity().Not.Nullable();
Map(cprog => cprog.ProgressionPoints, "progression_points");
Map(cprog => cprog.NumOfSaves, "num_of_saves");
}
}
public class StageMap : ClassMap<Stage>
{
public StageMap()
{
Table("stages");
Id(st => st.Id, "id").GeneratedBy.Identity().Not.Nullable();
Map(st => st.Name, "name");
Map(st => st.Description, "description");
}
}
Now, the thing is that I would like to use SaveOrUpdate() on a character and use the composite id for the update, since the character uniqueness is defined by those 3 fields - region, realm, name.
However, when I am referencing the Character from CharProgression, I don't want to use the composite Id as I don't want the char_progression table to hold 3 fields for identifying a character, a simple Id is enough... which is why I also defined an IDENTITY id on the Character entity.
Is what i'm trying possible? or is there another way to achieve this?
Thanks :)

FluentNhibernate Eager loading Many-To-Many child objects

I try to query data using FluentNhibernate and I get this error: "Sequence contains more than one matching element"
Here are my classes and mappings:
public class Course
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Instructor> Instructors { get; set; }
}
public class Instructor
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual ImageData Portrait { get; set; }
public virtual ImageData PortraitThumb { get; set; }
public virtual IList<Course> TeachingCourses { get; private set; }
}
public class ImageData : Entity
{
public virtual int Id { get; private set; }
public virtual byte[] Data { get; set; }
}
public class CourseMap : ClassMap<Course>
{
public CourseMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Instructors)
.Cascade.All()
.Table("CourseInstructor");
}
}
public class InstructorMap : ClassMap<Instructor>
{
public InstructorMap()
{
Id(x => x.Id);
Map(x=> x.Name);
References(x => x.Portrait)
.Nullable()
.Cascade.All();
References(x => x.PortraitThumb)
.Nullable()
.Cascade.All();
HasManyToMany(x => x.TeachingCourses)
.Cascade.All()
.Inverse()
.Table("CourseInstructor");
}
}
public class ImageDataMap : ClassMap<ImageData>
{
public ImageDataMap()
{
Id(x => x.Id);
Map(x => x.Data);
}
}
Then I try to get data using below code:
var course = session.CreateCriteria(typeof(Course))
.SetFetchMode("Instructors", FetchMode.Eager)
.SetFetchMode("Instructors.Portrait", FetchMode.Eager)
.SetFetchMode("Instructors.PortraitThumb", FetchMode.Eager)
.List<Course>();
But I get the following error: "Sequence contains more than one matching element"
Also, when I try this
var course = session.CreateCriteria(typeof(Course))
.SetFetchMode("Instructors", FetchMode.Eager)
.SetFetchMode("Instructors.Portrait", FetchMode.Eager)
.SetFetchMode("Instructors.PortraitThumb", FetchMode.Eager)
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Course>();
No error occurs but I get duplicate Instructor objects.
I did try below posts and some others as well. But it doesn't help.
NHibernate Eager loading multi-level child objects
Eager Loading Using Fluent NHibernate/Nhibernate & Automapping
FluentNhibernate uses a bag-mapping for many-to-many relations, if the mapped property is of type IList.
A bag mapping has a few major drawbacks Performance of Collections / hibernate. The one that currently bites you is that NH does not permit duplicate element values and, as they have no index column, no primary key can be defined.
Simply said NH does not know to which bag do they belong to when you join them all together.
Instead of a bag I would use a indexed variant a set, assuming that an Instructor does not has the same persistent Course assigned twice.
You can fix your query results by amending your domain classes, this tells FluentNhibernate to use a set instead of a bag by convention:
public class Course
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual Iesi.Collections.Generic.ISet<Instructor> Instructors { get; set; }
}
public class Instructor
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual ImageData Portrait { get; set; }
public virtual ImageData PortraitThumb { get; set; }
public virtual Iesi.Collections.Generic.ISet<Course> TeachingCourses { get; private set; }
}
In addition you can amend your mapping by using .AsSet(). FluentNHibernate: What is the effect of AsSet()?

Fluent NHibernate map 2 identical classes to one table, first-lazy on, second-lazy off

So, I need to map my class to database table, but sometimes I need lazy loading to be on, sometimes to be off.
Example:
I made duplicates of these two classes described below, and I map them using FNH but with lazy loading on in original, and off in duplicate.
http://i.stack.imgur.com/goG30.png
Basically, I want to be able to get a team from DB that has all lists with TeamMembers fetched but those members should not have all their teams and everything else, just plain info about a TeamMember. Also when i get an TeamMember from DB, I want all its teams to contain only plain info's.
"http://i.stack.imgur.com/7OkyD.png" ->they don't allow new users to post pics or links.
So if I have only those two classes, then if lazy loading is turned on on any side, one of the situations explained before is not satisfied. If lazy is off on both sides I get a whole bunch of data that I don't need nor want.
At first original and duplicate had same names and were in different packages, but I got an exception that mapping was ambiguous. If there is a way for this to work, that would be ideal.
Is there a way to do this?
I couldn't find an answer so I changed name of duplicate to be NameOfOriginal+Lite.
Mapping was parsed but when I wanted to get a team from database, I get an exception:
{"ORA-00904: \"SUPERVIZ_\".\"TEAMMEMBERLITE_ID\": invalid identifier\n"}
So, apparently FNH reads a class name and ads an "_ID" and uses that as a ID for my duplicate class and that causes the problem. I tried with
.ParentKeyColumn("")
.ChildKeyColumn("")
but no success.
I hope I didn't confuse you too much :)
[DataContract]
public class Team
{
[DataMember]
public virtual int Team_id { get; private set; }
[DataMember]
public virtual String Name { get; set; }
[DataMember]
public virtual String Description { get; set; }
[DataMember]
public virtual TeamMember Deputy { get; set; }
[DataMember]
public virtual TeamMember Leader { get; set; }
[DataMember]
public virtual IList<TeamMember> TeamMembers { get; set; }
[DataMember]
public virtual IList<TeamMember> Supervizors { get; set; }
...
}
[DataContract]
public class TeamMember
{
[DataMember]
public virtual int TeamMember_id { get; set; }
[DataMember]
public virtual String First_name { get; set; }
[DataMember]
public virtual String Last_name { get; set; }
[DataMember]
public virtual String Sid { get; set; }
[DataMember]
public virtual IList<Team> SupevisingTeams { get; set; }
[DataMember]
public virtual IList<Team> LeaderInTeams { get; set; }
[DataMember]
public virtual IList<Team> DeputyInTeams { get; set; }
[DataMember]
public virtual IList<Team> MemberInTeams { get; set; }
...
}
You can map a class multiple times if you give the mapping an entity name. You will need to use ClassMap for this rather than auto mapping.
Using Fluent NHibernate:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
public class FooMap1 : ClassMap<Foo>
{
public FooMap1()
{
Table("Foo");
EntityName("Foo");
Id(x => x.Id);
Map(x => x.Name);
}
}
public class FooMap2 : ClassMap<Foo>
{
public FooMap2()
{
Table("Foo");
EntityName("Bar");
Id(x => x.Id);
Map(x => x.Name);
}
}
You need to specify the entity name when building the query to make nhibernate use the correct mapping:
using (var session = _sessionFactory.GetCurrentSession())
{
return session.CreateCriteria("Bar")
.Add(Restrictions.Eq("Name", "Andrew"))
.List<Foo>();
}

How do I create this reference mapping in Fluent NHibernate?

Using Fluent NHibernate I need a clue how to map my Invoice class.
public class Buyer
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string TaxRegNo { get; set; }
// .... more properties....
}
public class Invoice
{
public virtual int Id { get; set; }
public virtual int IdBuyer { get; set; }
public virtual Buyer Buyer { get; set; }
// ....more properties
}
The problem is that I want to have in Invoice class:
BuyerId - just an integer ID for reference and foregin key relationship
a copy of almost all buyer properties (its accounting document and properties cannot be changed after confirmation) - as component
I tried to this using following mapping but it doesn't work
public InvoiceMap()
{
Id(x => x.Id);
References(x => x.IdBuyer);
Component(x => x.Buyer, BuyerMap.WithColumnPrefix("buyer_"));
// ....more properties
}
You would normally not map both the foreign key and the child object. If you do map both, then do this in the mapping (or similar):
References(x => x.Buyer);
Map(x => x.IdBuyer).Column("BuyerId").Not.Insert().Not.Update();
Then you don't double up on the column name in SQL statements, which causes errors around mismatched numbers of parameters.