fluent nhibernate - Many to Many mapping with attribute - c#

I have 2 master tables which are linked by a map table as below
User [UserId,Name]
Resource [ResourceId,Name]
UserResourceMap [UserId,ResourceId,AccessLevel]
How would the Resource and User ClassMap with AccessLevel as a resource attribute look?
My Domain classes look like this
public class User
{
public virtual int UserId { get;protected set; }
public virtual string Name { get;set; }
}
public class Resource
{
public virtual int ResourceId { get;protected set; }
public virtual string Name { get;set; }
public virtual string AccessLevel { get;set; }//Issue-populate this using fluent
}
How can I use fluent to map the accessLevel attribute in the below code.
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("User");
Id(x => x.Key);
Map(x=>x.Name);
}
}
public class ResourceMap : ClassMap<Resource>
{
public ResourceMap()
{
Table("Resource");
Id(x => x.Key);
Map(x=>x.Name);//Need some Map Here to make a hasManyToMany Map with attribute
}
}

Your domain model does not seem to match your database model - the Resource class has the property AccessLevel (i.e. one AccessLevel per Resource) but in the DB model AccessLevel is a column on the map table (i.e. one AccessLevel per User-Resource relation).
Assuming the DB model is the correct model one (fairly straightforward) way of mapping this would be to introduce a class like this.
public class UserResource {
public virtual int UserResourceId { get; protected set; }
public virtual User User { get; set; }
public virtual Resource { get; set; }
public virtual string AccessLevel { get; set; }
}
and map it in this way:
public class UserResourceMap : ClassMap<UserResource> {
public UserResourceMap() {
Table("UserResourceMap");
Id(x => x.UserResourceId);
References(x => x.User).UniqueKey("UniqueUserAndResource");
References(x => x.Resource).UniqueKey("UniqueUserAndResource");
Map(x => x.AccessLevel);
}
}
If you want bidirectional associations you could also add a Collection property on User and/or Resource and map these with HasMany(...).Inverse(). Of course, this kind of mapping would introduce a new UserResourceId column in the UserResourceMap table (using a composite key consisting of User and Resource would mitigate that).
Another solution would be to add an EntityMap association. If the association is owned by User it would be a Dictionary<Resource, string> property. Something like this might do the trick:
public class User {
public virtual int UserId { get; protected set; }
public virtual string Name { get; set; }
public virtual Dictionary<Resource, string> Resources { get; set; } // Resource -> AccessLevel
}
public class UserMap : ClassMap<User> {
public UserMap() {
Table("User");
Id(x => x.UserId);
Map(x => x.Name);
HasMany<Resource, string>(x => x.Resources).AsEntityMap().Element("AccessLevel");
}
}

As you've correctly identified in your database schema, this isn't a pure many-to-many relationship - it's two one-to-many relationships as the intermediate table has an attribute (the access level).
I therefore think your domain is missing an entity - there doesn't appear to be any relationship in your model between a user and the resources they can access.
How about something like this:
public class User
{
public virtual int Id { get;protected set; }
public virtual string Name { get;set; }
public virtual ICollection<UserResource> UserResources { get; set;}
}
public class UserResource
{
public virtual int Id { get; protected set; }
public virtual User User { get; set;}
public virtual Resource Resource { get; set;}
public virtual string AccessLevel { get; set;}
}
public class Resource
{
public virtual int Id { get;protected set; }
public virtual string Name { get;set; }
}
And mappings like:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.UserResource)
.AsSet()
.Inverse()
.Cascade.AllDeleteOrphan();
}
}
public class UserResourceMap : ClassMap<UserResource>
{
public UserResourceMap()
{
Table("UserResourceMap");
Id(x => x.Id);
References(x => x.User).Not.Nullable();
References(x => x.Resource).Not.Nullable();
Map(x => x.AccessLevel);
}
}
public class ResourceMap : ClassMap<Resource>
{
public ResourceMap()
{
Cache.ReadOnly();
Id(x => x.Id);
Map(x => x.Name);
}
}

Related

