'How to map a collection of components as a set in fluent nhibernate

I want to map a collection of components on an entity. The component looks like this:

public class PermissionUserSetting
{
    public virtual PermissionType PermissionType { get; set; }
    public virtual string SettingName { get; set; }
    public virtual CustomFieldInputType ValueType { get; set; }
    public virtual string Value { get; set; }

    public PermissionUserSetting(PermissionType permissionType, string settingName, CustomFieldInputType valueType, string value)
    {
        PermissionType = permissionType;
        SettingName = settingName;
        ValueType = valueType;
        Value = value;
    }

    protected PermissionUserSetting()
    {
    }

}

These are unique across PermissionType and SettingName, so assuming in the database the PK would be OrganizationUserId, PermissionType, and SettingName.

This is on an entity OrganizationUser. For the mapping I have this:

mapping.HasMany(x => x.PermissionUserSettings)
            .AsSet()
            .KeyColumn("OrganizationUserId")
            //.KeyColumns.Add("PermissionType", "SettingName") THIS DOESN'T WORK!
            .Component(part =>
            {
                part.Map(x => x.PermissionType);
                part.Map(x => x.SettingName);
                part.Map(x => x.ValueType);
                part.Map(x => x.Value);
            });

I'm getting an error: - InnerException {"Foreign key (FK23A9E495A0C2E98C:PermissionUserSettings [OrganizationUserId, PermissionType, SettingName])) must have same number of columns as the referenced primary key (OrganizationUser [Id])"} System.Exception {NHibernate.FKUnmatchingColumnsException}

How do I get this mapped?



Solution 1:[1]

You need to declare a composite id with Fluent NHibernate. This is how to do

public class PermissionUserSettingMap : ClassMap<PermissionUserSetting>
{
    public EntityMap()
    {
        CompositeId()
        .KeyProperty(x => x.OrganizationUserId)
        .KeyReference(x => x.PermissionType) // assuming PermissionType is a Reference type
        .KeyProperty(x => x.SettingName);

        Map(x => x.ValueType);
        Map(x => x.Value);
    }
}

To use a composite Id your entity must override bool Equals(object obj) and int GetHashCode()

public class PermissionUserSetting
{
    public virtual int OrganizationUserId { get; set; } // you must declare all part of your composite key
    public virtual PermissionType PermissionType { get; set; }
    public virtual string SettingName { get; set; }
    public virtual CustomFieldInputType ValueType { get; set; }
    public virtual string Value { get; set; }

    public override bool Equals(object obj) =>
        obj is PermissionUserSetting pus &&
        OrganizationUserId == pus.OrganizationUserId  &&
        PermissionType == pus.PermissionType &&
        SettingName == pus.SettingName);

    public override int GetHashCode() => 
        OrganizationUserId.GetHashCode() ^
        PermissionType.GetHashCode() ^
        SettingName.GetHashCode();
}

Its always better to have a single primary key for Nhibernate with a unique constraint than a Composite Id

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Orkad