Fluent NHibernate question - nhibernate

Let's say you have two tables, "Users" and "UserRoles". Here's how the two tables are structured (table - columns):
Users - UserID (int)
UserRoles - UserID (int), Role (string)
What I want is for my "User" class in my domain to have an IList of roles. How do I construct my Fluent NHibernate mapping to achieve this?

What you're looking for is a of a set of elements, which in standard hbm mapping is:
<set name="Roles" table="UserRoles">
<key column="UserID" />
<element column="Role" />
</set>
For Fluent NHibernate you can map this like so:
HasMany<string>(x => x.Roles)
.AsElement("Role");
You may need to also specify the key name using WithKeyColumn(string).

FWIW this has change minorly of present day. The current mapping is
HasMany<string>(x => x.Roles)
.Element("Role");

I beleive it would be
public User()
{
Id(x => x.UserID);
HasMany<UserRoles>(x => x.UserRoles).AsBag();
}
You'll also have to make sure you map your UserRoles class as well

This also worked:
HasMany<Role>(u => u.Roles)
.WithTableName("UserRoles")
.Component(role => role.Map(r => r.Name))
.AsList();
You don't need to map Role or UserRoles.
Make sure Role implements IEquatable < Role > ;.

Related

Convert Mapping from XML to Confirmst(Code Mappings)

Hello I am presently converting the xml mappings to code mappings and I am stuck at one place as I am not getting the proper way to convert ManyToOne Mappings.
The XML Mappings are
</many-to-one>
<many-to-one class="MyProject.Activity.Communication, MyProject.Activity" name="Comm">
<column name="CommID" />
</many-to-one>
Now I have this MyProject.Activity.Communication in other solution and don't have any reference in my mapping project. I want to specify class in my code mappings.
ManyToOne(x => x.Comm, map =>
{
map.Column("CommID");
});
How do I specify class in this mapping as the Entity name is referenced so I need to add the class in my code mappings.
The mappings would be using Reflection to fetch the assembly name.
ManyToOne(x => x.Survey, map =>
{
map.Column("SurveyID");
map.Class(Type.GetType("MyProject.Activity.Communication, MyProject.Activity"));
});

mapping (by code - not fluent) an FK column as a list of enums in parent entity

I have an entity class that represents a person and an enum that represents permissions that a person has. I am trying to map this relationship to a database using nhibernate mapping by code without any success.
The code looks like this:
public enum Permissions
{
None = 1,
CanUpdate = 2,
CanInsert = 3,
CanDelete = 4
}
public class Person
{
private ICollection<Permissions> permissions;
public Person()
{
this.permissions = new Collection<Permissions>();
}
public virtual ICollection<Permissions> Permissions
{
get
{
return this.permissions;
}
}
}
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
this.Set(
x => x.Permissions,
m =>
{
m.Access(Accessor.Field);
m.Key(k => k.Column("PersonId"));
m.Table("PersonHasPermission");
},
map => map.Element(
p =>
{
p.Column("PermissionId");
p.Type<NHibernate.Type.EnumType<Permissions>>();
}));
}
}
The database tables look like this:
Person
-----------------------
PersonId (PK, uniqueidentifier, not null)
Name (nvarchar(max), not null)
PersonHasPermission
-----------------------
PersonId (PK, FK, uniqueidentifier, not null)
PermissionId (PK, FK, int, not null)
So, with this configuration I do not get any exceptions but whenever I try to fetch the permissions for a person the collection is always empty even though there is data in the database.
I'm hoping that the code above is explains what I am trying to achieve, but do let know if further clarity is required.
Any help with this would be much appreciated. Thanks in advance.
I am using XML configuration, where I can map even protected or private members. So, surprising for me is that you are not receiving any Exception. Because your Collection does not have any setter (public nor protected). I would expect something like this:
NHibernate.PropertyNotFoundException: {"Could not find a setter for property 'Permissions' in class 'Person'"}
But maybe fluent is able to translate your mapping from Property to field.
I tried to test your code with a public setter or field (both shown below) and it worked. there is my snippet of xml mapping:
<!-- public or protected setter -->
<bag name="Permissions" inverse="false" lazy="true" table="PresonHasPermission" cascade="all">
<key column="PersonId" />
<element type="Permissions" column="PermissionId" />
</bag>
<!-- accessing the field -->
<bag name="permissions" inverse="false" lazy="true" table="PresonHasPermission" cascade="all"
access="field" >
<key column="PersonId" />
<element type="Permissions" column="PermissionId" />
</bag>
(Leaving your class definition unchanged when mapping to field)

FluentNhibernate IDictionary<Entity,ValueObject>