Fluent NHibernate CompositeId trying to insert null values

I am working to map an existing database using Fluent NHibernate and have encountered a problem when it comes to complex many-to-many relationships (additional columns).
I know that many-to-many relationships with additional columns have to be mapped as HasMany rather than HasManyToMany as they are not pure many-to-many relationships. The linking table has to be mapped as a class within itself, which I have done in the example below.
When loading this data from an existing database it loads fine. The project I am working on takes this data and inserts it into an empty database, which is where the problem occurs. I think that when inserting into the new database the CompositeId is trying to insert NULL values for ItemID and ItemGroupID which is not allowed in the database. Changing the database structure is not a viable option at this point, is there a way around this issue?
Thanks, example code below.
Entity Classes
public class Item
{
public virtual long ItemID { get; set; }
public virtual string Name { get; set; }
}
public class ItemGroup
{
public virtual long ItemGroupID { get; set; }
public virtual string Name { get; set; }
public virtual IList<ItemInGroup> ItemsInGroup { get; set; }
}
public class ItemInGroup
{
public virtual Item Item { get; set; }
public virtual ItemGroup ItemGroup { get; set; }
public virtual int? DisplayOrder { get; set; }
}
Mapping Classes
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Table("Items");
Id(x => x.ItemID).GeneratedBy.Identity();
Map(x => x.Name);
}
}
public class ItemGroupMap : ClassMap<ItemGroup>
{
public ItemGroupMap()
{
Table("ItemGroups");
Id(x => x.ItemGroupID).GeneratedBy.Identity();
Map(x => x.Name);
HasMany(x => x.ItemsInGroup).KeyColumn("ItemGroupID").Cascade.All();
}
}
public class ItemInGroupMap : ClassMap<ItemInGroup>
{
public ItemInGroupMap()
{
Table("ItemsInGroups");
CompositeId().KeyReference(x => x.Item, "ItemID")
.KeyReference(x => x.ItemGroup, "ItemGroupID");
Map(x => x.DisplayOrder);
}
}
assuming DisplayOrder is the only extra column in the link table why not use the List index of IList as order?
public class ItemGroup
{
public virtual long ItemGroupID { get; set; }
public virtual string Name { get; set; }
public virtual IList<Item> Items { get; private set; }
}
public class ItemGroupMap : ClassMap<ItemGroup>
{
public ItemGroupMap()
{
Table("ItemGroups");
Id(x => x.ItemGroupID).GeneratedBy.Identity();
Map(x => x.Name);
HasManyToMany(x => x.ItemsInGroup)
.Table("ItemsInGroups")
.ParentKeyColumn("ItemGroupID")
.ChildKeyColumn("ItemID")
.AsList("DisplayOrder")
.Cascade.All();
}
}

Fluent NHibernate (ignore a specific property) [duplicate]

I have a class that is mapped in fluent nhibernate but I want one of the classes properties to be ignored by the mapping.
With class and mapping below I get this error:
The following types may not be used as proxies:
iMasterengine.Data.Model.Calendar: method get_HasEvents should be virtual
//my class
public class Calendar : IEntity {
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual string SiteId { get; set; }
public virtual IList<CalendarEvent> Events { get; set; }
//ignore this property
public bool HasEvents { get { return Events.Count > 0; } }
}
//my mapping
public class CalendarMap : ClassMap<Calendar> {
public CalendarMap() {
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.SiteId);
HasMany(x => x.Events).Inverse();
//what do I put here to tell nhibernate
//to ignore my HasEvents property?
}
}
You can just make HasEvents virtual in the class:
public virtual bool HasEvents { get { return Events.Count > 0; } }
You don't need to add anything to the mappings.
You only need to tell fluent to ingore a property if you are using Auto Mapping, which I don't think you are.
map.IgnoreProperty(p => p.What);

NHibernate mapping to entity using common properties

I am using IQueryable<> to build up batching queries.
I have used views successfully to fetch information so the IQueryable<> can find it, but in this instance I can't work out how to map a view, as it depends on properties rather than the entity's ID.
Say I have this entity and mapping:
public class Calculation
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual TransactionTotal TransactionTotal { get; set; }
}
public class CalculationMap : ClassMap<Calculation>
{
public CalculationMap()
{
Id(x => x.Id).GeneratedBy.Identity();
References(x => x.Organisation).Not.Nullable().UniqueKey("OC");
References(x => x.Charge).Not.Nullable().UniqueKey("OC");
}
This is the class I need to get in there: I'm using a view to give me the total amount per Organisation and Charge:
public class TransactionTotal
{
public virtual int Id { get; set; }
public virtual Organisation Organisation { get; set; }
public virtual Charge Charge { get; set; }
public virtual decimal Amount { get; set; }
}
public class TransactionTotalMap : ClassMap<TransactionTotal>
{
public TransactionTotalMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Table("TransactionTotalsView");
References(x => x.Charge).Not.Nullable();
References(x => x.Organisation).Not.Nullable();
Map(x => x.Amount).Precision(15).Scale(2).Not.Nullable();
}
}
Other places I have used views I have successfully used mappings like HasOne(x => x.TransactionTotal); but in this instance I need to tell Nhibernate to use the Organisation and Charge properties as the key.
Is this even possible? If so, how do I map the TransactionTotal class to the Calculation class?
Edit: I have used CompositeId in TransactionTotalMap as suggested by #David:
CompositeId().KeyProperty(x => x.Organisation.Id).KeyProperty(x => x.Charge.Id);
I'm still stuck on what to put in the CalculationMap though.
use the CompositeId() method in your mapping

Mapping with NHibernate?

I have following classes in my domain model:
public class Player
{
public virtual string ID { get; set; }
public virtual string Name { get; set; }
public virtual List<Match> Matches { get; set; }
}
public class Match
{
public virtual int ID { get; set; }
public virtual Player Player1 { get; set; }
public virtual Player Player2 { get; set; }
}
As you can see a Player can play multiple matches, and every match has two players.
How can I map these classes correctly using Fluent mapping?
The players on the match mapping would be References (many-to-one) referencing different playerIds in the match table and the matches on player would be hasMany (one-to-many):
public sealed class PlayerMap : ClassMap<Player>
{
public PlayerMap()
{
Id(x => x.ID).GeneratedBy.Native();
Map(x => x.Name);
HasMany(x => x.Matches).Cascade.SaveUpdate();
}
}
public sealed class MatchMap : ClassMap<Match>
{
public MatchMap()
{
Id(x => x.ID).GeneratedBy.Native();
References(x => x.Player1, "player1_id").NotFound.Ignore().Cascade.None();
References(x => x.Player2, "player2_id").NotFound.Ignore().Cascade.None();
}
}
You need a many-to-many (this case being a many-to-2). A possible fluent mapping is
public PlayerMap()
{
[...]
HasManyToMany(x => x.Matches).Cascade.AllDeleteOrphan();
}
otherwise nhibernate wouldn't know which column to use (match.player1_id or match.player2_id)

How to tell Fluent NHibernate not to map a class property

I have a class that is mapped in fluent nhibernate but I want one of the classes properties to be ignored by the mapping.
With class and mapping below I get this error:
The following types may not be used as proxies:
iMasterengine.Data.Model.Calendar: method get_HasEvents should be virtual
//my class
public class Calendar : IEntity {
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual string SiteId { get; set; }
public virtual IList<CalendarEvent> Events { get; set; }
//ignore this property
public bool HasEvents { get { return Events.Count > 0; } }
}
//my mapping
public class CalendarMap : ClassMap<Calendar> {
public CalendarMap() {
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.SiteId);
HasMany(x => x.Events).Inverse();
//what do I put here to tell nhibernate
//to ignore my HasEvents property?
}
}
You can just make HasEvents virtual in the class:
public virtual bool HasEvents { get { return Events.Count > 0; } }
You don't need to add anything to the mappings.
You only need to tell fluent to ingore a property if you are using Auto Mapping, which I don't think you are.
map.IgnoreProperty(p => p.What);

Categories