I had a mapping for a IDictionary<StocksLocation,decimal> property, this was the mapping:
HasMany<StocksLocation>(mq => mq.StocksLocation)
.KeyColumn("IDProduct")
.AsEntityMap("IDLocation")
.Element("Quantity", qt => qt.Type<decimal>());
Now i changed from decimal to a Value Object: Quantity.
Quantity has two properties, decimal Value and Unit Unit (where Unit is an enum).
I now have to map IDictionary<StocksLocation,Quantity>, how can i achieve this?
Thanks in advance
Option 1: Map it as an Entity
I'm guessing that your table looks similar to this:
CREATE TABLE Quantity (
ID int NOT NULL,
IDProduct int NOT NULL,
IDLocation int NOT NULL,
Value decimal(18,2) NOT NULL,
Unit int NOT NULL,
PRIMARY KEY (ID),
FOREIGN KEY (IDProduct) REFERENCES Product (ID),
FOREIGN KEY (IDLocation) REFERENCES StocksLocation (ID),
UNIQUE KEY (IDProduct, IDLocation)
);
Go ahead and map Quantity as an entity class:
public class QuantityMap : ClassMap<Quantity>
{
public QuantityMap()
{
Id(x => x.Id);
References(x => x.Product, "IDProduct");
References(x => x.Location, "IDLocation");
Map(x => x.Value);
Map(x => x.Unit);
}
}
... and then change the Product.StocksLocation mapping to:
HasMany<StocksLocation, Quantity>(mq => mq.StocksLocation)
.KeyColumn("IDProduct")
.AsMap(x => x.Location);
Option 2: Map it as a Component
Because you commented that you'd rather not map Quantity as an entity, let's consider how we would map this as a component instead. The *.hbm.xml mapping for the Product.StocksLocation dictionary would look like this:
<map name="StocksLocation" table="Quantity">
<key column="IDProduct" />
<index-many-to-many column="IDLocation" class="YourNamespace.StocksLocation, YourAssembly" />
<composite-element class="YourNamespace.Quantity, YourAssembly">
<property name="Unit" type="YourNamespace.Unit, YourAssembly" />
<property name="Value" type="System.Decimal, mscorlib" />
</composite-element>
</map>
How do we do this with FluentNHibernate? As far as I know, there isn't a method for doing this in the trunk, so you have a few options:
Gabriel Schenker implemented a HasManyComponent method. He has a link to the source code for his project, but I don't know whether that source includes the changes he made to FluentNHibernate.
If the source for his changes are not available, feel free to implement your own modifications to FluentNHibernate and submit them back to the community via Github.
If that sounds like too much trouble, FluentNHibernate has an ultimate fallback when all else fails. It allows you to mix and match various mapping methods. Auto-map some of your classes, write ClassMap classes for others, and write a *.hbm.xml file for any classes that can't be mapped with FluentNHibernate.

Fluent Nhibernate Order-By on Collection

If I have a collection mapped in Fluent NHibernate and I want to apply an order to that collection how do I do that?
Eg:
HasMany(x => x.PastDates)
.AsBag().Cascade
.SaveUpdate()
.KeyColumnNames.Add("EventId")
.Where(e => e.DateFrom < DateTime.Now.Date)
.Inverse();
I'm looking for the equivalent of the order-by attribute in HBM files.
Thanks
Fluent NHibernate now has an OrderBy method which you can use:
HasMany(x => x.PastDates)
.AsBag().Cascade
.SaveUpdate()
.KeyColumnNames.Add("EventId")
.Where(e => e.DateFrom < DateTime.Now.Date)
.Inverse()
.OrderBy("ColumnName DESC");
It appears that the "order-by" attribute is not in the FluentNHibernate API. I don't see an issue for it so this may be a conscious omission. You should be able to add it using SetAttribute but this user was unable to get it to work.
HasMany(x => x.PastDates)
.AsBag().Cascade
.SaveUpdate()
.KeyColumnNames.Add("EventId")
.Where(e => e.DateFrom < DateTime.Now.Date)
.Inverse()
.SetAttribute("order-by", "column_name");
Be aware that setting order-by may change the collection type that NHibernate uses; however this does not apply for bags.

NHibernate Map many-to-many join table

My database structure looks something like this:
Person
Id
Name
FieldA
FieldB
Phone
Id
Number
PersonPhone
PhoneId
PersonId
IsDefault
My NHibernate mappings for the Person and Phone objects are straight forward, its the PersonPhone I'm having difficult with. I want to have a collection of PersonPhone objects as a property of Person which will allow me to have the Phone number of a person and be able to tell which is the "default" or primary phone number for a person.
ideally Id like my PersonPhone object to look like this:
public class PersonPhone
{
public virtual Person Person { get; set; }
public virtual Phone Phone { get; set; }
public virtual bool IsDefault { get; set; }
}
so far my NHibernate mapping for this table looks like the following:
<class name="PersonPhone" table="PersonPhone">
<composite-id>
<key-property name="Person" column="PersonId" />
<key-property name="Phone" column="PhoneId" />
</composite-id>
<property name="IsDefault" column="IsDefault"/>
</class>
but when NHibernate compiles my mappings I get an error saying:
Could not compile the mapping document: MyApp.Entities.PersonPhone.hbm.xml. NHibernate.MappingException : Could not determine type for: MyApp.Entities.Person, MyApp.Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, for columns: NHibernate.Mapping.Column(PersonId)
Any ideas on how this should be mapped?
The answer is to use the element in your composite key rather than the key-property
<class name="PersonPhone" table="PersonPhone">
<composite-id>
<key-many-to-one name="Person" column="PersonId"></key-many-to-one>
<key-many-to-one name="Phone" column="PhoneId"></key-many-to-one>
</composite-id>
<property name="IsDefault" column="IsDefault"/>
</class>
I think It is more proper to consider Many-to-Many relationship between Phone and Peron entities and get rid of PersonPhone entity.
To set-up the same mapping with Fluent NHibernate, do this:
public class PersonPhoneMap : ClassMap<PersonPhone>
{
public PersonPhoneMap()
{
CompositeId()
.KeyReference(p => m.Person)
.KeyReference(p => m.Phone);
References(p => p.Person)
.Column("PersonID");
References(m => m.Phone)
.Column("PhoneID");
Map(p => p.IsDefault)
.Column("IsDefault");
}
}